05 Typehints config configmaterial
This commit is contained in:
parent
98381bff2a
commit
086ff42be5
|
@ -2,25 +2,34 @@ import copy
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from collections.abc import Iterable
|
from collections.abc import Iterable
|
||||||
import abc
|
import abc
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Union, Dict, Any, Type, TypeVar
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
from ._typehints import FileHandle
|
||||||
from . import Rotation
|
from . import Rotation
|
||||||
|
|
||||||
|
MyType = TypeVar('MyType', bound='Config')
|
||||||
|
|
||||||
class NiceDumper(yaml.SafeDumper):
|
class NiceDumper(yaml.SafeDumper):
|
||||||
"""Make YAML readable for humans."""
|
"""Make YAML readable for humans."""
|
||||||
|
|
||||||
def write_line_break(self, data=None):
|
def write_line_break(self,
|
||||||
|
data: str = None):
|
||||||
super().write_line_break(data)
|
super().write_line_break(data)
|
||||||
|
|
||||||
if len(self.indents) == 1:
|
if len(self.indents) == 1:
|
||||||
super().write_line_break()
|
super().write_line_break()
|
||||||
|
|
||||||
def increase_indent(self, flow=False, indentless=False):
|
def increase_indent(self,
|
||||||
|
flow: bool = False,
|
||||||
|
indentless: bool = False):
|
||||||
return super().increase_indent(flow, False)
|
return super().increase_indent(flow, False)
|
||||||
|
|
||||||
def represent_data(self, data):
|
def represent_data(self,
|
||||||
|
data: Any):
|
||||||
"""Cast Config objects and its subclasses to dict."""
|
"""Cast Config objects and its subclasses to dict."""
|
||||||
if isinstance(data, dict) and type(data) != dict:
|
if isinstance(data, dict) and type(data) != dict:
|
||||||
return self.represent_data(dict(data))
|
return self.represent_data(dict(data))
|
||||||
|
@ -31,14 +40,17 @@ class NiceDumper(yaml.SafeDumper):
|
||||||
else:
|
else:
|
||||||
return super().represent_data(data)
|
return super().represent_data(data)
|
||||||
|
|
||||||
def ignore_aliases(self, data):
|
def ignore_aliases(self,
|
||||||
|
data: Any) -> bool:
|
||||||
"""Do not use references to existing objects."""
|
"""Do not use references to existing objects."""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
class Config(dict):
|
class Config(dict):
|
||||||
"""YAML-based configuration."""
|
"""YAML-based configuration."""
|
||||||
|
|
||||||
def __init__(self,yml=None,**kwargs):
|
def __init__(self,
|
||||||
|
yml: Union[str, Dict[str, Any]] = None,
|
||||||
|
**kwargs):
|
||||||
"""Initialize from YAML, dict, or key=value pairs."""
|
"""Initialize from YAML, dict, or key=value pairs."""
|
||||||
if isinstance(yml,str):
|
if isinstance(yml,str):
|
||||||
kwargs.update(yaml.safe_load(yml))
|
kwargs.update(yaml.safe_load(yml))
|
||||||
|
@ -47,7 +59,7 @@ class Config(dict):
|
||||||
|
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
"""Show as in file."""
|
"""Show as in file."""
|
||||||
output = StringIO()
|
output = StringIO()
|
||||||
self.save(output)
|
self.save(output)
|
||||||
|
@ -55,14 +67,15 @@ class Config(dict):
|
||||||
return ''.join(output.readlines())
|
return ''.join(output.readlines())
|
||||||
|
|
||||||
|
|
||||||
def __copy__(self):
|
def __copy__(self: MyType) -> MyType:
|
||||||
"""Create deep copy."""
|
"""Create deep copy."""
|
||||||
return copy.deepcopy(self)
|
return copy.deepcopy(self)
|
||||||
|
|
||||||
copy = __copy__
|
copy = __copy__
|
||||||
|
|
||||||
|
|
||||||
def __or__(self,other):
|
def __or__(self: MyType,
|
||||||
|
other) -> MyType:
|
||||||
"""
|
"""
|
||||||
Update configuration with contents of other.
|
Update configuration with contents of other.
|
||||||
|
|
||||||
|
@ -76,18 +89,24 @@ class Config(dict):
|
||||||
updated : damask.Config
|
updated : damask.Config
|
||||||
Updated configuration.
|
Updated configuration.
|
||||||
|
|
||||||
|
Note
|
||||||
|
----
|
||||||
|
This functionality is a backport for Python 3.8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
duplicate = self.copy()
|
duplicate = self.copy()
|
||||||
duplicate.update(other)
|
duplicate.update(other)
|
||||||
return duplicate
|
return duplicate
|
||||||
|
|
||||||
|
|
||||||
def __ior__(self,other):
|
def __ior__(self: MyType,
|
||||||
|
other) -> MyType:
|
||||||
"""Update configuration with contents of other."""
|
"""Update configuration with contents of other."""
|
||||||
return self.__or__(other)
|
return self.__or__(other)
|
||||||
|
|
||||||
|
|
||||||
def delete(self,keys):
|
def delete(self: MyType,
|
||||||
|
keys: Union[Iterable, str]) -> MyType:
|
||||||
"""
|
"""
|
||||||
Remove configuration keys.
|
Remove configuration keys.
|
||||||
|
|
||||||
|
@ -109,7 +128,8 @@ class Config(dict):
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls,fname):
|
def load(cls: Type[MyType],
|
||||||
|
fname: FileHandle) -> MyType:
|
||||||
"""
|
"""
|
||||||
Load from yaml file.
|
Load from yaml file.
|
||||||
|
|
||||||
|
@ -124,14 +144,15 @@ class Config(dict):
|
||||||
Configuration from file.
|
Configuration from file.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
try:
|
if isinstance(fname, (str, Path)):
|
||||||
fhandle = open(fname)
|
fhandle = open(fname)
|
||||||
except TypeError:
|
else:
|
||||||
fhandle = fname
|
fhandle = fname
|
||||||
return cls(yaml.safe_load(fhandle))
|
return cls(yaml.safe_load(fhandle))
|
||||||
|
|
||||||
|
def save(self,
|
||||||
def save(self,fname,**kwargs):
|
fname: FileHandle,
|
||||||
|
**kwargs):
|
||||||
"""
|
"""
|
||||||
Save to yaml file.
|
Save to yaml file.
|
||||||
|
|
||||||
|
@ -143,9 +164,9 @@ class Config(dict):
|
||||||
Keyword arguments parsed to yaml.dump.
|
Keyword arguments parsed to yaml.dump.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
try:
|
if isinstance(fname, (str, Path)):
|
||||||
fhandle = open(fname,'w',newline='\n')
|
fhandle = open(fname,'w',newline='\n')
|
||||||
except TypeError:
|
else:
|
||||||
fhandle = fname
|
fhandle = fname
|
||||||
|
|
||||||
if 'width' not in kwargs:
|
if 'width' not in kwargs:
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import h5py
|
import h5py
|
||||||
|
from typing import Sequence, Dict, Any, Collection
|
||||||
|
|
||||||
|
from ._typehints import FileHandle
|
||||||
from . import Config
|
from . import Config
|
||||||
from . import Rotation
|
from . import Rotation
|
||||||
from . import Orientation
|
from . import Orientation
|
||||||
from . import util
|
from . import util
|
||||||
|
from . import Table
|
||||||
|
|
||||||
|
|
||||||
class ConfigMaterial(Config):
|
class ConfigMaterial(Config):
|
||||||
"""
|
"""
|
||||||
|
@ -17,7 +21,9 @@ class ConfigMaterial(Config):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self,d=None,**kwargs):
|
def __init__(self,
|
||||||
|
d: Dict[str, Any] = None,
|
||||||
|
**kwargs):
|
||||||
"""
|
"""
|
||||||
New material configuration.
|
New material configuration.
|
||||||
|
|
||||||
|
@ -30,14 +36,17 @@ class ConfigMaterial(Config):
|
||||||
Initial content specified as pairs of key=value.
|
Initial content specified as pairs of key=value.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
default: Collection
|
||||||
if d is None:
|
if d 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__(d,**kwargs)
|
||||||
|
|
||||||
|
|
||||||
def save(self,fname='material.yaml',**kwargs):
|
def save(self,
|
||||||
|
fname: FileHandle = 'material.yaml',
|
||||||
|
**kwargs):
|
||||||
"""
|
"""
|
||||||
Save to yaml file.
|
Save to yaml file.
|
||||||
|
|
||||||
|
@ -53,7 +62,8 @@ class ConfigMaterial(Config):
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls,fname='material.yaml'):
|
def load(cls,
|
||||||
|
fname: FileHandle = 'material.yaml') -> 'ConfigMaterial':
|
||||||
"""
|
"""
|
||||||
Load from yaml file.
|
Load from yaml file.
|
||||||
|
|
||||||
|
@ -72,10 +82,14 @@ class ConfigMaterial(Config):
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load_DREAM3D(fname,
|
def load_DREAM3D(fname: str,
|
||||||
grain_data=None,cell_data=None,cell_ensemble_data='CellEnsembleData',
|
grain_data: str = None,
|
||||||
phases='Phases',Euler_angles='EulerAngles',phase_names='PhaseName',
|
cell_data: str = None,
|
||||||
base_group=None):
|
cell_ensemble_data: str = 'CellEnsembleData',
|
||||||
|
phases: str = 'Phases',
|
||||||
|
Euler_angles: str = 'EulerAngles',
|
||||||
|
phase_names: str = 'PhaseName',
|
||||||
|
base_group: str = None) -> 'ConfigMaterial':
|
||||||
"""
|
"""
|
||||||
Load DREAM.3D (HDF5) file.
|
Load DREAM.3D (HDF5) file.
|
||||||
|
|
||||||
|
@ -154,7 +168,8 @@ class ConfigMaterial(Config):
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_table(table,**kwargs):
|
def from_table(table: Table,
|
||||||
|
**kwargs) -> 'ConfigMaterial':
|
||||||
"""
|
"""
|
||||||
Generate from an ASCII table.
|
Generate from an ASCII table.
|
||||||
|
|
||||||
|
@ -207,7 +222,7 @@ class ConfigMaterial(Config):
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_complete(self):
|
def is_complete(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Check for completeness.
|
Check for completeness.
|
||||||
|
|
||||||
|
@ -267,12 +282,11 @@ class ConfigMaterial(Config):
|
||||||
if homogenization - set(self['homogenization']):
|
if homogenization - set(self['homogenization']):
|
||||||
print(f'Homogenization(s) {homogenization-set(self["homogenization"])} missing')
|
print(f'Homogenization(s) {homogenization-set(self["homogenization"])} missing')
|
||||||
ok = False
|
ok = False
|
||||||
|
|
||||||
return ok
|
return ok
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_valid(self):
|
def is_valid(self) -> bool:
|
||||||
"""
|
"""
|
||||||
Check for valid content.
|
Check for valid content.
|
||||||
|
|
||||||
|
@ -316,7 +330,10 @@ class ConfigMaterial(Config):
|
||||||
return ok
|
return ok
|
||||||
|
|
||||||
|
|
||||||
def material_rename_phase(self,mapping,ID=None,constituent=None):
|
def material_rename_phase(self,
|
||||||
|
mapping: Dict[str, str],
|
||||||
|
ID: Sequence[int] = None,
|
||||||
|
constituent: Sequence[int] = None) -> 'ConfigMaterial':
|
||||||
"""
|
"""
|
||||||
Change phase name in material.
|
Change phase name in material.
|
||||||
|
|
||||||
|
@ -347,7 +364,9 @@ class ConfigMaterial(Config):
|
||||||
return dup
|
return dup
|
||||||
|
|
||||||
|
|
||||||
def material_rename_homogenization(self,mapping,ID=None):
|
def material_rename_homogenization(self,
|
||||||
|
mapping: Dict[str, str],
|
||||||
|
ID: Sequence[int] = None) -> 'ConfigMaterial':
|
||||||
"""
|
"""
|
||||||
Change homogenization name in material.
|
Change homogenization name in material.
|
||||||
|
|
||||||
|
@ -374,7 +393,8 @@ class ConfigMaterial(Config):
|
||||||
return dup
|
return dup
|
||||||
|
|
||||||
|
|
||||||
def material_add(self,**kwargs):
|
def material_add(self,
|
||||||
|
**kwargs: Any) -> 'ConfigMaterial':
|
||||||
"""
|
"""
|
||||||
Add material entries.
|
Add material entries.
|
||||||
|
|
||||||
|
@ -453,7 +473,7 @@ class ConfigMaterial(Config):
|
||||||
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
|
||||||
|
|
||||||
mat = [{'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:
|
if 'v' not in kwargs:
|
||||||
shaped['v'] = np.broadcast_to(1/n,(N,n))
|
shaped['v'] = np.broadcast_to(1/n,(N,n))
|
||||||
|
@ -461,7 +481,7 @@ class ConfigMaterial(Config):
|
||||||
map_shape = {'O':(N,n,4),'F_i':(N,n,3,3)}
|
map_shape = {'O':(N,n,4),'F_i':(N,n,3,3)}
|
||||||
for k,v in shaped.items():
|
for k,v in shaped.items():
|
||||||
target = map_shape.get(k,(N,n))
|
target = map_shape.get(k,(N,n))
|
||||||
obj = np.broadcast_to(v.reshape(util.shapeshifter(v.shape,target,mode='right')),target)
|
obj = np.broadcast_to(v.reshape(util.shapeshifter(v.shape, target, mode = 'right')), target)
|
||||||
for i in range(N):
|
for i in range(N):
|
||||||
if k in ['phase','O','v','F_i']:
|
if k in ['phase','O','v','F_i']:
|
||||||
for j in range(n):
|
for j in range(n):
|
||||||
|
|
|
@ -9,7 +9,7 @@ import re
|
||||||
import fractions
|
import fractions
|
||||||
from collections import abc
|
from collections import abc
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from typing import Union, Tuple, Iterable, Callable, Dict, List, Any, Literal, Optional
|
from typing import Union, Tuple, Iterable, Callable, Dict, List, Any, Literal, SupportsIndex, Sequence
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
@ -427,7 +427,7 @@ def hybrid_IA(dist: np.ndarray,
|
||||||
def shapeshifter(fro: Tuple[int, ...],
|
def shapeshifter(fro: Tuple[int, ...],
|
||||||
to: Tuple[int, ...],
|
to: Tuple[int, ...],
|
||||||
mode: Literal['left','right'] = 'left',
|
mode: Literal['left','right'] = 'left',
|
||||||
keep_ones: bool = False) -> Tuple[Optional[int], ...]:
|
keep_ones: bool = False) -> Sequence[SupportsIndex]:
|
||||||
"""
|
"""
|
||||||
Return dimensions that reshape 'fro' to become broadcastable to 'to'.
|
Return dimensions that reshape 'fro' to become broadcastable to 'to'.
|
||||||
|
|
||||||
|
@ -483,14 +483,14 @@ def shapeshifter(fro: Tuple[int, ...],
|
||||||
grp = match.groups()
|
grp = match.groups()
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
raise ValueError(f'Shapes can not be shifted {fro} --> {to}')
|
raise ValueError(f'Shapes can not be shifted {fro} --> {to}')
|
||||||
fill: Tuple[Optional[int], ...] = ()
|
fill: Any = ()
|
||||||
for g,d in zip(grp,fro+(None,)):
|
for g,d in zip(grp,fro+(None,)):
|
||||||
fill += (1,)*g.count(',')+(d,)
|
fill += (1,)*g.count(',')+(d,)
|
||||||
return fill[:-1]
|
return fill[:-1]
|
||||||
|
|
||||||
|
|
||||||
def shapeblender(a: Tuple[int, ...],
|
def shapeblender(a: Tuple[int, ...],
|
||||||
b: Tuple[int, ...]) -> Tuple[int, ...]:
|
b: Tuple[int, ...]) -> Sequence[SupportsIndex]:
|
||||||
"""
|
"""
|
||||||
Return a shape that overlaps the rightmost entries of 'a' with the leftmost of 'b'.
|
Return a shape that overlaps the rightmost entries of 'a' with the leftmost of 'b'.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue