Merge remote-tracking branch 'origin/development' into dislotwin-correction

This commit is contained in:
Sharan Roongta 2021-02-24 15:16:55 +01:00
commit 8e2aace7eb
3 changed files with 103 additions and 115 deletions

View File

@ -3,6 +3,7 @@ import numpy as np
from . import Config from . import Config
from . import Rotation from . import Rotation
from . import Orientation from . import Orientation
from . import util
class ConfigMaterial(Config): class ConfigMaterial(Config):
"""Material configuration.""" """Material configuration."""
@ -46,7 +47,7 @@ class ConfigMaterial(Config):
@staticmethod @staticmethod
def from_table(table,constituents={},**kwargs): def from_table(table,**kwargs):
""" """
Load from an ASCII table. Load from an ASCII table.
@ -54,12 +55,9 @@ class ConfigMaterial(Config):
---------- ----------
table : damask.Table table : damask.Table
Table that contains material information. Table that contains material information.
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 **kwargs
Keyword arguments where the key is the name and the value specifies Keyword arguments where the key is the name and the value specifies
the label of the data column in the table the label of the data column in the table.
Examples Examples
-------- --------
@ -70,7 +68,8 @@ class ConfigMaterial(Config):
pos pos pos qu qu qu qu phase homog pos pos pos qu qu qu qu phase homog
0 0 0 0 0.19 0.8 0.24 -0.51 Aluminum SX 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 1 1 0 0 0.8 0.19 0.24 -0.51 Steel SX
>>> cm.from_table(t,{'O':'qu','phase':'phase'},homogenization='homog') 1 1 1 0 0.8 0.19 0.24 -0.51 Steel SX
>>> cm.from_table(t,O='qu',phase='phase',homogenization='homog')
material: material:
- constituents: - constituents:
- O: [0.19, 0.8, 0.24, -0.51] - O: [0.19, 0.8, 0.24, -0.51]
@ -86,16 +85,13 @@ class ConfigMaterial(Config):
phase: {} phase: {}
""" """
constituents_ = {k:table.get(v) for k,v in constituents.items()}
kwargs_ = {k:table.get(v) for k,v in kwargs.items()} kwargs_ = {k:table.get(v) for k,v in kwargs.items()}
_,idx = np.unique(np.hstack(list({**constituents_,**kwargs_}.values())),return_index=True,axis=0) _,idx = np.unique(np.hstack(list(kwargs_.values())),return_index=True,axis=0)
idx = np.sort(idx) idx = np.sort(idx)
constituents_ = {k:np.atleast_1d(v[idx].squeeze()) for k,v in constituents_.items()} kwargs_ = {k:np.atleast_1d(v[idx].squeeze()) for k,v in kwargs_.items()}
kwargs_ = {k:np.atleast_1d(v[idx].squeeze()) for k,v in kwargs_.items()}
return ConfigMaterial().material_add(constituents_,**kwargs_) return ConfigMaterial().material_add(**kwargs_)
@property @property
@ -153,7 +149,7 @@ class ConfigMaterial(Config):
@property @property
def is_valid(self): def is_valid(self):
"""Check for valid file layout.""" """Check for valid content."""
ok = True ok = True
if 'phase' in self: if 'phase' in self:
@ -162,8 +158,7 @@ class ConfigMaterial(Config):
try: try:
Orientation(lattice=v['lattice']) Orientation(lattice=v['lattice'])
except KeyError: except KeyError:
s = v['lattice'] print(f"Invalid lattice '{v['lattice']}' in phase '{k}'")
print(f"Invalid lattice: '{s}' in phase '{k}'")
ok = False ok = False
if 'material' in self: if 'material' in self:
@ -171,16 +166,15 @@ class ConfigMaterial(Config):
if 'constituents' in m: if 'constituents' in m:
v = 0.0 v = 0.0
for c in m['constituents']: for c in m['constituents']:
v+= float(c['v']) v += float(c['v'])
if 'O' in c: if 'O' in c:
try: try:
Rotation.from_quaternion(c['O']) Rotation.from_quaternion(c['O'])
except ValueError: except ValueError:
o = c['O'] print(f"Invalid orientation '{c['O']}' in material '{i}'")
print(f"Invalid orientation: '{o}' in material '{i}'")
ok = False ok = False
if not np.isclose(v,1.0): if not np.isclose(v,1.0):
print(f"Invalid total fraction (v) '{v}' in material '{i}'") print(f"Total fraction v = {v} ≠ 1 in material '{i}'")
ok = False ok = False
return ok return ok
@ -199,6 +193,11 @@ class ConfigMaterial(Config):
constituent: list of ints, optional constituent: list of ints, optional
Limit renaming to selected constituents. Limit renaming to selected constituents.
Returns
-------
cfg : damask.ConfigMaterial
Updated material configuration.
""" """
dup = self.copy() dup = self.copy()
for i,m in enumerate(dup['material']): for i,m in enumerate(dup['material']):
@ -223,6 +222,11 @@ class ConfigMaterial(Config):
ID: list of ints, optional ID: list of ints, optional
Limit renaming to selected homogenization IDs. Limit renaming to selected homogenization IDs.
Returns
-------
cfg : damask.ConfigMaterial
Updated material configuration.
""" """
dup = self.copy() dup = self.copy()
for i,m in enumerate(dup['material']): for i,m in enumerate(dup['material']):
@ -234,24 +238,27 @@ class ConfigMaterial(Config):
return dup return dup
def material_add(self,constituents=None,**kwargs): def material_add(self,**kwargs):
""" """
Add material entries. Add material entries.
Parameters Parameters
---------- ----------
constituents : dict, optional
Entries for 'constituents' as key-value pair.
**kwargs **kwargs
Key-value pairs. Key-value pairs.
Returns
-------
cfg : damask.ConfigMaterial
Updated material configuration.
Examples Examples
-------- --------
>>> import numpy as np
>>> import damask >>> import damask
>>> O = damask.Rotation.from_random(3) >>> m = damask.ConfigMaterial().material_add(phase = ['Aluminum','Steel'],
>>> phase = ['Aluminum','Steel','Aluminum'] ... O = damask.Rotation.from_random(2),
>>> m = damask.ConfigMaterial().material_add(constituents={'phase':phase,'O':O}, ... homogenization = 'SX')
... homogenization='SX')
>>> m >>> m
material: material:
- constituents: - constituents:
@ -264,63 +271,59 @@ class ConfigMaterial(Config):
v: 1.0 v: 1.0
phase: Steel phase: Steel
homogenization: SX homogenization: SX
homogenization: {}
phase: {}
>>> m = damask.ConfigMaterial().material_add(phase = np.array(['Austenite','Martensite']).reshape(1,2),
... O = damask.Rotation.from_random((2,2)),
... v = np.array([0.2,0.8]).reshape(1,2),
... homogenization = ['A','B'])
>>> m
material:
- constituents: - constituents:
- O: [0.0886257, -0.144848, 0.615674, -0.769487] - phase: Austenite
v: 1.0 O: [0.659802978293224, 0.6953785848195171, 0.22426295326327111, -0.17554139512785227]
phase: Aluminum v: 0.2
homogenization: SX - phase: Martensite
O: [0.49356745891301596, 0.2841806579193434, -0.7487679215072818, -0.339085707289975]
v: 0.8
homogenization: A
- constituents:
- phase: Austenite
O: [0.26542221365204055, 0.7268854930702071, 0.4474726435701472, -0.44828201137283735]
v: 0.2
- phase: Martensite
O: [0.6545817158479885, -0.08004812803625233, -0.6226561293931374, 0.4212059104577611]
v: 0.8
homogenization: B
homogenization: {} homogenization: {}
phase: {} phase: {}
""" """
length = -1 N,n,shaped = 1,1,{}
for v in kwargs.values():
if hasattr(v,'__len__') and not isinstance(v,str):
if length != -1 and len(v) != length:
raise ValueError('Cannot add entries of different length')
else:
length = len(v)
length = max(1,length)
c = [{} for _ in range(length)] if constituents is None else \
[{'constituents':u} for u in ConfigMaterial._constituents(**constituents)]
if len(c) == 1: c = [c[0] for _ in range(length)]
if length != 1 and length != len(c):
raise ValueError('Cannot add entries of different length')
for k,v in kwargs.items(): for k,v in kwargs.items():
if hasattr(v,'__len__') and not isinstance(v,str): shaped[k] = np.array(v)
for i,vv in enumerate(v): s = shaped[k].shape[:-1] if k=='O' else shaped[k].shape
c[i][k] = vv.item() if isinstance(vv,np.generic) else vv N = max(N,s[0]) if len(s)>0 else N
else: n = max(n,s[1]) if len(s)>1 else n
for i in range(len(c)):
c[i][k] = v mat = [{'constituents':[{} for _ in range(n)]} for _ in range(N)]
if 'v' not in kwargs:
shaped['v'] = np.broadcast_to(1/n,(N,n))
for k,v in shaped.items():
target = (N,n,4) if k=='O' else (N,n)
obj = np.broadcast_to(v.reshape(util.shapeshifter(v.shape,target,mode='right')),target)
for i in range(N):
if k in ['phase','O','v']:
for j in range(n):
mat[i]['constituents'][j][k] = obj[i,j].item() if isinstance(obj[i,j],np.generic) else obj[i,j]
else:
mat[i][k] = obj[i,0].item() if isinstance(obj[i,0],np.generic) else obj[i,0]
dup = self.copy() dup = self.copy()
dup['material'] = dup['material'] + c if 'material' in dup else c dup['material'] = dup['material'] + mat if 'material' in dup else mat
return dup return dup
@staticmethod
def _constituents(N=1,**kwargs):
"""Construct list of constituents."""
N_material=1
for v in kwargs.values():
if hasattr(v,'__len__') and not isinstance(v,str): N_material = len(v)
if N == 1:
m = [[{'v':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('Cannot add entries of different length')
for i,vv in enumerate(np.array(v)):
m[i][0][k] = vv.item() if isinstance(vv,np.generic) else vv
else:
for i in range(N_material):
m[i][0][k] = v
return m
else:
raise NotImplementedError

View File

@ -122,7 +122,7 @@ class Grid:
@size.setter @size.setter
def size(self,size): def size(self,size):
if len(size) != 3 or any(np.array(size) <= 0): if len(size) != 3 or any(np.array(size) < 0):
raise ValueError(f'invalid size {size}') raise ValueError(f'invalid size {size}')
else: else:
self._size = np.array(size) self._size = np.array(size)
@ -303,7 +303,7 @@ class Grid:
Need to be ordered (1./x fast, 3./z slow). Need to be ordered (1./x fast, 3./z slow).
labels : str or list of str labels : str or list of str
Label(s) of the columns containing the material definition. Label(s) of the columns containing the material definition.
Each unique combintation of values results in one material ID. Each unique combination of values results in one material ID.
""" """
cells,size,origin = grid_filters.cellsSizeOrigin_coordinates0_point(table.get(coordinates)) cells,size,origin = grid_filters.cellsSizeOrigin_coordinates0_point(table.get(coordinates))

View File

@ -5,6 +5,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
@pytest.fixture @pytest.fixture
def ref_path(ref_path_base): def ref_path(ref_path_base):
@ -85,42 +86,26 @@ class TestConfigMaterial:
def test_from_table(self): def test_from_table(self):
N = np.random.randint(3,10) N = np.random.randint(3,10)
a = np.vstack((np.hstack((np.arange(N),np.arange(N)[::-1])),np.ones(N*2),np.zeros(N*2),np.ones(N*2))).T a = np.vstack((np.hstack((np.arange(N),np.arange(N)[::-1])),np.ones(N*2),np.zeros(N*2),np.ones(N*2),np.ones(N*2))).T
t = Table(a,{'varying':2,'constant':2}) print(a)
c = ConfigMaterial.from_table(t,constituents={'a':'varying','b':'1_constant'},c='2_constant') t = Table(a,{'varying':1,'constant':4})
c = ConfigMaterial.from_table(t,**{'phase':'varying','O':'constant','homogenization':'4_constant'})
assert len(c['material']) == N assert len(c['material']) == N
for i,m in enumerate(c['material']): for i,m in enumerate(c['material']):
c = m['constituents'][0] assert m['homogenization'] == 1 and (m['constituents'][0]['O'] == [1,0,1,1]).all()
assert m['c'] == 1 and c['b'] == 0 and (c['a'] == [i,1]).all()
def test_constituents(self): @pytest.mark.parametrize('N,n,kw',[
c = ConfigMaterial._constituents(c=1,v=[2,3]) (1,1,{'phase':'Gold',
assert c[0][0]['c'] == c[1][0]['c'] == 1 'O':[1,0,0,0],
assert c[0][0]['v'] == c[1][0]['v'] -1 ==2 'homogenization':'SX'}),
(3,1,{'phase':'Gold',
@pytest.mark.parametrize('constituents',[{'W':1,'X':[2,3]},{'Y':4},{'Z':[5,6]}]) 'O':Rotation.from_random(3),
@pytest.mark.parametrize('a',[[7.,8.],9.]) 'homogenization':'SX'}),
@pytest.mark.parametrize('b',['bd',['efg','hi']]) (2,3,{'phase':np.broadcast_to(['a','b','c'],(2,3)),
def test_material_add(self,tmp_path,constituents,a,b): 'O':Rotation.from_random((2,3)),
len_c = len(ConfigMaterial()._constituents(1,**constituents)) 'homogenization':['SX','PX']}),
len_a = len(a) if isinstance(a,list) else 1 ])
len_b = len(b) if isinstance(b,list) else 1 def test_material_add(self,kw,N,n):
m = ConfigMaterial().material_add(constituents,a=a,b=b) m = ConfigMaterial().material_add(**kw)
m.save() assert len(m['material']) == N
assert len(m['material']) == np.max([len_a,len_b,len_c]) assert len(m['material'][0]['constituents']) == n
@pytest.mark.parametrize('constituents',[{'W':1,'X':np.array([2,3])},{'Y':4},{'Z':np.array([5,6])}])
@pytest.mark.parametrize('a',[np.array([7,8]),9])
def test_material_add_np(self,tmp_path,constituents,a):
len_c = len(ConfigMaterial()._constituents(1,**constituents))
len_a = len(a) if isinstance(a,np.ndarray) else 1
m = ConfigMaterial().material_add(constituents,ld=a)
m.save()
assert len(m['material']) == np.max([len_a,len_c])
@pytest.mark.parametrize('constituents',[{'X':np.array([2,3,4,5])},{'Y':4}])
@pytest.mark.parametrize('a',[np.array([1,2,3]),[4,5,6]])
@pytest.mark.parametrize('b',[np.array([6.,7.]),[8.,9.]])
def test_material_add_invalid(self,constituents,a,b):
with pytest.raises(ValueError):
ConfigMaterial().material_add(constituents,a=a,u=b)