Merge branch 'YAML-class-improvements' into 'development'

some improvements to the YAML-related classes in Python

See merge request damask/DAMASK!710
This commit is contained in:
Daniel Otto de Mentock 2023-01-31 17:42:34 +00:00
commit 750a0443ad
4 changed files with 52 additions and 39 deletions

View File

@ -43,7 +43,7 @@ class NiceDumper(SafeDumper):
return self.represent_data(data.tolist()) return self.represent_data(data.tolist())
if isinstance(data, Rotation): if isinstance(data, Rotation):
return self.represent_data(data.quaternion.tolist()) return self.represent_data(data.quaternion.tolist())
if hasattr(data, 'dtype'): if isinstance(data, np.generic):
return self.represent_data(data.item()) return self.represent_data(data.item())
return super().represent_data(data) return super().represent_data(data)
@ -57,13 +57,23 @@ class Config(dict):
"""YAML-based configuration.""" """YAML-based configuration."""
def __init__(self, def __init__(self,
yml: Union[None, str, Dict[str, Any]] = None, config: Optional[Union[str, Dict[str, Any]]] = None,
**kwargs): **kwargs):
"""Initialize from YAML, dict, or key=value pairs.""" """
if isinstance(yml,str): New YAML-based configuration.
kwargs.update(yaml.load(yml, Loader=SafeLoader))
elif isinstance(yml,dict): Parameters
kwargs.update(yml) ----------
config : dict or str, optional
Configuration. String needs to be valid YAML.
**kwargs: arbitray keyword-value pairs, optional
Top level entries of the configuration.
"""
if isinstance(config,str):
kwargs.update(yaml.load(config, Loader=SafeLoader))
elif isinstance(config,dict):
kwargs.update(config)
super().__init__(**kwargs) super().__init__(**kwargs)

View File

@ -1,8 +1,9 @@
import numpy as np
import h5py
from typing import Optional, Union, Sequence, Dict, Any, Collection from typing import Optional, Union, Sequence, Dict, Any, Collection
from ._typehints import FileHandle import numpy as np
import h5py
from ._typehints import FileHandle, FloatSequence, StrSequence
from . import Config from . import Config
from . import Rotation from . import Rotation
from . import Orientation from . import Orientation
@ -22,33 +23,34 @@ class ConfigMaterial(Config):
""" """
def __init__(self, def __init__(self,
d: Optional[Dict[str, Any]] = None, config: Optional[Dict[str, Any]] = None,
**kwargs): **kwargs):
""" """
New material configuration. New material configuration.
Parameters Parameters
---------- ----------
d : dictionary or YAML string, optional config : dict or str, optional
Initial content. Defaults to None, in which case empty entries for Material configuration. String needs to be valid YAML.
Defaults to None, in which case empty entries for
any missing material, homogenization, and phase entry are created. any missing material, homogenization, and phase entry are created.
kwargs : key=value pairs, optional kwargs : key=value pairs, optional
Initial content specified as pairs of key=value. Initial content specified as pairs of key=value.
""" """
default: Collection default: Collection
if d is None: if config is None:
for section, default in {'material':[],'homogenization':{},'phase':{}}.items(): for section, default in {'material':[],'homogenization':{},'phase':{}}.items():
if section not in kwargs: kwargs.update({section:default}) if section not in kwargs: kwargs.update({section:default})
super().__init__(d,**kwargs) super().__init__(config,**kwargs)
def save(self, def save(self,
fname: FileHandle = 'material.yaml', fname: FileHandle = 'material.yaml',
**kwargs): **kwargs):
""" """
Save to yaml file. Save to YAML file.
Parameters Parameters
---------- ----------
@ -65,7 +67,7 @@ class ConfigMaterial(Config):
def load(cls, def load(cls,
fname: FileHandle = 'material.yaml') -> 'ConfigMaterial': fname: FileHandle = 'material.yaml') -> 'ConfigMaterial':
""" """
Load from yaml file. Load from YAML file.
Parameters Parameters
---------- ----------
@ -361,7 +363,7 @@ class ConfigMaterial(Config):
Parameters Parameters
---------- ----------
mapping: dictionary mapping: dict
Mapping from old name to new name Mapping from old name to new name
ID: list of ints, optional ID: list of ints, optional
Limit renaming to selected material IDs. Limit renaming to selected material IDs.
@ -394,7 +396,7 @@ class ConfigMaterial(Config):
Parameters Parameters
---------- ----------
mapping: dictionary mapping: dict
Mapping from old name to new name Mapping from old name to new name
ID: list of ints, optional ID: list of ints, optional
Limit renaming to selected homogenization IDs. Limit renaming to selected homogenization IDs.
@ -416,11 +418,11 @@ class ConfigMaterial(Config):
def material_add(self,*, def material_add(self,*,
homogenization: Any = None, homogenization: Optional[Union[str,StrSequence]] = None,
phase: Any = None, phase: Optional[Union[str,StrSequence]] = None,
v: Any = None, v: Optional[Union[float,FloatSequence]] = None,
O: Any = None, O: Optional[Union[float,FloatSequence]] = None,
V_e: Any = None) -> 'ConfigMaterial': V_e: Optional[Union[float,FloatSequence]] = None) -> 'ConfigMaterial':
""" """
Add material entries. Add material entries.
@ -432,6 +434,7 @@ class ConfigMaterial(Config):
Phase label (per constituent). Phase label (per constituent).
v: (array-like) of float, optional v: (array-like) of float, optional
Constituent volume fraction (per constituent). Constituent volume fraction (per constituent).
Defaults to 1/N_constituents
O: (array-like) of damask.Rotation or np.array/list of shape(4), optional O: (array-like) of damask.Rotation or np.array/list of shape(4), optional
Orientation as unit quaternion (per constituent). Orientation as unit quaternion (per constituent).
V_e: (array-like) of np.array/list of shape(3,3), optional V_e: (array-like) of np.array/list of shape(3,3), optional
@ -444,9 +447,8 @@ class ConfigMaterial(Config):
Notes Notes
----- -----
First index of array-like values that are defined per First index of array-like values that are defined per constituent
consituent runs over materials, whereas second index runs runs over materials, whereas second index runs over constituents.
over constituents.
Examples Examples
-------- --------
@ -533,32 +535,32 @@ class ConfigMaterial(Config):
_dim = {'O':(4,),'V_e':(3,3,)} _dim = {'O':(4,),'V_e':(3,3,)}
_ex = dict((k, -len(v)) for k, v in _dim.items()) _ex = dict((k, -len(v)) for k, v in _dim.items())
N,n = 1,1 N_materials,N_constituents = 1,1
shaped : Dict[str, Union[None,np.ndarray]] = \ shaped : Dict[str, Union[None,np.ndarray]] = \
{'v': None, {'v': None,
'phase': None, 'phase': None,
'homogenization': None, 'homogenization': None,
} }
for k,v in kwargs.items(): for arg,value in kwargs.items():
shaped[k] = np.array(v) shaped[arg] = np.array(value)
s = shaped[k].shape[:_ex.get(k,None)] # type: ignore s = shaped[arg].shape[:_ex.get(arg,None)] # type: ignore
N = max(N,s[0]) if len(s)>0 else N N_materials = max(N_materials,s[0]) if len(s)>0 else N_materials
n = max(n,s[1]) if len(s)>1 else n N_constituents = max(N_constituents,s[1]) if len(s)>1 else N_constituents
shaped['v'] = np.array(1./n) if shaped['v'] is None else shaped['v'] shaped['v'] = np.array(1./N_constituents) 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_constituents)]} for _ in range(N_materials)]
for k,v in shaped.items(): for k,v in shaped.items():
target = (N,n) + _dim.get(k,()) target = (N_materials,N_constituents) + _dim.get(k,())
obj = np.broadcast_to(np.array(v).reshape(util.shapeshifter(() if v is None else v.shape, obj = np.broadcast_to(np.array(v).reshape(util.shapeshifter(() if v is None else v.shape,
target, target,
mode = 'right')), mode = 'right')),
target) target)
for i in range(N): for i in range(N_materials):
if k in _constituent_properties: if k in _constituent_properties:
for j in range(n): for j in range(N_constituents):
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:
mat[i][k] = obj[i,0].item() if isinstance(obj[i,0],np.generic) else obj[i,0] mat[i][k] = obj[i,0].item() if isinstance(obj[i,0],np.generic) else obj[i,0]

View File

@ -8,6 +8,7 @@ import numpy as np
FloatSequence = Union[np.ndarray,Sequence[float]] FloatSequence = Union[np.ndarray,Sequence[float]]
IntSequence = Union[np.ndarray,Sequence[int]] IntSequence = Union[np.ndarray,Sequence[int]]
StrSequence = Union[np.ndarray,Sequence[str]]
FileHandle = Union[TextIO, str, Path] FileHandle = Union[TextIO, str, Path]
CrystalFamily = Union[None,Literal['triclinic', 'monoclinic', 'orthorhombic', 'tetragonal', 'hexagonal', 'cubic']] CrystalFamily = Union[None,Literal['triclinic', 'monoclinic', 'orthorhombic', 'tetragonal', 'hexagonal', 'cubic']]
CrystalLattice = Union[None,Literal['aP', 'mP', 'mS', 'oP', 'oS', 'oI', 'oF', 'tP', 'tI', 'hP', 'cP', 'cI', 'cF']] CrystalLattice = Union[None,Literal['aP', 'mP', 'mS', 'oP', 'oS', 'oI', 'oF', 'tP', 'tI', 'hP', 'cP', 'cI', 'cF']]

View File

@ -800,7 +800,7 @@ class ProgressBar:
prefix: str, prefix: str,
bar_length: int): bar_length: int):
""" """
Set current time as basis for ETA estimation. New progress bar.
Parameters Parameters
---------- ----------