Merge branch 'geom_from_xx' into development

This commit is contained in:
Sharan Roongta 2020-10-11 20:24:29 +02:00
commit 8ab405e5d8
8 changed files with 242 additions and 158 deletions

1
.gitattributes vendored
View File

@ -8,6 +8,7 @@
*.jpg binary *.jpg binary
*.hdf5 binary *.hdf5 binary
*.pdf binary *.pdf binary
*.dream3d binary
# ignore files from MSC.Marc in language statistics # ignore files from MSC.Marc in language statistics
installation/mods_MarcMentat/20*/* linguist-vendored installation/mods_MarcMentat/20*/* linguist-vendored

View File

@ -1,12 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
import sys
from optparse import OptionParser from optparse import OptionParser
import h5py
import numpy as np
import damask import damask
@ -64,88 +60,12 @@ parser.set_defaults(pointwise = 'CellData',
if options.basegroup is None: if options.basegroup is None:
parser.error('No base group selected') parser.error('No base group selected')
rootDir ='DataContainers'
if filenames == []: parser.error('no input file specified.') if filenames == []: parser.error('no input file specified.')
for name in filenames: for name in filenames:
damask.util.report(scriptName,name) damask.util.report(scriptName,name)
errors = [] geom = damask.Geom.load_DREAM3D(name,options.basegroup,options.pointwise)
inFile = h5py.File(name, 'r')
group_geom = os.path.join(rootDir,options.basegroup,'_SIMPL_GEOMETRY')
try:
size = inFile[os.path.join(group_geom,'DIMENSIONS')][...] \
* inFile[os.path.join(group_geom,'SPACING')][...]
grid = inFile[os.path.join(group_geom,'DIMENSIONS')][...]
origin = inFile[os.path.join(group_geom,'ORIGIN')][...]
except KeyError:
errors.append('Geometry data ({}) not found'.format(group_geom))
group_pointwise = os.path.join(rootDir,options.basegroup,options.pointwise)
if options.average is None:
label = 'Point'
dataset = os.path.join(group_pointwise,options.quaternion)
try:
quats = np.reshape(inFile[dataset][...],(np.product(grid),4))
rot = [damask.Rotation.from_quaternion(q,True,P=+1) for q in quats]
except KeyError:
errors.append('Pointwise orientation (quaternion) data ({}) not readable'.format(dataset))
dataset = os.path.join(group_pointwise,options.phase)
try:
phase = np.reshape(inFile[dataset][...],(np.product(grid)))
except KeyError:
errors.append('Pointwise phase data ({}) not readable'.format(dataset))
microstructure = np.arange(1,np.product(grid)+1,dtype=int).reshape(grid,order='F')
else:
label = 'Grain'
dataset = os.path.join(group_pointwise,options.microstructure)
try:
microstructure = np.transpose(inFile[dataset][...].reshape(grid[::-1]),(2,1,0)) # convert from C ordering
except KeyError:
errors.append('Link between pointwise and grain average data ({}) not readable'.format(dataset))
group_average = os.path.join(rootDir,options.basegroup,options.average)
dataset = os.path.join(group_average,options.quaternion)
try:
rot = [damask.Rotation.from_quaternion(q,True,P=+1) for q in inFile[dataset][...][1:]] # skip first entry (unindexed)
except KeyError:
errors.append('Average orientation data ({}) not readable'.format(dataset))
dataset = os.path.join(group_average,options.phase)
try:
phase = [i[0] for i in inFile[dataset][...]][1:] # skip first entry (unindexed)
except KeyError:
errors.append('Average phase data ({}) not readable'.format(dataset))
if errors != []:
damask.util.croak(errors)
continue
config_header = ['<texture>']
for i in range(np.nanmax(microstructure)):
config_header += ['[{}{}]'.format(label,i+1),
'(gauss)\tphi1 {:.2f}\tPhi {:.2f}\tphi2 {:.2f}'.format(*rot[i].as_Eulers(degrees = True)),
]
config_header += ['<microstructure>']
for i in range(np.nanmax(microstructure)):
config_header += ['[{}{}]'.format(label,i+1),
'(constituent)\tphase {}\ttexture {}\tfraction 1.0'.format(phase[i],i+1),
]
header = [scriptID + ' ' + ' '.join(sys.argv[1:])]\
+ config_header
geom = damask.Geom(microstructure,size,origin,comments=header)
damask.util.croak(geom) damask.util.croak(geom)
geom.save_ASCII(os.path.splitext(name)[0]+'.geom',compress=False) geom.save_ASCII(os.path.splitext(name)[0]+'.geom',compress=False)

View File

@ -2,11 +2,8 @@
import os import os
import sys import sys
from io import StringIO
from optparse import OptionParser from optparse import OptionParser
import numpy as np
import damask import damask
@ -40,64 +37,21 @@ parser.add_option('-q', '--quaternion',
dest = 'quaternion', dest = 'quaternion',
type = 'string', metavar='string', type = 'string', metavar='string',
help = 'quaternion label') help = 'quaternion label')
parser.add_option('--axes',
dest = 'axes',
type = 'string', nargs = 3, metavar = ' '.join(['string']*3),
help = 'orientation coordinate frame in terms of position coordinate frame [+x +y +z]')
parser.set_defaults(pos= 'pos')
parser.set_defaults(pos = 'pos',
)
(options,filenames) = parser.parse_args() (options,filenames) = parser.parse_args()
if filenames == []: filenames = [None] if filenames == []: filenames = [None]
if np.sum([options.quaternion is not None,
options.microstructure is not None]) != 1:
parser.error('need either microstructure or quaternion (and optionally phase) as input.')
if options.microstructure is not None and options.phase is not None:
parser.error('need either microstructure or phase (and mandatory quaternion) as input.')
if options.axes is not None and not set(options.axes).issubset(set(['x','+x','-x','y','+y','-y','z','+z','-z'])):
parser.error('invalid axes {} {} {}.'.format(*options.axes))
for name in filenames: for name in filenames:
damask.util.report(scriptName,name) damask.util.report(scriptName,name)
table = damask.Table.load(StringIO(''.join(sys.stdin.read())) if name is None else name) labels = []
table.sort_by(['{}_{}'.format(i,options.pos) for i in range(3,0,-1)]) # x fast, y slow for l in [options.quaternion,options.phase,options.microstructure]:
grid,size,origin = damask.grid_filters.cell_coord0_gridSizeOrigin(table.get(options.pos)) if l is not None: labels.append(l)
config_header = table.comments t = damask.Table.load(name)
geom = damask.Geom.from_table(t,options.pos,labels)
if options.microstructure:
microstructure = table.get(options.microstructure).reshape(grid,order='F')
elif options.quaternion:
q = table.get(options.quaternion)
phase = table.get(options.phase).astype(int) if options.phase else \
np.ones((table.data.shape[0],1),dtype=int)
unique,unique_inverse = np.unique(np.hstack((q,phase)),return_inverse=True,axis=0)
microstructure = unique_inverse.reshape(grid,order='F') + 1
config_header = ['<texture>']
for i,data in enumerate(unique):
ori = damask.Rotation(data[0:4])
config_header += ['[Grain{}]'.format(i+1),
'(gauss)\tphi1 {:.2f}\tPhi {:.2f}\tphi2 {:.2f}'.format(*ori.as_Eulers(degrees = True)),
]
if options.axes is not None: config_header += ['axes\t{} {} {}'.format(*options.axes)]
config_header += ['<microstructure>']
for i,data in enumerate(unique):
config_header += ['[Grain{}]'.format(i+1),
'(constituent)\tphase {}\ttexture {}\tfraction 1.0'.format(int(data[4]),i+1),
]
header = [scriptID + ' ' + ' '.join(sys.argv[1:])]\
+ config_header
geom = damask.Geom(microstructure,size,origin,
comments=header)
damask.util.croak(geom) damask.util.croak(geom)
geom.save_ASCII(sys.stdout if name is None else os.path.splitext(name)[0]+'.geom',compress=False) geom.save_ASCII(sys.stdout if name is None else os.path.splitext(name)[0]+'.geom',compress=False)

View File

@ -10,21 +10,21 @@ with open(_Path(__file__).parent/_Path('VERSION')) as _f:
# make classes directly accessible as damask.Class # make classes directly accessible as damask.Class
from ._environment import Environment as _ # noqa from ._environment import Environment as _ # noqa
environment = _() environment = _()
from ._table import Table # noqa
from ._vtk import VTK # noqa
from ._colormap import Colormap # noqa
from ._rotation import Rotation # noqa
from ._lattice import Symmetry, Lattice# noqa
from ._orientation import Orientation # noqa
from ._result import Result # noqa
from ._geom import Geom # noqa
from ._config import Config # noqa
from ._configmaterial import ConfigMaterial # noqa
from . import solver # noqa
from . import util # noqa from . import util # noqa
from . import seeds # noqa from . import seeds # noqa
from . import grid_filters # noqa
from . import mechanics # noqa from . import mechanics # noqa
from . import solver # noqa
from . import grid_filters # noqa
from ._lattice import Symmetry, Lattice# noqa
from ._table import Table # noqa
from ._rotation import Rotation # noqa
from ._vtk import VTK # noqa
from ._colormap import Colormap # noqa
from ._orientation import Orientation # noqa
from ._config import Config # noqa
from ._configmaterial import ConfigMaterial # noqa
from ._geom import Geom # noqa
from ._result import Result # noqa

View File

@ -2,6 +2,7 @@ import copy
import numpy as np import numpy as np
from . import grid_filters
from . import Config from . import Config
from . import Lattice from . import Lattice
from . import Rotation from . import Rotation
@ -17,12 +18,73 @@ class ConfigMaterial(Config):
---------- ----------
fname : file, str, or pathlib.Path, optional fname : file, str, or pathlib.Path, optional
Filename or file for writing. Defaults to 'material.yaml'. Filename or file for writing. Defaults to 'material.yaml'.
**kwargs : dict **kwargs
Keyword arguments parsed to yaml.dump. Keyword arguments parsed to yaml.dump.
""" """
super().save(fname,**kwargs) super().save(fname,**kwargs)
@staticmethod
def from_table(table,coordinates=None,constituents={},**kwargs):
"""
Load from an ASCII table.
Parameters
----------
table : damask.Table
Table that contains material information.
coordinates : str, optional
Label of spatial coordiates. Used for sorting and performing a
sanity check. Default to None, in which case no sorting or checking is
peformed.
constituents : dict, optional
Entries for 'constituents'. The key is the name and the value specifies
the label of the data column in the table
**kwargs
Keyword arguments where the key is the name and the value specifies
the label of the data column in the table
Examples
--------
>>> import damask
>>> import damask.ConfigMaterial as cm
>>> t = damask.Table.load('small.txt')
>>> t
pos pos pos qu qu qu qu phase homog
0 0 0 0 0.19 0.8 0.24 -0.51 Aluminum SX
1 1 0 0 0.8 0.19 0.24 -0.51 Steel SX
>>> cm.from_table(t,'pos',{'O':'qu','phase':'phase'},homogenization='homog')
material:
- constituents:
- O: [0.19, 0.8, 0.24, -0.51]
fraction: 1.0
phase: Aluminum
homogenization: SX
- constituents:
- O: [0.8, 0.19, 0.24, -0.51]
fraction: 1.0
phase: Steel
homogenization: SX
"""
if coordinates is not None:
t = table.sort_by([f'{i}_{coordinates}' for i in range(3,0,-1)])
grid_filters.coord0_check(t.get(coordinates))
else:
t = table
constituents_ = {k:t.get(v) for k,v in constituents.items()}
kwargs_ = {k:t.get(v) for k,v in kwargs.items()}
_,idx = np.unique(np.hstack(list({**constituents_,**kwargs_}.values())),return_index=True,axis=0)
constituents_ = {k:v[idx].squeeze() for k,v in constituents_.items()}
kwargs_ = {k:v[idx].squeeze() for k,v in kwargs_.items()}
return ConfigMaterial().material_add(constituents_,**kwargs_)
@property @property
def is_complete(self): def is_complete(self):
"""Check for completeness.""" """Check for completeness."""
@ -62,10 +124,10 @@ class ConfigMaterial(Config):
print(f'No lattice specified in phase {k}') print(f'No lattice specified in phase {k}')
ok = False ok = False
#for k,v in self['homogenization'].items(): for k,v in self['homogenization'].items():
# if 'N_constituents' not in v: if 'N_constituents' not in v:
# print(f'No. of constituents not specified in homogenization {k}'} print(f'No. of constituents not specified in homogenization {k}')
# ok = False ok = False
if phase - set(self['phase']): if phase - set(self['phase']):
print(f'Phase(s) {phase-set(self["phase"])} missing') print(f'Phase(s) {phase-set(self["phase"])} missing')
@ -158,3 +220,80 @@ class ConfigMaterial(Config):
except KeyError: except KeyError:
continue continue
return dup return dup
def material_add(self,constituents,**kwargs):
"""
Add material entries.
Parameters
----------
constituents : dict
Entries for 'constituents'. The key is the name and the value specifies
the label of the data column in the table
**kwargs
Keyword arguments where the key is the name and the value specifies
the label of the data column in the table
Examples
--------
>>> import damask
>>> m = damask.ConfigMaterial()
>>> O = damask.Rotation.from_random(3).as_quaternion()
>>> phase = ['Aluminum','Steel','Aluminum']
>>> m.material_add(constituents={'phase':phase,'O':O},homogenization='SX')
material:
- constituents:
- O: [0.577764, -0.146299, -0.617669, 0.513010]
fraction: 1.0
phase: Aluminum
homogenization: SX
- constituents:
- O: [0.184176, 0.340305, 0.737247, 0.553840]
fraction: 1.0
phase: Steel
homogenization: SX
- constituents:
- O: [0.0886257, -0.144848, 0.615674, -0.769487]
fraction: 1.0
phase: Aluminum
homogenization: SX
"""
c = [{'constituents':u} for u in ConfigMaterial._constituents(**constituents)]
for k,v in kwargs.items():
if hasattr(v,'__len__') and not isinstance(v,str):
if len(v) != len(c):
raise ValueError('len mismatch 1')
for i,vv in enumerate(v):
c[i][k] = [w.item() for w in vv] if isinstance(vv,np.ndarray) else vv.item()
else:
for i in range(len(c)):
c[i][k] = v
dup = copy.deepcopy(self)
if 'material' not in dup: dup['material'] = []
dup['material'] +=c
return dup
@staticmethod
def _constituents(N=1,**kwargs):
"""Construct list of constituents."""
for v in kwargs.values():
if hasattr(v,'__len__') and not isinstance(v,str): N_material = len(v)
if N == 1:
m = [[{'fraction':1.0}] for _ in range(N_material)]
for k,v in kwargs.items():
if hasattr(v,'__len__') and not isinstance(v,str):
if len(v) != N_material:
raise ValueError('len mismatch 2')
for i,vv in enumerate(np.array(v)):
m[i][0][k] = [w.item() for w in vv] if isinstance(vv,np.ndarray) else vv.item()
else:
for i in range(N_material):
m[i][0][k] = v
return m
else:
raise NotImplementedError

View File

@ -1,15 +1,17 @@
import copy import copy
import multiprocessing as mp import multiprocessing as mp
from functools import partial from functools import partial
from os import path
import numpy as np import numpy as np
import h5py
from scipy import ndimage,spatial from scipy import ndimage,spatial
from . import environment from . import environment
from . import Rotation
from . import VTK from . import VTK
from . import util from . import util
from . import grid_filters from . import grid_filters
from . import Rotation
class Geom: class Geom:
@ -207,6 +209,68 @@ class Geom:
return Geom(material.reshape(grid,order='F'),size,origin,comments) return Geom(material.reshape(grid,order='F'),size,origin,comments)
@staticmethod
def load_DREAM3D(fname,base_group,point_data=None,material='FeatureIds'):
"""
Load a DREAM.3D file.
Parameters
----------
fname : str
Filename of the DREAM.3D file
base_group : str
Name of the group (folder) below 'DataContainers'. For example
'SyntheticVolumeDataContainer'.
point_data : str, optional
Name of the group (folder) containing the point wise material data,
for example 'CellData'. Defaults to None, in which case points consecutively numbered.
material : str, optional
Name of the dataset containing the material ID. Defaults to
'FeatureIds'.
"""
root_dir ='DataContainers'
f = h5py.File(fname, 'r')
g = path.join(root_dir,base_group,'_SIMPL_GEOMETRY')
size = f[path.join(g,'DIMENSIONS')][()] * f[path.join(g,'SPACING')][()]
grid = f[path.join(g,'DIMENSIONS')][()]
origin = f[path.join(g,'ORIGIN')][()]
group_pointwise = path.join(root_dir,base_group,point_data)
ma = np.arange(1,np.product(grid)+1,dtype=int) if point_data is None else \
np.reshape(f[path.join(group_pointwise,material)],grid.prod())
return Geom(ma.reshape(grid,order='F'),size,origin,util.execution_stamp('Geom','load_DREAM3D'))
@staticmethod
def from_table(table,coordinates,labels):
"""
Load an ASCII table.
Parameters
----------
table : damask.Table
Table that contains material information.
coordinates : str
Label of the column containing the spatial coordinates.
labels : str or list of str
Label(s) of the columns containing the material definition.
Each unique combintation of values results in a material.
"""
t = table.sort_by([f'{i}_{coordinates}' for i in range(3,0,-1)])
grid,size,origin = grid_filters.cell_coord0_gridSizeOrigin(t.get(coordinates))
labels_ = [labels] if isinstance(labels,str) else labels
_,unique_inverse = np.unique(np.hstack([t.get(l) for l in labels_]),return_inverse=True,axis=0)
ma = unique_inverse.reshape(grid,order='F') + 1
return Geom(ma,size,origin,util.execution_stamp('Geom','from_table'))
@staticmethod @staticmethod
def _find_closest_seed(seeds, weights, point): def _find_closest_seed(seeds, weights, point):
return np.argmin(np.sum((np.broadcast_to(point,(len(seeds),3))-seeds)**2,axis=1) - weights) return np.argmin(np.sum((np.broadcast_to(point,(len(seeds),3))-seeds)**2,axis=1) - weights)
@ -399,7 +463,7 @@ class Geom:
def save(self,fname,compress=True): def save(self,fname,compress=True):
""" """
Generates vtk rectilinear grid. Store as vtk rectilinear grid.
Parameters Parameters
---------- ----------
@ -536,7 +600,7 @@ class Geom:
coords_rot = R.broadcast_to(tuple(self.grid))@coords coords_rot = R.broadcast_to(tuple(self.grid))@coords
with np.errstate(all='ignore'): with np.errstate(all='ignore'):
mask = np.where(np.sum(np.power(coords_rot/r,2.0**exponent),axis=-1) > 1.0,True,False) mask = np.sum(np.power(coords_rot/r,2.0**np.array(exponent)),axis=-1) > 1.0
if periodic: # translate back to center if periodic: # translate back to center
mask = np.roll(mask,((c-np.ones(3)*.5)*self.grid).astype(int),(0,1,2)) mask = np.roll(mask,((c-np.ones(3)*.5)*self.grid).astype(int),(0,1,2))

View File

@ -33,6 +33,10 @@ class Table:
"""Brief overview.""" """Brief overview."""
return util.srepr(self.comments)+'\n'+self.data.__repr__() return util.srepr(self.comments)+'\n'+self.data.__repr__()
def __len__(self):
"""Number of rows."""
return len(self.data)
def __copy__(self): def __copy__(self):
"""Copy Table.""" """Copy Table."""
return copy.deepcopy(self) return copy.deepcopy(self)

View File

@ -1,8 +1,10 @@
homogenization: homogenization:
SX: SX:
N_constituents: 2
mech: {type: none} mech: {type: none}
Taylor: Taylor:
mech: {N_constituents: 2, type: isostrain} N_constituents: 2
mech: {type: isostrain}
material: material:
- constituents: - constituents: