improving import from DREAM.3D
- no duplicated entries even for non-segmented data - using phase labels from file - material.yaml: Dummy homogenization and phase - tests to ensure correct order and match between Grid and ConfigMaterial
This commit is contained in:
parent
cdcedd0d44
commit
394fda5f37
|
@ -1,4 +1,4 @@
|
||||||
from os import path
|
import os.path
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import h5py
|
import h5py
|
||||||
|
@ -98,7 +98,10 @@ class ConfigMaterial(Config):
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load_DREAM3D(fname,data_group,ori_data,phase_id,phase_name,base_group=None):
|
def load_DREAM3D(fname,
|
||||||
|
grain_data=None,cell_data='CellData',cell_ensemble_data='CellEnsembleData',
|
||||||
|
phases='Phases',Euler_angles='EulerAngles',phase_names='PhaseName',
|
||||||
|
base_group=None):
|
||||||
"""
|
"""
|
||||||
Load material data from DREAM3D file.
|
Load material data from DREAM3D file.
|
||||||
|
|
||||||
|
@ -107,70 +110,33 @@ class ConfigMaterial(Config):
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
fname : str
|
fname : str
|
||||||
path to the DREAM3D file.
|
Filename of the DREAM.3D (HDF5) file.
|
||||||
base_group : str
|
base_group : str
|
||||||
Name of the group (folder) below 'DataContainers',
|
Path to the group (folder) that contains the geometry (_SIMPL_GEOMETRY),
|
||||||
for example 'SyntheticVolumeDataContainer'.
|
and, optionally, the cell data. Defaults to None, in which case
|
||||||
data_group : str
|
it is set as the path that contains _SIMPL_GEOMETRY/SPACING.
|
||||||
Name of the group (folder) having relevant data for conversion,
|
|
||||||
for example 'Grain Data' or 'CellData'.
|
|
||||||
ori_data : str
|
|
||||||
Name of the dataset having orientation data (working with Euler Angles in dream3D file),
|
|
||||||
For example 'EulerAngles'.
|
|
||||||
phase_id : str
|
|
||||||
Name of the dataset containing phase IDs for each grain,
|
|
||||||
for example 'Phases'.
|
|
||||||
phase_name : list
|
|
||||||
List with name of the phases.
|
|
||||||
|
|
||||||
Examples
|
|
||||||
--------
|
|
||||||
for grain based data with single phase
|
|
||||||
>>> import damask
|
|
||||||
>>> import damask.ConfigMaterial as cm
|
|
||||||
>>> cm.load_from_Dream3D('20grains16x16x16.dream3D','SyntheticVolumeDataContainer', 'Grain Data',
|
|
||||||
... 'EulerAngles','Phases',['Ferrite'])
|
|
||||||
|
|
||||||
for point based data with single phase
|
|
||||||
>>> import damask
|
|
||||||
>>> import damask.ConfigMaterial as cm
|
|
||||||
>>> cm.load_from_Dream3D('20grains16x16x16.dream3D','SyntheticVolumeDataContainer', 'CellData',
|
|
||||||
... 'EulerAngles','Phases',['Ferrite'])
|
|
||||||
|
|
||||||
for grain based data with dual phase
|
|
||||||
>>> import damask
|
|
||||||
>>> import damask.ConfigMaterial as cm
|
|
||||||
>>> cm.load_from_Dream3D('20grains16x16x16.dream3D','SyntheticVolumeDataContainer', 'Grain Data',
|
|
||||||
... 'EulerAngles','Phases',['Ferrite','Martensite'])
|
|
||||||
|
|
||||||
for point based data with dual phase
|
|
||||||
>>> import damask
|
|
||||||
>>> import damask.ConfigMaterial as cm
|
|
||||||
>>> cm.load_from_Dream3D('20grains16x16x16.dream3D','SyntheticVolumeDataContainer', 'CellData',
|
|
||||||
... 'EulerAngles','Phases',['Ferrite','Martensite'])
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
b = util.DREAM3D_base_group(fname) if base_group is None else base_group
|
b = util.DREAM3D_base_group(fname) if base_group is None else base_group
|
||||||
hdf = h5py.File(fname,'r')
|
f = h5py.File(fname,'r')
|
||||||
|
|
||||||
orientation_path = path.join(b,data_group,ori_data)
|
if grain_data is None:
|
||||||
if hdf[orientation_path].attrs['TupleDimensions'].shape == (3,):
|
phase = f[os.path.join(b,cell_data,phases)][()].flatten()
|
||||||
grain_orientations = np.array(hdf[orientation_path]).reshape(-1,3,order='F')
|
O = Rotation.from_Euler_angles(f[os.path.join(b,cell_data,Euler_angles)]).as_quaternion().reshape(-1,4) # noqa
|
||||||
|
_,idx = np.unique(np.hstack([O,phase.reshape(-1,1)]),return_index=True,axis=0)
|
||||||
|
idx = np.sort(idx)
|
||||||
else:
|
else:
|
||||||
grain_orientations = np.array(hdf[orientation_path])[1:]
|
phase = f[os.path.join(b,grain_data,phases)][()]
|
||||||
|
O = Rotation.from_Euler_angles(f[os.path.join(b,grain_data,Euler_angles)]).as_quaternion() # noqa
|
||||||
|
idx = np.arange(phase.size)
|
||||||
|
|
||||||
grain_quats = Rotation.from_Euler_angles(grain_orientations).as_quaternion()
|
if cell_ensemble_data is not None:
|
||||||
|
names = np.array([s.decode() for s in f[os.path.join(b,cell_ensemble_data,phase_names)]])
|
||||||
|
phase = names[phase]
|
||||||
|
|
||||||
phase_path = path.join(b,data_group,phase_id)
|
material = {k:np.atleast_1d(v[idx].squeeze()) for k,v in zip(['O','phase'],[O,phase])}
|
||||||
if hdf[phase_path].attrs['TupleDimensions'].shape == (3,):
|
return ConfigMaterial({'phase':{k if isinstance(k,int) else str(k):'tbd' for k in np.unique(phase)},
|
||||||
grain_phase = np.array(hdf[phase_path]).reshape(-1,order='F')
|
'homogenization':{'direct':{'N_constituents':1}}}).material_add(**material)
|
||||||
else:
|
|
||||||
grain_phase = np.array(hdf[phase_path])[1:]
|
|
||||||
|
|
||||||
grain_phase = grain_phase.reshape(len(grain_phase),)
|
|
||||||
phase_name_list = [phase_name[i - 1] for i in grain_phase]
|
|
||||||
|
|
||||||
return ConfigMaterial().material_add(phase=phase_name_list, O = grain_quats) # noqa
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -256,14 +256,17 @@ class Grid:
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load_DREAM3D(fname,cell_data=None,material='FeatureIds',base_group=None):
|
def load_DREAM3D(fname,
|
||||||
|
feature_IDs=None,cell_data='CellData',
|
||||||
|
phases='Phases',Euler_angles='EulerAngles',
|
||||||
|
base_group=None):
|
||||||
"""
|
"""
|
||||||
Load from DREAM.3D file.
|
Load from DREAM.3D file.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
fname : str
|
fname : str
|
||||||
Filename of the DREAM.3D file
|
Filename of the DREAM.3D (HDF5) file.
|
||||||
cell_data : str, optional
|
cell_data : str, optional
|
||||||
Name of the group (folder) containing the pointwise material data,
|
Name of the group (folder) containing the pointwise material data,
|
||||||
for example 'CellData'. Defaults to None, in which case points are consecutively numbered.
|
for example 'CellData'. Defaults to None, in which case points are consecutively numbered.
|
||||||
|
@ -279,11 +282,17 @@ class Grid:
|
||||||
b = util.DREAM3D_base_group(fname) if base_group is None else base_group
|
b = util.DREAM3D_base_group(fname) if base_group is None else base_group
|
||||||
f = h5py.File(fname, 'r')
|
f = h5py.File(fname, 'r')
|
||||||
cells = f[os.path.join(b,'_SIMPL_GEOMETRY','DIMENSIONS')][()]
|
cells = f[os.path.join(b,'_SIMPL_GEOMETRY','DIMENSIONS')][()]
|
||||||
size = f[os.path.join(b,'_SIMPL_GEOMETRY','SPACING')][()] * cells
|
size = f[os.path.join(b,'_SIMPL_GEOMETRY','SPACING')] * cells
|
||||||
origin = f[os.path.join(b,'_SIMPL_GEOMETRY','ORIGIN')][()]
|
origin = f[os.path.join(b,'_SIMPL_GEOMETRY','ORIGIN')][()]
|
||||||
|
|
||||||
ma = np.arange(cells.prod(),dtype=int) if cell_data is None else \
|
if feature_IDs is None:
|
||||||
np.reshape(f[os.path.join(b,cell_data,material)],cells.prod())
|
phase = f[os.path.join(b,cell_data,phases)][()].reshape(-1,1)
|
||||||
|
O = Rotation.from_Euler_angles(f[os.path.join(b,cell_data,Euler_angles)]).as_quaternion().reshape(-1,4) # noqa
|
||||||
|
unique,unique_inverse = np.unique(np.hstack([O,phase]),return_inverse=True,axis=0)
|
||||||
|
ma = np.arange(cells.prod()) if len(unique) == cells.prod() else \
|
||||||
|
np.arange(unique.size)[np.argsort(pd.unique(unique_inverse))][unique_inverse]
|
||||||
|
else:
|
||||||
|
ma = f[os.path.join(b,cell_data,feature_IDs)][()].flatten()
|
||||||
|
|
||||||
return Grid(ma.reshape(cells,order='F'),size,origin,util.execution_stamp('Grid','load_DREAM3D'))
|
return Grid(ma.reshape(cells,order='F'),size,origin,util.execution_stamp('Grid','load_DREAM3D'))
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import numpy as np
|
||||||
from damask import ConfigMaterial
|
from damask import ConfigMaterial
|
||||||
from damask import Table
|
from damask import Table
|
||||||
from damask import Rotation
|
from damask import Rotation
|
||||||
|
from damask import Grid
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def ref_path(ref_path_base):
|
def ref_path(ref_path_base):
|
||||||
|
@ -108,3 +109,24 @@ class TestConfigMaterial:
|
||||||
m = ConfigMaterial().material_add(**kw)
|
m = ConfigMaterial().material_add(**kw)
|
||||||
assert len(m['material']) == N
|
assert len(m['material']) == N
|
||||||
assert len(m['material'][0]['constituents']) == n
|
assert len(m['material'][0]['constituents']) == n
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('cell_ensemble_data',[None,'CellEnsembleData'])
|
||||||
|
def test_load_DREAM3D(self,ref_path,cell_ensemble_data):
|
||||||
|
grain_c = ConfigMaterial.load_DREAM3D(ref_path/'2phase_irregularGrid.dream3d','Grain Data',
|
||||||
|
cell_ensemble_data = cell_ensemble_data)
|
||||||
|
point_c = ConfigMaterial.load_DREAM3D(ref_path/'2phase_irregularGrid.dream3d',
|
||||||
|
cell_ensemble_data = cell_ensemble_data)
|
||||||
|
|
||||||
|
assert point_c.is_valid and grain_c.is_valid
|
||||||
|
assert len(point_c['material'])+1 == len(grain_c['material'])
|
||||||
|
|
||||||
|
grain_m = Grid.load_DREAM3D(ref_path/'2phase_irregularGrid.dream3d','FeatureIds').material.flatten()
|
||||||
|
point_m = Grid.load_DREAM3D(ref_path/'2phase_irregularGrid.dream3d').material.flatten()
|
||||||
|
|
||||||
|
for i in np.unique(point_m):
|
||||||
|
j = int(grain_m[(point_m==i).nonzero()[0][0]])
|
||||||
|
assert np.allclose(point_c['material'][i]['constituents'][0]['O'],
|
||||||
|
grain_c['material'][j]['constituents'][0]['O'])
|
||||||
|
assert point_c['material'][i]['constituents'][0]['phase'] == \
|
||||||
|
grain_c['material'][j]['constituents'][0]['phase']
|
||||||
|
|
|
@ -420,6 +420,7 @@ class TestGrid:
|
||||||
t = Table(np.column_stack((coords.reshape(-1,3,order='F'),grid.material.flatten(order='F'))),{'c':3,'m':1})
|
t = Table(np.column_stack((coords.reshape(-1,3,order='F'),grid.material.flatten(order='F'))),{'c':3,'m':1})
|
||||||
assert grid_equal(grid.sort().renumber(),Grid.from_table(t,'c',['m']))
|
assert grid_equal(grid.sort().renumber(),Grid.from_table(t,'c',['m']))
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('periodic',[True,False])
|
@pytest.mark.parametrize('periodic',[True,False])
|
||||||
@pytest.mark.parametrize('direction',['x','y','z',['x','y'],'zy','xz',['x','y','z']])
|
@pytest.mark.parametrize('direction',['x','y','z',['x','y'],'zy','xz',['x','y','z']])
|
||||||
def test_get_grain_boundaries(self,update,ref_path,periodic,direction):
|
def test_get_grain_boundaries(self,update,ref_path,periodic,direction):
|
||||||
|
@ -429,3 +430,12 @@ class TestGrid:
|
||||||
current.save(ref_path/f'get_grain_boundaries_8g12x15x20_{direction}_{periodic}.vtu',parallel=False)
|
current.save(ref_path/f'get_grain_boundaries_8g12x15x20_{direction}_{periodic}.vtu',parallel=False)
|
||||||
reference = VTK.load(ref_path/f'get_grain_boundaries_8g12x15x20_{"".join(direction)}_{periodic}.vtu')
|
reference = VTK.load(ref_path/f'get_grain_boundaries_8g12x15x20_{"".join(direction)}_{periodic}.vtu')
|
||||||
assert current.__repr__() == reference.__repr__()
|
assert current.__repr__() == reference.__repr__()
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_DREAM3D(self,ref_path):
|
||||||
|
grain = Grid.load_DREAM3D(ref_path/'2phase_irregularGrid.dream3d','FeatureIds')
|
||||||
|
point = Grid.load_DREAM3D(ref_path/'2phase_irregularGrid.dream3d')
|
||||||
|
|
||||||
|
assert np.allclose(grain.origin,point.origin) and \
|
||||||
|
np.allclose(grain.size,point.size) and \
|
||||||
|
(grain.sort().material == point.material+1).all()
|
||||||
|
|
Loading…
Reference in New Issue