From ce6c86dd6d234f7b72713cb0bf327ef2d7a2cb12 Mon Sep 17 00:00:00 2001 From: Martin Diehl Date: Tue, 24 Jan 2023 08:15:03 +0100 Subject: [PATCH] essentially same behavior as before, but better documentation, a few simplifications, and typehints --- python/damask/_config.py | 24 ++++++++---- python/damask/_configmaterial.py | 64 ++++++++++++++++---------------- python/damask/_typehints.py | 1 + python/damask/util.py | 2 +- 4 files changed, 52 insertions(+), 39 deletions(-) diff --git a/python/damask/_config.py b/python/damask/_config.py index 1be1e313a..db215b097 100644 --- a/python/damask/_config.py +++ b/python/damask/_config.py @@ -43,7 +43,7 @@ class NiceDumper(SafeDumper): return self.represent_data(data.tolist()) if isinstance(data, Rotation): return self.represent_data(data.quaternion.tolist()) - if hasattr(data, 'dtype'): + if isinstance(data, np.generic): return self.represent_data(data.item()) return super().represent_data(data) @@ -57,13 +57,23 @@ class Config(dict): """YAML-based configuration.""" def __init__(self, - yml: Union[None, str, Dict[str, Any]] = None, + config: Optional[Union[str, Dict[str, Any]]] = None, **kwargs): - """Initialize from YAML, dict, or key=value pairs.""" - if isinstance(yml,str): - kwargs.update(yaml.load(yml, Loader=SafeLoader)) - elif isinstance(yml,dict): - kwargs.update(yml) + """ + New YAML-based configuration. + + Parameters + ---------- + 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) diff --git a/python/damask/_configmaterial.py b/python/damask/_configmaterial.py index 5ee8ea73d..a6c6f3bc8 100644 --- a/python/damask/_configmaterial.py +++ b/python/damask/_configmaterial.py @@ -1,8 +1,9 @@ -import numpy as np -import h5py 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 Rotation from . import Orientation @@ -22,33 +23,34 @@ class ConfigMaterial(Config): """ def __init__(self, - d: Optional[Dict[str, Any]] = None, + config: Optional[Dict[str, Any]] = None, **kwargs): """ New material configuration. Parameters ---------- - d : dictionary or YAML string, optional - Initial content. Defaults to None, in which case empty entries for + config : dict or str, optional + 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. kwargs : key=value pairs, optional Initial content specified as pairs of key=value. """ default: Collection - if d is None: + if config is None: for section, default in {'material':[],'homogenization':{},'phase':{}}.items(): if section not in kwargs: kwargs.update({section:default}) - super().__init__(d,**kwargs) + super().__init__(config,**kwargs) def save(self, fname: FileHandle = 'material.yaml', **kwargs): """ - Save to yaml file. + Save to YAML file. Parameters ---------- @@ -65,7 +67,7 @@ class ConfigMaterial(Config): def load(cls, fname: FileHandle = 'material.yaml') -> 'ConfigMaterial': """ - Load from yaml file. + Load from YAML file. Parameters ---------- @@ -361,7 +363,7 @@ class ConfigMaterial(Config): Parameters ---------- - mapping: dictionary + mapping: dict Mapping from old name to new name ID: list of ints, optional Limit renaming to selected material IDs. @@ -394,7 +396,7 @@ class ConfigMaterial(Config): Parameters ---------- - mapping: dictionary + mapping: dict Mapping from old name to new name ID: list of ints, optional Limit renaming to selected homogenization IDs. @@ -416,11 +418,11 @@ class ConfigMaterial(Config): def material_add(self,*, - homogenization: Any = None, - phase: Any = None, - v: Any = None, - O: Any = None, - V_e: Any = None) -> 'ConfigMaterial': + homogenization: Optional[Union[str,StrSequence]] = None, + phase: Optional[Union[str,StrSequence]] = None, + v: Optional[Union[float,FloatSequence]] = None, + O: Optional[Union[float,FloatSequence]] = None, + V_e: Optional[Union[float,FloatSequence]] = None) -> 'ConfigMaterial': """ Add material entries. @@ -432,6 +434,7 @@ class ConfigMaterial(Config): Phase label (per constituent). v: (array-like) of float, optional Constituent volume fraction (per constituent). + Defaults to 1/N_constituents O: (array-like) of damask.Rotation or np.array/list of shape(4), optional Orientation as unit quaternion (per constituent). V_e: (array-like) of np.array/list of shape(3,3), optional @@ -444,9 +447,8 @@ class ConfigMaterial(Config): Notes ----- - First index of array-like values that are defined per - consituent runs over materials, whereas second index runs - over constituents. + First index of array-like values that are defined per constituent + runs over materials, whereas second index runs over constituents. Examples -------- @@ -533,32 +535,32 @@ class ConfigMaterial(Config): _dim = {'O':(4,),'V_e':(3,3,)} _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]] = \ {'v': None, 'phase': None, 'homogenization': None, } - for k,v in kwargs.items(): - shaped[k] = np.array(v) - 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[1]) if len(s)>1 else n + for arg,value in kwargs.items(): + shaped[arg] = np.array(value) + s = shaped[arg].shape[:_ex.get(arg,None)] # type: ignore + N_materials = max(N_materials,s[0]) if len(s)>0 else N_materials + 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(): - 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, target, mode = 'right')), target) - for i in range(N): + for i in range(N_materials): 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] else: mat[i][k] = obj[i,0].item() if isinstance(obj[i,0],np.generic) else obj[i,0] diff --git a/python/damask/_typehints.py b/python/damask/_typehints.py index 5fcf39a41..5891e6305 100644 --- a/python/damask/_typehints.py +++ b/python/damask/_typehints.py @@ -8,6 +8,7 @@ import numpy as np FloatSequence = Union[np.ndarray,Sequence[float]] IntSequence = Union[np.ndarray,Sequence[int]] +StrSequence = Union[np.ndarray,Sequence[str]] FileHandle = Union[TextIO, str, Path] 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']] diff --git a/python/damask/util.py b/python/damask/util.py index 666bffc0b..cfcde1efe 100644 --- a/python/damask/util.py +++ b/python/damask/util.py @@ -800,7 +800,7 @@ class ProgressBar: prefix: str, bar_length: int): """ - Set current time as basis for ETA estimation. + New progress bar. Parameters ----------