Merge branch 'material_add' into 'development'

configMaterial functionality

See merge request damask/DAMASK!651
This commit is contained in:
Martin Diehl 2022-11-20 12:59:01 +00:00
commit 815d825fac
4 changed files with 141 additions and 87 deletions

View File

@ -1,6 +1,6 @@
import numpy as np import numpy as np
import h5py import h5py
from typing import Sequence, Dict, Any, Collection from typing import Union, Sequence, Dict, Any, Collection
from ._typehints import FileHandle from ._typehints import FileHandle
from . import Config from . import Config
@ -160,7 +160,7 @@ class ConfigMaterial(Config):
pass pass
base_config = ConfigMaterial({'phase':{k if isinstance(k,int) else str(k):'t.b.d.' for k in np.unique(phase)}, base_config = ConfigMaterial({'phase':{k if isinstance(k,int) else str(k): None for k in np.unique(phase)},
'homogenization':{'direct':{'N_constituents':1}}}) 'homogenization':{'direct':{'N_constituents':1}}})
constituent = {k:np.atleast_1d(v[idx].squeeze()) for k,v in zip(['O','phase'],[O,phase])} constituent = {k:np.atleast_1d(v[idx].squeeze()) for k,v in zip(['O','phase'],[O,phase])}
@ -193,10 +193,10 @@ class ConfigMaterial(Config):
>>> import damask.ConfigMaterial as cm >>> import damask.ConfigMaterial as cm
>>> t = damask.Table.load('small.txt') >>> t = damask.Table.load('small.txt')
>>> t >>> t
pos pos pos qu qu qu qu phase homog 3:pos pos pos 4: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
1 1 1 0 0.8 0.19 0.24 -0.51 Steel SX 2 1 1 0 0.8 0.19 0.24 -0.51 Steel SX
>>> cm.from_table(t,O='qu',phase='phase',homogenization='homog') >>> cm.from_table(t,O='qu',phase='phase',homogenization='homog')
material: material:
- constituents: - constituents:
@ -209,8 +209,8 @@ class ConfigMaterial(Config):
v: 1.0 v: 1.0
phase: Steel phase: Steel
homogenization: SX homogenization: SX
homogenization: {} homogenization: {SX: null}
phase: {} phase: {Aluminum: null, Steel: null}
>>> cm.from_table(t,O='qu',phase='phase',homogenization='single_crystal') >>> cm.from_table(t,O='qu',phase='phase',homogenization='single_crystal')
material: material:
@ -224,8 +224,8 @@ class ConfigMaterial(Config):
v: 1.0 v: 1.0
phase: Steel phase: Steel
homogenization: single_crystal homogenization: single_crystal
homogenization: {} homogenization: {single_crystal: null}
phase: {} phase: {Aluminum: null, Steel: null}
""" """
kwargs_ = {k:table.get(v) if v in table.labels else np.atleast_2d([v]*len(table)).T for k,v in kwargs.items()} kwargs_ = {k:table.get(v) if v in table.labels else np.atleast_2d([v]*len(table)).T for k,v in kwargs.items()}
@ -233,6 +233,8 @@ class ConfigMaterial(Config):
_,idx = np.unique(np.hstack(list(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)
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()}
for what in ['phase','homogenization']:
if what not in kwargs_: kwargs_[what] = what+'_label'
return ConfigMaterial().material_add(**kwargs_) return ConfigMaterial().material_add(**kwargs_)
@ -243,7 +245,7 @@ class ConfigMaterial(Config):
Check for completeness. Check for completeness.
Only the general file layout is considered. Only the general file layout is considered.
This check does not consider whether parameters for This check does not consider whether specific parameters for
a particular phase/homogenization model are missing. a particular phase/homogenization model are missing.
Returns Returns
@ -252,52 +254,56 @@ class ConfigMaterial(Config):
Whether the material.yaml definition is complete. Whether the material.yaml definition is complete.
""" """
def LabeledList(label,items):
return f'{label.capitalize()}{"s" if len(items)>1 else ""} {util.srepr(items,",",quote=True)}'
ok = True ok = True
for top_level in ['homogenization','phase','material']: msg = []
ok &= top_level in self all = set(['homogenization','phase','material'])
if top_level not in self: print(f'{top_level} entry missing') miss = set([item for item in all if item not in self])
empty = set([item for item in all-miss if self[item] is None])
if miss:
msg.append(f'{LabeledList("top-level",miss)} missing')
ok = False
if empty:
msg.append(f'{LabeledList("top-level",empty)} empty')
if ok: if ok:
ok &= len(self['material']) > 0 ok &= len(self['material']) > 0
if len(self['material']) < 1: print('Incomplete material definition') if len(self['material']) < 1: msg.append('No materials defined')
if ok:
homogenization = set() homogenization = set()
phase = set() phase = set()
for i,v in enumerate(self['material']): for i,v in enumerate(self['material']):
if 'homogenization' in v: if 'homogenization' in v:
homogenization.add(v['homogenization']) homogenization.add(v['homogenization'])
else: else:
print(f'No homogenization specified in material {i}') msg.append(f'No homogenization specified for material {i}')
ok = False ok = False
if 'constituents' in v: if 'constituents' in v:
for ii,vv in enumerate(v['constituents']): for ii,vv in enumerate(v['constituents']):
if 'O' not in vv: if 'O' not in vv:
print('No orientation specified in constituent {ii} of material {i}') msg.append(f'No orientation specified for constituent {ii} of material {i}')
ok = False ok = False
if 'phase' in vv: if 'phase' in vv:
phase.add(vv['phase']) phase.add(vv['phase'])
else: else:
print(f'No phase specified in constituent {ii} of material {i}') msg.append(f'No phase specified for constituent {ii} of material {i}')
ok = False ok = False
for k,v in self['phase'].items(): for v,other in {'phase':phase,
if 'lattice' not in v: 'homogenization':homogenization}.items():
print(f'No lattice specified in phase {k}') me = set([] if v in empty else self[v])
if _miss := other - me:
msg.append(f'{LabeledList(v,_miss)} missing')
ok = False
if len(_empty := [item for item in me if self[v][item] is None]) > 0:
msg.append(f'{LabeledList(v,_empty)} undefined')
ok = False ok = False
for k,v in self['homogenization'].items(): print(util.srepr(msg))
if 'N_constituents' not in v:
print(f'No. of constituents not specified in homogenization {k}')
ok = False
if phase - set(self['phase']):
print(f'Phase(s) {phase-set(self["phase"])} missing')
ok = False
if homogenization - set(self['homogenization']):
print(f'Homogenization(s) {homogenization-set(self["homogenization"])} missing')
ok = False
return ok return ok
@ -320,7 +326,7 @@ class ConfigMaterial(Config):
if 'phase' in self: if 'phase' in self:
for k,v in self['phase'].items(): for k,v in self['phase'].items():
if 'lattice' in v: if v is not None and 'lattice' in v:
try: try:
Orientation(lattice=v['lattice']) Orientation(lattice=v['lattice'])
except KeyError: except KeyError:
@ -418,6 +424,8 @@ class ConfigMaterial(Config):
---------- ----------
**kwargs **kwargs
Key-value pairs. Key-value pairs.
First index of array-like values runs over materials,
whereas second index runs over constituents.
Returns Returns
------- -------
@ -426,13 +434,12 @@ class ConfigMaterial(Config):
Examples Examples
-------- --------
Create a dual-phase steel microstructure for micromechanical simulations: Create two grains of ferrite and one grain of martensite, each with random orientation:
>>> import numpy as np
>>> import damask >>> import damask
>>> m = damask.ConfigMaterial() >>> m = damask.ConfigMaterial()
>>> m = m.material_add(phase = ['Ferrite','Martensite'], >>> m = m.material_add(phase = ['Ferrite','Martensite','Ferrite'],
... O = damask.Rotation.from_random(2), ... O = damask.Rotation.from_random(3),
... homogenization = 'SX') ... homogenization = 'SX')
>>> m >>> m
material: material:
@ -446,60 +453,91 @@ class ConfigMaterial(Config):
v: 1.0 v: 1.0
phase: Martensite phase: Martensite
homogenization: SX homogenization: SX
homogenization: {} - constituents:
phase: {} - O: [0.47925185, -0.04294454, 0.78760173, -0.3849116 ]
v: 1.0
phase: Ferrite
homogenization: SX
homogenization: {SX: null}
phase: {Ferrite: null, Martensite: null}
Create a duplex stainless steel microstructure for forming simulations: Create hundred materials that each approximate a duplex stainless steel microstructure
with three austenite and one relatively bigger ferrite grain of random orientation each:
>>> import numpy as np
>>> import damask >>> import damask
>>> m = damask.ConfigMaterial() >>> m = damask.ConfigMaterial()
>>> m = m.material_add(phase = np.array(['Austenite','Ferrite']).reshape(1,2), >>> m = m.material_add(phase = np.array(['Austenite']*3+['Ferrite']),
... O = damask.Rotation.from_random((2,2)), ... O = damask.Rotation.from_random((100,4)),
... v = np.array([0.2,0.8]).reshape(1,2), ... v = np.array([0.2]*3+[0.4]),
... homogenization = 'Taylor') ... homogenization = 'Taylor')
>>> m >>> m
material: material:
- constituents: - constituents:
- phase: Austenite - v: 0.2
O: [0.659802978293224, 0.6953785848195171, 0.22426295326327111, -0.17554139512785227] phase: Austenite
v: 0.2 O: [0.46183665006602664, 0.2215160420973196, -0.5594313187331139, 0.6516702781083836]
- phase: Ferrite - v: 0.2
O: [0.49356745891301596, 0.2841806579193434, -0.7487679215072818, -0.339085707289975] phase: Austenite
v: 0.8 O: [0.11321658382410027, 0.6354079414360444, 0.00562701344273936, 0.7638108992590535]
- v: 0.2
phase: Austenite
O: [0.050991978809077604, 0.8069522034362003, -0.11352928955610851, -0.5773552285027659]
- v: 0.4
phase: Ferrite
O: [0.9460076150721788, 0.15880754622367604, -0.0069841062241482385, -0.28249066842661014]
homogenization: Taylor homogenization: Taylor
.
.
.
- constituents: - constituents:
- phase: Austenite - v: 0.2
O: [0.26542221365204055, 0.7268854930702071, 0.4474726435701472, -0.44828201137283735] phase: Austenite
v: 0.2 O: [0.12531400788494199, -0.18637769037997565, 0.31737548053338394, -0.9213210951197429]
- phase: Ferrite - v: 0.2
O: [0.6545817158479885, -0.08004812803625233, -0.6226561293931374, 0.4212059104577611] phase: Austenite
v: 0.8 O: [0.37453930577161404, -0.33529507696450805, -0.3266564259130028, -0.800370601162502]
- v: 0.2
phase: Austenite
O: [0.035776891752713764, -0.720706371010592, -0.4540438656728926, -0.5226342017569017]
- v: 0.4
phase: Ferrite
O: [0.6782596727966124, -0.20800082041703685, -0.138636083554039, 0.6909989227925536]
homogenization: Taylor homogenization: Taylor
homogenization: {}
phase: {} homogenization: {Taylor: null}
phase: {Austenite: null, Ferrite: null}
""" """
N,n,shaped = 1,1,{} _constituent_properties = ['phase','O','v','V_e']
_dim = {'O':(4,),'V_e':(3,3,)}
_ex = dict((k, -len(v)) for k, v in _dim.items())
N,n = 1,1
shaped : Dict[str, Union[None,np.ndarray]] = \
{'v': None,
'phase': None,
'homogenization': None,
}
map_dim = {'O':-1,'V_e':-2}
for k,v in kwargs.items(): for k,v in kwargs.items():
shaped[k] = np.array(v) shaped[k] = np.array(v)
s = shaped[k].shape[:map_dim.get(k,None)] s = shaped[k].shape[:_ex.get(k,None)] # type: ignore
N = max(N,s[0]) if len(s)>0 else N N = max(N,s[0]) if len(s)>0 else N
n = max(n,s[1]) if len(s)>1 else n n = max(n,s[1]) if len(s)>1 else n
shaped['v'] = np.array(1./n) if shaped['v'] is None else shaped['v']
mat: Sequence[dict] = [{'constituents':[{} for _ in range(n)]} for _ in range(N)] mat: Sequence[dict] = [{'constituents':[{} for _ in range(n)]} for _ in range(N)]
if 'v' not in kwargs:
shaped['v'] = np.broadcast_to(1/n,(N,n))
map_shape = {'O':(N,n,4),'V_e':(N,n,3,3)}
for k,v in shaped.items(): for k,v in shaped.items():
target = map_shape.get(k,(N,n)) target = (N,n) + _dim.get(k,())
obj = np.broadcast_to(v.reshape(util.shapeshifter(v.shape, target, mode = 'right')), target) obj = np.broadcast_to(np.array(v).reshape(util.shapeshifter(() if v is None else v.shape,
target,
mode = 'right')),
target)
for i in range(N): for i in range(N):
if k in ['phase','O','v','V_e']: if k in _constituent_properties:
for j in range(n): 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] mat[i]['constituents'][j][k] = obj[i,j].item() if isinstance(obj[i,j],np.generic) else obj[i,j]
else: else:
@ -508,4 +546,8 @@ class ConfigMaterial(Config):
dup = self.copy() dup = self.copy()
dup['material'] = dup['material'] + mat if 'material' in dup else mat dup['material'] = dup['material'] + mat if 'material' in dup else mat
for what in [item for item in ['phase','homogenization'] if shaped[item] is not None]:
for k in np.unique(shaped[what]): # type: ignore
if k not in dup[what]: dup[what][str(k)] = None
return dup return dup

View File

@ -40,9 +40,10 @@ _colors = {
# Functions # Functions
#################################################################################################### ####################################################################################################
def srepr(msg, def srepr(msg,
glue: str = '\n') -> str: glue: str = '\n',
quote: bool = False) -> str:
r""" r"""
Join items with glue string. Join (quoted) items with glue string.
Parameters Parameters
---------- ----------
@ -50,19 +51,22 @@ def srepr(msg,
Items to join. Items to join.
glue : str, optional glue : str, optional
Glue used for joining operation. Defaults to '\n'. Glue used for joining operation. Defaults to '\n'.
quote : bool, optional
Quote items. Defaults to False.
Returns Returns
------- -------
joined : str joined : str
String representation of the joined items. String representation of the joined and quoted items.
""" """
q = '"' if quote else ''
if (not hasattr(msg, 'strip') and if (not hasattr(msg, 'strip') and
(hasattr(msg, '__getitem__') or (hasattr(msg, '__getitem__') or
hasattr(msg, '__iter__'))): hasattr(msg, '__iter__'))):
return glue.join(str(x) for x in msg) return glue.join(q+str(x)+q for x in msg)
else: else:
return msg if isinstance(msg,str) else repr(msg) return q+(msg if isinstance(msg,str) else repr(msg))+q
def emph(msg) -> str: def emph(msg) -> str:

View File

@ -1,4 +1,4 @@
phase: {'1': t.b.d., '2': t.b.d.} phase: {'1': null, '2': null}
homogenization: homogenization:
direct: {N_constituents: 1} direct: {N_constituents: 1}

View File

@ -65,17 +65,6 @@ class TestConfigMaterial:
del material_config['material'][0]['homogenization'] del material_config['material'][0]['homogenization']
assert not material_config.is_complete assert not material_config.is_complete
def test_incomplete_homogenization_N_constituents(self,ref_path):
material_config = ConfigMaterial.load(ref_path/'material.yaml')
for h in material_config['homogenization'].keys():
del material_config['homogenization'][h]['N_constituents']
assert not material_config.is_complete
def test_incomplete_phase_lattice(self,ref_path):
material_config = ConfigMaterial.load(ref_path/'material.yaml')
del material_config['phase']['Aluminum']['lattice']
assert not material_config.is_complete
def test_incomplete_wrong_phase(self,ref_path): def test_incomplete_wrong_phase(self,ref_path):
material_config = ConfigMaterial.load(ref_path/'material.yaml') material_config = ConfigMaterial.load(ref_path/'material.yaml')
new = material_config.material_rename_phase({'Steel':'FeNbC'}) new = material_config.material_rename_phase({'Steel':'FeNbC'})
@ -86,6 +75,16 @@ class TestConfigMaterial:
new = material_config.material_rename_homogenization({'Taylor':'isostrain'}) new = material_config.material_rename_homogenization({'Taylor':'isostrain'})
assert not new.is_complete assert not new.is_complete
def test_empty_phase(self,ref_path):
material_config = ConfigMaterial.load(ref_path/'material.yaml')
material_config['phase'] = None
assert not material_config.is_complete
def test_empty_homogenization(self,ref_path):
material_config = ConfigMaterial.load(ref_path/'material.yaml')
material_config['homogenization'] = None
assert not material_config.is_complete
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])), a = np.vstack((np.hstack((np.arange(N),np.arange(N)[::-1])),
@ -98,6 +97,15 @@ class TestConfigMaterial:
for i,m in enumerate(c['material']): for i,m in enumerate(c['material']):
assert m['homogenization'] == 1 and (m['constituents'][0]['O'] == [1,0,1,1]).all() assert m['homogenization'] == 1 and (m['constituents'][0]['O'] == [1,0,1,1]).all()
def test_updated_dicts(self,ref_path):
m1 = ConfigMaterial().material_add(phase=['Aluminum'],O=[1.0,0.0,0.0,0.0],homogenization='SX')
m2 = ConfigMaterial.load(ref_path/'material.yaml')
for k in m2['phase']:
m2 = m2.material_add(phase=[k],O=[1.0,0.0,0.0,0.0],homogenization='SX')
assert not m2['phase'].get(k) is None
assert m1['phase'].get('Aluminum') is None
assert m1['homogenization'].get('SX') is None
def test_from_table_with_constant(self): def test_from_table_with_constant(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])), a = np.vstack((np.hstack((np.arange(N),np.arange(N)[::-1])),