Merge branch 'generalize-configmaterial' into 'development'

Generalize configmaterial

See merge request damask/DAMASK!346
This commit is contained in:
Sharan Roongta 2021-02-24 13:58:53 +00:00
commit 0d0226f703
4 changed files with 104 additions and 116 deletions

@ -1 +1 @@
Subproject commit f654a4143b1fbbecd137dc5d2193f5cf48ab1448
Subproject commit 0289c1bbfec1a1aef77a8cbaeed134035549e738

View File

@ -3,6 +3,7 @@ import numpy as np
from . import Config
from . import Rotation
from . import Orientation
from . import util
class ConfigMaterial(Config):
"""Material configuration."""
@ -46,7 +47,7 @@ class ConfigMaterial(Config):
@staticmethod
def from_table(table,constituents={},**kwargs):
def from_table(table,**kwargs):
"""
Load from an ASCII table.
@ -54,12 +55,9 @@ class ConfigMaterial(Config):
----------
table : damask.Table
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
Keyword arguments where the key is the name and the value specifies
the label of the data column in the table
Keyword arguments where the key is the name and the value specifies
the label of the data column in the table.
Examples
--------
@ -70,7 +68,8 @@ class ConfigMaterial(Config):
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,{'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:
- constituents:
- O: [0.19, 0.8, 0.24, -0.51]
@ -86,16 +85,13 @@ class ConfigMaterial(Config):
phase: {}
"""
constituents_ = {k:table.get(v) for k,v in constituents.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)
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
@ -153,7 +149,7 @@ class ConfigMaterial(Config):
@property
def is_valid(self):
"""Check for valid file layout."""
"""Check for valid content."""
ok = True
if 'phase' in self:
@ -162,8 +158,7 @@ class ConfigMaterial(Config):
try:
Orientation(lattice=v['lattice'])
except KeyError:
s = v['lattice']
print(f"Invalid lattice: '{s}' in phase '{k}'")
print(f"Invalid lattice '{v['lattice']}' in phase '{k}'")
ok = False
if 'material' in self:
@ -171,16 +166,15 @@ class ConfigMaterial(Config):
if 'constituents' in m:
v = 0.0
for c in m['constituents']:
v+= float(c['v'])
v += float(c['v'])
if 'O' in c:
try:
Rotation.from_quaternion(c['O'])
except ValueError:
o = c['O']
print(f"Invalid orientation: '{o}' in material '{i}'")
print(f"Invalid orientation '{c['O']}' in material '{i}'")
ok = False
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
return ok
@ -199,6 +193,11 @@ class ConfigMaterial(Config):
constituent: list of ints, optional
Limit renaming to selected constituents.
Returns
-------
cfg : damask.ConfigMaterial
Updated material configuration.
"""
dup = self.copy()
for i,m in enumerate(dup['material']):
@ -223,6 +222,11 @@ class ConfigMaterial(Config):
ID: list of ints, optional
Limit renaming to selected homogenization IDs.
Returns
-------
cfg : damask.ConfigMaterial
Updated material configuration.
"""
dup = self.copy()
for i,m in enumerate(dup['material']):
@ -234,24 +238,27 @@ class ConfigMaterial(Config):
return dup
def material_add(self,constituents=None,**kwargs):
def material_add(self,**kwargs):
"""
Add material entries.
Parameters
----------
constituents : dict, optional
Entries for 'constituents' as key-value pair.
**kwargs
Key-value pairs.
Returns
-------
cfg : damask.ConfigMaterial
Updated material configuration.
Examples
--------
>>> import numpy as np
>>> import damask
>>> O = damask.Rotation.from_random(3)
>>> phase = ['Aluminum','Steel','Aluminum']
>>> m = damask.ConfigMaterial().material_add(constituents={'phase':phase,'O':O},
... homogenization='SX')
>>> m = damask.ConfigMaterial().material_add(phase = ['Aluminum','Steel'],
... O = damask.Rotation.from_random(2),
... homogenization = 'SX')
>>> m
material:
- constituents:
@ -264,63 +271,59 @@ class ConfigMaterial(Config):
v: 1.0
phase: Steel
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:
- O: [0.0886257, -0.144848, 0.615674, -0.769487]
v: 1.0
phase: Aluminum
homogenization: SX
- phase: Austenite
O: [0.659802978293224, 0.6953785848195171, 0.22426295326327111, -0.17554139512785227]
v: 0.2
- 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: {}
phase: {}
"""
length = -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')
N,n,shaped = 1,1,{}
for k,v in kwargs.items():
if hasattr(v,'__len__') and not isinstance(v,str):
for i,vv in enumerate(v):
c[i][k] = vv.item() if isinstance(vv,np.generic) else vv
else:
for i in range(len(c)):
c[i][k] = v
shaped[k] = np.array(v)
s = shaped[k].shape[:-1] if k=='O' else shaped[k].shape
N = max(N,s[0]) if len(s)>0 else N
n = max(n,s[1]) if len(s)>1 else n
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['material'] = dup['material'] + c if 'material' in dup else c
dup['material'] = dup['material'] + mat if 'material' in dup else mat
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
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}')
else:
self._size = np.array(size)
@ -303,7 +303,7 @@ class Grid:
Need to be ordered (1./x fast, 3./z slow).
labels : str or list of str
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))

View File

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