diff --git a/python/damask/__init__.py b/python/damask/__init__.py index aa3c36b07..fd49a00e4 100644 --- a/python/damask/__init__.py +++ b/python/damask/__init__.py @@ -25,5 +25,6 @@ from ._colormap import Colormap # noqa from ._vtk import VTK # noqa from ._config import Config # noqa from ._configmaterial import ConfigMaterial # noqa +from ._loadcasegrid import LoadcaseGrid # noqa from ._grid import Grid # noqa from ._result import Result # noqa diff --git a/python/damask/_loadcasegrid.py b/python/damask/_loadcasegrid.py new file mode 100644 index 000000000..496824713 --- /dev/null +++ b/python/damask/_loadcasegrid.py @@ -0,0 +1,78 @@ +from typing import Optional, Union, Dict, Any, List + +from numpy import ma +import yaml + +from ._typehints import FileHandle +from ._config import NiceDumper +from . import util +from . import Config + + +class MaskedMatrixDumper(NiceDumper): + """Format masked matrices.""" + + def represent_data(self, data: Any): + return super().represent_data(data.astype(object).filled('x') if isinstance(data, ma.core.MaskedArray) else data) # type: ignore[attr-defined] + + +class LoadcaseGrid(Config): + """Load case for grid solver.""" + + def __init__(self, + config: Optional[Union[str,Dict[str,Any]]] = None, + *, + solver: Optional[Dict[str,str]] = None, + loadstep: Optional[List[Dict[str,Any]]] = None): + """ + New grid solver load case. + + Parameters + ---------- + config : dict or str, optional + Grid solver load case. String needs to be valid YAML. + solver : dict, optional + Solver configuration. + Defaults to an empty dict if 'config' is not given. + loadstep : list of dict, optional + Load step configuration. + Defaults to an empty list if 'config' is not given. + + """ + kwargs: Dict[str,Union[Dict[str,str],List[Dict[str,Any]]]] = {} + default: Union[List,Dict] + for arg,value,default in [('solver',solver,{}),('loadstep',loadstep,[])]: # type: ignore[assignment] + if value is not None: + kwargs[arg] = value + elif config is None: + kwargs[arg] = default + + super().__init__(config,**kwargs) + + + def save(self, + fname: FileHandle, + **kwargs): + """ + Save to YAML file. + + Parameters + ---------- + fname : file, str, or pathlib.Path + Filename or file to write. + **kwargs : dict + Keyword arguments parsed to yaml.dump. + + """ + for key,default in dict(width=256, + default_flow_style=None, + sort_keys=False).items(): + if key not in kwargs: + kwargs[key] = default + + fhandle = util.open_text(fname,'w') + try: + fhandle.write(yaml.dump(self,Dumper=MaskedMatrixDumper,**kwargs)) + except TypeError: # compatibility with old pyyaml + del kwargs['sort_keys'] + fhandle.write(yaml.dump(self,Dumper=MaskedMatrixDumper,**kwargs)) diff --git a/python/damask/_result.py b/python/damask/_result.py index f6dddc9a0..dce267a82 100644 --- a/python/damask/_result.py +++ b/python/damask/_result.py @@ -15,7 +15,7 @@ from typing import Optional, Union, Callable, Any, Sequence, Literal, Dict, List import h5py import numpy as np -import numpy.ma as ma +from numpy import ma import damask from . import VTK diff --git a/python/tests/test_LoadcaseGrid.py b/python/tests/test_LoadcaseGrid.py new file mode 100644 index 000000000..54649d0c5 --- /dev/null +++ b/python/tests/test_LoadcaseGrid.py @@ -0,0 +1,22 @@ +import numpy as np +from numpy import ma + +from damask import LoadcaseGrid + +class TestGridConfig: + + def test_dumper(self): + a = ma.MaskedArray(np.arange(3,dtype=float),mask=[0,1,0]) + assert str(LoadcaseGrid({'a':a})) == 'a: [0.0, x, 2.0]\n' + assert str(LoadcaseGrid({'a':a.astype(int)})) == 'a: [0, x, 2]\n' + assert str(LoadcaseGrid({'a':a.data})) == 'a: [0.0, 1.0, 2.0]\n' + + def test_init(self): + assert LoadcaseGrid() \ + == LoadcaseGrid({'solver':{}, + 'loadstep':[], + }) + assert LoadcaseGrid(solver={'mechanical':'spectral_basic'}) \ + == LoadcaseGrid({'solver':{'mechanical':'spectral_basic'}, + 'loadstep':[], + })