2020-06-26 15:15:06 +05:30
|
|
|
|
import multiprocessing as mp
|
2019-04-13 14:41:32 +05:30
|
|
|
|
import re
|
2021-04-04 23:14:06 +05:30
|
|
|
|
import fnmatch
|
2019-12-13 16:45:45 +05:30
|
|
|
|
import os
|
2021-04-05 11:23:19 +05:30
|
|
|
|
import copy
|
2020-05-25 22:20:31 +05:30
|
|
|
|
import datetime
|
2021-12-17 14:17:58 +05:30
|
|
|
|
import warnings
|
2022-02-02 04:32:03 +05:30
|
|
|
|
import xml.etree.ElementTree as ET # noqa
|
2020-05-05 13:27:22 +05:30
|
|
|
|
import xml.dom.minidom
|
2020-06-03 14:13:07 +05:30
|
|
|
|
from pathlib import Path
|
2020-02-21 23:54:26 +05:30
|
|
|
|
from functools import partial
|
2020-11-28 02:15:23 +05:30
|
|
|
|
from collections import defaultdict
|
2021-03-31 18:49:23 +05:30
|
|
|
|
from collections.abc import Iterable
|
2019-09-12 06:33:19 +05:30
|
|
|
|
|
|
|
|
|
import h5py
|
2019-04-17 23:27:16 +05:30
|
|
|
|
import numpy as np
|
2021-03-30 10:54:47 +05:30
|
|
|
|
import numpy.ma as ma
|
2019-09-12 06:33:19 +05:30
|
|
|
|
|
2020-06-28 15:10:19 +05:30
|
|
|
|
import damask
|
2020-03-11 11:20:13 +05:30
|
|
|
|
from . import VTK
|
2020-02-16 00:39:24 +05:30
|
|
|
|
from . import Orientation
|
2020-02-21 22:17:47 +05:30
|
|
|
|
from . import grid_filters
|
2020-03-13 00:22:33 +05:30
|
|
|
|
from . import mechanics
|
2020-11-16 03:44:46 +05:30
|
|
|
|
from . import tensor
|
2020-03-13 00:22:33 +05:30
|
|
|
|
from . import util
|
|
|
|
|
|
2020-11-05 20:43:29 +05:30
|
|
|
|
h5py3 = h5py.__version__[0] == '3'
|
2019-04-13 14:41:32 +05:30
|
|
|
|
|
2021-05-28 16:50:56 +05:30
|
|
|
|
chunk_size = 1024**2//8 # for compression in HDF5
|
|
|
|
|
|
2021-12-17 14:17:58 +05:30
|
|
|
|
def _view_transition(what,datasets,increments,times,phases,homogenizations,fields):
|
|
|
|
|
if (datasets is not None and what is None) or (what is not None and datasets is None):
|
|
|
|
|
raise ValueError('"what" and "datasets" need to be used as a pair')
|
|
|
|
|
if datasets is not None or what is not None:
|
|
|
|
|
warnings.warn('Arguments "what" and "datasets" will be removed in DAMASK v3.0.0-alpha7', DeprecationWarning,2)
|
|
|
|
|
return what,datasets
|
|
|
|
|
if sum(1 for _ in filter(None.__ne__, [increments,times,phases,homogenizations,fields])) > 1:
|
|
|
|
|
raise ValueError('Only one out of "increments", "times", "phases", "homogenizations", and "fields" can be used')
|
|
|
|
|
else:
|
|
|
|
|
if increments is not None: return "increments", increments
|
|
|
|
|
if times is not None: return "times", times
|
|
|
|
|
if phases is not None: return "phases", phases
|
|
|
|
|
if homogenizations is not None: return "homogenizations", homogenizations
|
|
|
|
|
if fields is not None: return "fields", fields
|
2021-04-03 14:38:22 +05:30
|
|
|
|
|
2021-04-02 12:26:14 +05:30
|
|
|
|
def _read(dataset):
|
2021-04-05 11:23:19 +05:30
|
|
|
|
"""Read a dataset and its metadata into a numpy.ndarray."""
|
2021-04-05 13:59:34 +05:30
|
|
|
|
metadata = {k:(v.decode() if not h5py3 and type(v) is bytes else v) for k,v in dataset.attrs.items()}
|
2021-04-02 12:26:14 +05:30
|
|
|
|
dtype = np.dtype(dataset.dtype,metadata=metadata)
|
|
|
|
|
return np.array(dataset,dtype=dtype)
|
2021-03-31 15:35:51 +05:30
|
|
|
|
|
2021-04-03 14:38:22 +05:30
|
|
|
|
def _match(requested,existing):
|
2021-04-05 13:43:08 +05:30
|
|
|
|
"""Find matches among two sets of labels."""
|
2021-04-03 14:38:22 +05:30
|
|
|
|
def flatten_list(list_of_lists):
|
|
|
|
|
return [e for e_ in list_of_lists for e in e_]
|
|
|
|
|
|
|
|
|
|
if requested is True:
|
|
|
|
|
requested = '*'
|
|
|
|
|
elif requested is False or requested is None:
|
|
|
|
|
requested = []
|
|
|
|
|
|
|
|
|
|
requested_ = requested if hasattr(requested,'__iter__') and not isinstance(requested,str) else \
|
|
|
|
|
[requested]
|
|
|
|
|
|
2021-04-04 23:14:06 +05:30
|
|
|
|
return sorted(set(flatten_list([fnmatch.filter(existing,r) for r in requested_])),
|
2021-04-03 14:38:22 +05:30
|
|
|
|
key=util.natural_sort)
|
|
|
|
|
|
2021-04-06 21:09:44 +05:30
|
|
|
|
def _empty_like(dataset,N_materialpoints,fill_float,fill_int):
|
2021-04-05 11:23:19 +05:30
|
|
|
|
"""Create empty numpy.ma.MaskedArray."""
|
2021-04-04 16:55:16 +05:30
|
|
|
|
return ma.array(np.empty((N_materialpoints,)+dataset.shape[1:],dataset.dtype),
|
|
|
|
|
fill_value = fill_float if dataset.dtype in np.sctypes['float'] else fill_int,
|
|
|
|
|
mask = True)
|
2021-03-31 15:35:51 +05:30
|
|
|
|
|
2020-03-09 18:09:20 +05:30
|
|
|
|
class Result:
|
2019-09-20 01:02:15 +05:30
|
|
|
|
"""
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Add data to and export data from a DADF5 file.
|
2019-09-16 08:49:14 +05:30
|
|
|
|
|
2021-04-24 22:42:44 +05:30
|
|
|
|
A DADF5 (DAMASK HDF5) file contains DAMASK results.
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Its group/folder structure reflects the layout in material.yaml.
|
2021-04-23 22:50:07 +05:30
|
|
|
|
|
2021-04-24 22:42:44 +05:30
|
|
|
|
This class provides a customizable view on the DADF5 file.
|
2021-04-23 22:50:07 +05:30
|
|
|
|
Upon initialization, all attributes are visible.
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Derived quantities are added to the file and existing data is
|
|
|
|
|
exported based on the current view.
|
|
|
|
|
|
|
|
|
|
Examples
|
|
|
|
|
--------
|
2021-04-24 22:42:44 +05:30
|
|
|
|
Open 'my_file.hdf5', which is assumed to contain deformation gradient 'F'
|
2021-04-24 10:43:36 +05:30
|
|
|
|
and first Piola-Kirchhoff stress 'P', add the Mises equivalent of the
|
|
|
|
|
Cauchy stress, and export it to VTK (file) and numpy.ndarray (memory).
|
|
|
|
|
|
|
|
|
|
>>> import damask
|
|
|
|
|
>>> r = damask.Result('my_file.hdf5')
|
|
|
|
|
>>> r.add_Cauchy()
|
|
|
|
|
>>> r.add_equivalent_Mises('sigma')
|
2021-06-17 21:56:37 +05:30
|
|
|
|
>>> r.export_VTK()
|
2021-12-17 14:17:58 +05:30
|
|
|
|
>>> r_last = r.view(increments=-1)
|
2021-04-24 10:43:36 +05:30
|
|
|
|
>>> sigma_vM_last = r_last.get('sigma_vM')
|
2019-09-16 08:49:14 +05:30
|
|
|
|
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self,fname):
|
|
|
|
|
"""
|
2021-03-27 14:40:35 +05:30
|
|
|
|
New result view bound to a HDF5 file.
|
2019-09-16 08:49:14 +05:30
|
|
|
|
|
2020-02-21 12:15:05 +05:30
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2020-11-19 18:15:40 +05:30
|
|
|
|
fname : str or pathlib.Path
|
|
|
|
|
Name of the DADF5 file to be opened.
|
2019-09-20 01:02:15 +05:30
|
|
|
|
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
|
|
|
|
with h5py.File(fname,'r') as f:
|
|
|
|
|
|
2020-11-18 19:22:16 +05:30
|
|
|
|
self.version_major = f.attrs['DADF5_version_major']
|
|
|
|
|
self.version_minor = f.attrs['DADF5_version_minor']
|
|
|
|
|
|
2021-07-27 13:01:57 +05:30
|
|
|
|
if self.version_major != 0 or not 12 <= self.version_minor <= 14:
|
2020-06-25 01:04:51 +05:30
|
|
|
|
raise TypeError(f'Unsupported DADF5 version {self.version_major}.{self.version_minor}')
|
2021-07-27 13:01:57 +05:30
|
|
|
|
if self.version_major == 0 and self.version_minor < 14:
|
|
|
|
|
self.export_setup = None
|
2020-03-22 20:43:35 +05:30
|
|
|
|
|
2021-04-14 10:36:24 +05:30
|
|
|
|
self.structured = 'cells' in f['geometry'].attrs.keys()
|
2020-03-22 20:43:35 +05:30
|
|
|
|
|
|
|
|
|
if self.structured:
|
2021-04-14 10:36:24 +05:30
|
|
|
|
self.cells = f['geometry'].attrs['cells']
|
2020-03-22 20:43:35 +05:30
|
|
|
|
self.size = f['geometry'].attrs['size']
|
2020-11-18 19:22:16 +05:30
|
|
|
|
self.origin = f['geometry'].attrs['origin']
|
2021-05-29 00:27:44 +05:30
|
|
|
|
else:
|
|
|
|
|
self.add_curl = self.add_divergence = self.add_gradient = None
|
2021-05-28 16:55:28 +05:30
|
|
|
|
|
2021-04-14 10:36:24 +05:30
|
|
|
|
r=re.compile('increment_[0-9]+')
|
2021-04-03 14:38:22 +05:30
|
|
|
|
self.increments = sorted([i for i in f.keys() if r.match(i)],key=util.natural_sort)
|
2021-04-14 10:36:24 +05:30
|
|
|
|
self.times = [round(f[i].attrs['t/s'],12) for i in self.increments]
|
2021-06-01 10:19:14 +05:30
|
|
|
|
if len(self.increments) == 0:
|
|
|
|
|
raise ValueError('incomplete DADF5 file')
|
2020-05-07 22:42:05 +05:30
|
|
|
|
|
2021-04-26 23:56:16 +05:30
|
|
|
|
self.N_materialpoints, self.N_constituents = np.shape(f['cell_to/phase'])
|
2021-03-25 23:52:59 +05:30
|
|
|
|
|
2021-04-26 23:56:16 +05:30
|
|
|
|
self.homogenization = f['cell_to/homogenization']['label'].astype('str')
|
2021-04-24 23:09:28 +05:30
|
|
|
|
self.homogenizations = sorted(np.unique(self.homogenization),key=util.natural_sort)
|
2021-04-26 23:56:16 +05:30
|
|
|
|
self.phase = f['cell_to/phase']['label'].astype('str')
|
2021-04-24 23:09:28 +05:30
|
|
|
|
self.phases = sorted(np.unique(self.phase),key=util.natural_sort)
|
2020-03-22 20:43:35 +05:30
|
|
|
|
|
2021-04-02 11:54:49 +05:30
|
|
|
|
self.fields = []
|
2020-11-18 19:22:16 +05:30
|
|
|
|
for c in self.phases:
|
2021-04-02 11:54:49 +05:30
|
|
|
|
self.fields += f['/'.join([self.increments[0],'phase',c])].keys()
|
2020-11-18 19:22:16 +05:30
|
|
|
|
for m in self.homogenizations:
|
2021-04-02 11:54:49 +05:30
|
|
|
|
self.fields += f['/'.join([self.increments[0],'homogenization',m])].keys()
|
2021-04-03 14:38:22 +05:30
|
|
|
|
self.fields = sorted(set(self.fields),key=util.natural_sort) # make unique
|
2019-10-20 14:30:10 +05:30
|
|
|
|
|
2021-01-13 19:27:58 +05:30
|
|
|
|
self.visible = {'increments': self.increments,
|
|
|
|
|
'phases': self.phases,
|
|
|
|
|
'homogenizations': self.homogenizations,
|
2021-04-02 11:54:49 +05:30
|
|
|
|
'fields': self.fields,
|
2021-01-13 19:27:58 +05:30
|
|
|
|
}
|
2019-10-20 14:30:10 +05:30
|
|
|
|
|
2020-06-03 14:13:07 +05:30
|
|
|
|
self.fname = Path(fname).absolute()
|
2019-10-20 14:30:10 +05:30
|
|
|
|
|
2021-12-17 15:07:45 +05:30
|
|
|
|
self._protected = True
|
2020-05-25 22:42:31 +05:30
|
|
|
|
|
2019-10-20 14:30:10 +05:30
|
|
|
|
|
2021-04-05 11:23:19 +05:30
|
|
|
|
def __copy__(self):
|
|
|
|
|
"""Create deep copy."""
|
|
|
|
|
return copy.deepcopy(self)
|
|
|
|
|
|
|
|
|
|
copy = __copy__
|
|
|
|
|
|
|
|
|
|
|
2020-03-11 10:58:13 +05:30
|
|
|
|
def __repr__(self):
|
2020-11-05 11:45:59 +05:30
|
|
|
|
"""Show summary of file content."""
|
2022-02-17 11:46:55 +05:30
|
|
|
|
with h5py.File(self.fname,'r') as f:
|
|
|
|
|
header = [f'Created by {f.attrs["creator"]}',
|
2022-02-17 22:35:13 +05:30
|
|
|
|
f' on {f.attrs["created"]}',
|
|
|
|
|
f' executing "{f.attrs["call"]}"']
|
2021-01-13 19:27:58 +05:30
|
|
|
|
visible_increments = self.visible['increments']
|
2020-04-21 14:47:15 +05:30
|
|
|
|
|
2021-12-17 14:17:58 +05:30
|
|
|
|
first = self.view(increments=visible_increments[0:1]).list_data()
|
2020-04-21 14:47:15 +05:30
|
|
|
|
|
2022-02-17 22:35:13 +05:30
|
|
|
|
last = [] if len(visible_increments) < 2 else \
|
2021-12-17 14:17:58 +05:30
|
|
|
|
self.view(increments=visible_increments[-1:]).list_data()
|
2020-04-21 14:47:15 +05:30
|
|
|
|
|
2022-02-17 22:35:13 +05:30
|
|
|
|
in_between = [] if len(visible_increments) < 3 else \
|
|
|
|
|
[f'\n{inc}\n ...' for inc in visible_increments[1:-1]]
|
2020-04-21 14:47:15 +05:30
|
|
|
|
|
2022-02-17 22:35:13 +05:30
|
|
|
|
return util.srepr([util.deemph(header)] + first + in_between + last)
|
2020-03-11 10:58:13 +05:30
|
|
|
|
|
|
|
|
|
|
2021-01-13 19:27:58 +05:30
|
|
|
|
def _manage_view(self,action,what,datasets):
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
|
|
|
|
Manages the visibility of the groups.
|
2019-10-20 14:30:10 +05:30
|
|
|
|
|
2020-02-21 12:15:05 +05:30
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2020-03-03 04:17:29 +05:30
|
|
|
|
action : str
|
2020-11-23 23:52:48 +05:30
|
|
|
|
Select from 'set', 'add', and 'del'.
|
2020-03-03 04:17:29 +05:30
|
|
|
|
what : str
|
2021-02-26 07:50:28 +05:30
|
|
|
|
Attribute to change (must be from self.visible).
|
2021-04-06 21:09:44 +05:30
|
|
|
|
datasets : (list of) int (for increments), (list of) float (for times), (list of) str, or bool
|
|
|
|
|
Name of datasets; supports '?' and '*' wildcards.
|
|
|
|
|
True is equivalent to '*', False is equivalent to [].
|
2020-03-03 04:17:29 +05:30
|
|
|
|
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
view : damask.Result
|
|
|
|
|
Modified or new view on the DADF5 file.
|
|
|
|
|
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
|
|
|
|
# allow True/False and string arguments
|
2020-05-21 14:15:52 +05:30
|
|
|
|
if datasets is True:
|
2021-04-03 14:38:22 +05:30
|
|
|
|
datasets = '*'
|
|
|
|
|
elif datasets is False or datasets is None:
|
2020-03-03 04:17:29 +05:30
|
|
|
|
datasets = []
|
2021-03-30 23:27:49 +05:30
|
|
|
|
choice = list(datasets).copy() if hasattr(datasets,'__iter__') and not isinstance(datasets,str) else \
|
2020-03-03 18:37:02 +05:30
|
|
|
|
[datasets]
|
|
|
|
|
|
2021-05-28 11:36:58 +05:30
|
|
|
|
what_ = what if what.endswith('s') else what+'s'
|
|
|
|
|
|
|
|
|
|
if what_ == 'increments':
|
2021-04-14 10:36:24 +05:30
|
|
|
|
choice = [c if isinstance(c,str) and c.startswith('increment_') else
|
2021-04-27 03:26:15 +05:30
|
|
|
|
self.increments[c] if isinstance(c,int) and c<0 else
|
|
|
|
|
f'increment_{c}' for c in choice]
|
2021-05-28 11:36:58 +05:30
|
|
|
|
elif what_ == 'times':
|
|
|
|
|
what_ = 'increments'
|
2020-03-03 18:37:02 +05:30
|
|
|
|
if choice == ['*']:
|
|
|
|
|
choice = self.increments
|
|
|
|
|
else:
|
|
|
|
|
iterator = map(float,choice)
|
|
|
|
|
choice = []
|
|
|
|
|
for c in iterator:
|
2020-03-22 21:33:28 +05:30
|
|
|
|
idx = np.searchsorted(self.times,c)
|
2020-05-26 03:24:06 +05:30
|
|
|
|
if idx >= len(self.times): continue
|
2020-03-03 18:37:02 +05:30
|
|
|
|
if np.isclose(c,self.times[idx]):
|
|
|
|
|
choice.append(self.increments[idx])
|
|
|
|
|
elif np.isclose(c,self.times[idx+1]):
|
|
|
|
|
choice.append(self.increments[idx+1])
|
2019-10-20 14:30:10 +05:30
|
|
|
|
|
2021-05-28 11:36:58 +05:30
|
|
|
|
valid = _match(choice,getattr(self,what_))
|
|
|
|
|
existing = set(self.visible[what_])
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
2021-04-05 11:23:19 +05:30
|
|
|
|
dup = self.copy()
|
2020-02-21 12:15:05 +05:30
|
|
|
|
if action == 'set':
|
2021-05-28 11:36:58 +05:30
|
|
|
|
dup.visible[what_] = sorted(set(valid), key=util.natural_sort)
|
2020-02-21 12:15:05 +05:30
|
|
|
|
elif action == 'add':
|
2020-03-22 21:33:28 +05:30
|
|
|
|
add = existing.union(valid)
|
2021-05-28 11:36:58 +05:30
|
|
|
|
dup.visible[what_] = sorted(add, key=util.natural_sort)
|
2020-02-21 12:15:05 +05:30
|
|
|
|
elif action == 'del':
|
2020-03-22 21:33:28 +05:30
|
|
|
|
diff = existing.difference(valid)
|
2021-05-28 11:36:58 +05:30
|
|
|
|
dup.visible[what_] = sorted(diff, key=util.natural_sort)
|
2021-04-05 11:23:19 +05:30
|
|
|
|
|
|
|
|
|
return dup
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
2020-03-03 17:13:14 +05:30
|
|
|
|
|
2021-03-25 23:52:59 +05:30
|
|
|
|
def increments_in_range(self,start,end):
|
2020-11-19 18:15:40 +05:30
|
|
|
|
"""
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Get all increments within a given range.
|
2020-11-19 18:15:40 +05:30
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
start : int or str
|
|
|
|
|
Start increment.
|
|
|
|
|
end : int or str
|
|
|
|
|
End increment.
|
|
|
|
|
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
increments : list of ints
|
|
|
|
|
Increment number of all increments within the given bounds.
|
2021-04-24 18:17:52 +05:30
|
|
|
|
|
2020-11-19 18:15:40 +05:30
|
|
|
|
"""
|
2020-03-03 17:13:14 +05:30
|
|
|
|
selected = []
|
2021-04-14 10:36:24 +05:30
|
|
|
|
for i,inc in enumerate([int(i[10:]) for i in self.increments]):
|
|
|
|
|
s,e = map(lambda x: int(x[10:] if isinstance(x,str) and x.startswith('inc') else x), (start,end))
|
2020-03-03 17:13:14 +05:30
|
|
|
|
if s <= inc <= e:
|
|
|
|
|
selected.append(self.increments[i])
|
|
|
|
|
return selected
|
|
|
|
|
|
|
|
|
|
def times_in_range(self,start,end):
|
2020-11-19 18:15:40 +05:30
|
|
|
|
"""
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Get all increments within a given time range.
|
2020-11-19 18:15:40 +05:30
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
start : float
|
|
|
|
|
Time of start increment.
|
|
|
|
|
end : float
|
|
|
|
|
Time of end increment.
|
|
|
|
|
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
times : list of float
|
|
|
|
|
Simulation time of all increments within the given bounds.
|
2021-04-24 18:17:52 +05:30
|
|
|
|
|
2020-11-19 18:15:40 +05:30
|
|
|
|
"""
|
2020-03-03 17:13:14 +05:30
|
|
|
|
selected = []
|
|
|
|
|
for i,time in enumerate(self.times):
|
|
|
|
|
if start <= time <= end:
|
2020-03-10 03:58:25 +05:30
|
|
|
|
selected.append(self.times[i])
|
2020-03-03 17:13:14 +05:30
|
|
|
|
return selected
|
|
|
|
|
|
|
|
|
|
|
2021-12-17 14:17:58 +05:30
|
|
|
|
def view(self,what=None,datasets=None,*,
|
|
|
|
|
increments=None,
|
|
|
|
|
times=None,
|
|
|
|
|
phases=None,
|
|
|
|
|
homogenizations=None,
|
2021-12-17 15:01:41 +05:30
|
|
|
|
fields=None,
|
|
|
|
|
protected=None):
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
2021-01-13 19:27:58 +05:30
|
|
|
|
Set view.
|
2020-02-15 19:43:56 +05:30
|
|
|
|
|
2021-12-17 14:17:58 +05:30
|
|
|
|
Wildcard matching with '?' and '*' is supported.
|
|
|
|
|
True is equivalent to '*', False is equivalent to [].
|
|
|
|
|
|
2020-02-21 12:15:05 +05:30
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2021-04-05 13:59:34 +05:30
|
|
|
|
what : {'increments', 'times', 'phases', 'homogenizations', 'fields'}
|
2021-12-17 14:17:58 +05:30
|
|
|
|
Attribute to change. DEPRECATED.
|
2021-04-06 21:09:44 +05:30
|
|
|
|
datasets : (list of) int (for increments), (list of) float (for times), (list of) str, or bool
|
2021-12-17 14:17:58 +05:30
|
|
|
|
Name of datasets; supports '?' and '*' wildcards. DEPRECATED.
|
2021-04-06 21:09:44 +05:30
|
|
|
|
True is equivalent to '*', False is equivalent to [].
|
2021-12-17 14:17:58 +05:30
|
|
|
|
increments: (list of) int, (list of) str, or bool, optional.
|
|
|
|
|
Number(s) of increments to select.
|
|
|
|
|
times: (list of) float, (list of) str, or bool, optional.
|
|
|
|
|
Simulation time(s) of increments to select.
|
|
|
|
|
phases: (list of) str, or bool, optional.
|
|
|
|
|
Name(s) of phases to select.
|
|
|
|
|
homogenizations: (list of) str, or bool, optional.
|
|
|
|
|
Name(s) of homogenizations to select.
|
|
|
|
|
fields: (list of) str, or bool, optional.
|
|
|
|
|
Name(s) of fields to select.
|
2021-12-17 15:01:41 +05:30
|
|
|
|
protected: bool, optional.
|
|
|
|
|
Protection status of existing data.
|
2020-02-15 19:43:56 +05:30
|
|
|
|
|
2021-04-23 22:50:07 +05:30
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
view : damask.Result
|
2021-04-24 22:42:44 +05:30
|
|
|
|
View with only the selected attributes being visible.
|
2021-04-23 22:50:07 +05:30
|
|
|
|
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Examples
|
|
|
|
|
--------
|
|
|
|
|
Get a view that shows only results from the initial configuration:
|
|
|
|
|
|
|
|
|
|
>>> import damask
|
|
|
|
|
>>> r = damask.Result('my_file.hdf5')
|
2021-12-17 14:17:58 +05:30
|
|
|
|
>>> r_first = r.view(increment=0)
|
2021-04-24 10:43:36 +05:30
|
|
|
|
|
2021-04-24 22:42:44 +05:30
|
|
|
|
Get a view that shows all results between simulation times of 10 to 40:
|
2021-04-24 10:43:36 +05:30
|
|
|
|
|
|
|
|
|
>>> import damask
|
|
|
|
|
>>> r = damask.Result('my_file.hdf5')
|
2021-12-17 14:17:58 +05:30
|
|
|
|
>>> r_t10to40 = r.view(times=r.times_in_range(10.0,40.0))
|
2021-04-24 10:43:36 +05:30
|
|
|
|
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
2021-12-17 15:01:41 +05:30
|
|
|
|
v = _view_transition(what,datasets,increments,times,phases,homogenizations,fields)
|
|
|
|
|
if protected is not None:
|
2021-12-17 15:07:45 +05:30
|
|
|
|
if v is None:
|
2021-12-17 15:01:41 +05:30
|
|
|
|
dup = self.copy()
|
|
|
|
|
else:
|
|
|
|
|
what_,datasets_ = v
|
|
|
|
|
dup = self._manage_view('set',what_,datasets_)
|
2021-12-17 18:58:12 +05:30
|
|
|
|
if not protected:
|
2021-12-17 15:07:45 +05:30
|
|
|
|
print(util.warn('Warning: Modification of existing datasets allowed!'))
|
|
|
|
|
dup._protected = protected
|
2021-12-17 15:01:41 +05:30
|
|
|
|
else:
|
|
|
|
|
what_,datasets_ = v
|
|
|
|
|
dup = self._manage_view('set',what_,datasets_)
|
|
|
|
|
|
|
|
|
|
return dup
|
2020-02-15 22:26:20 +05:30
|
|
|
|
|
|
|
|
|
|
2021-12-17 14:17:58 +05:30
|
|
|
|
def view_more(self,what=None,datasets=None,*,
|
|
|
|
|
increments=None,
|
|
|
|
|
times=None,
|
|
|
|
|
phases=None,
|
|
|
|
|
homogenizations=None,
|
|
|
|
|
fields=None):
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
2021-01-13 19:27:58 +05:30
|
|
|
|
Add to view.
|
2020-02-15 22:26:20 +05:30
|
|
|
|
|
2021-12-17 14:17:58 +05:30
|
|
|
|
Wildcard matching with '?' and '*' is supported.
|
|
|
|
|
True is equivalent to '*', False is equivalent to [].
|
|
|
|
|
|
2020-02-21 12:15:05 +05:30
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2021-04-05 13:59:34 +05:30
|
|
|
|
what : {'increments', 'times', 'phases', 'homogenizations', 'fields'}
|
2021-12-17 14:17:58 +05:30
|
|
|
|
Attribute to change. DEPRECATED.
|
2021-04-06 21:09:44 +05:30
|
|
|
|
datasets : (list of) int (for increments), (list of) float (for times), (list of) str, or bool
|
2021-12-17 14:17:58 +05:30
|
|
|
|
Name of datasets; supports '?' and '*' wildcards. DEPRECATED.
|
2021-04-06 21:09:44 +05:30
|
|
|
|
True is equivalent to '*', False is equivalent to [].
|
2021-12-17 14:17:58 +05:30
|
|
|
|
increments: (list of) int, (list of) str, or bool, optional.
|
|
|
|
|
Number(s) of increments to select.
|
|
|
|
|
times: (list of) float, (list of) str, or bool, optional.
|
|
|
|
|
Simulation time(s) of increments to select.
|
|
|
|
|
phases: (list of) str, or bool, optional.
|
|
|
|
|
Name(s) of phases to select.
|
|
|
|
|
homogenizations: (list of) str, or bool, optional.
|
|
|
|
|
Name(s) of homogenizations to select.
|
|
|
|
|
fields: (list of) str, or bool, optional.
|
|
|
|
|
Name(s) of fields to select.
|
2020-02-15 22:26:20 +05:30
|
|
|
|
|
2021-04-23 22:50:07 +05:30
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
modified_view : damask.Result
|
2021-04-24 22:42:44 +05:30
|
|
|
|
View with additional visible attributes.
|
2021-04-23 22:50:07 +05:30
|
|
|
|
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Examples
|
|
|
|
|
--------
|
|
|
|
|
Get a view that shows only results from first and last increment:
|
|
|
|
|
|
|
|
|
|
>>> import damask
|
2021-12-17 14:17:58 +05:30
|
|
|
|
>>> r_empty = damask.Result('my_file.hdf5').view(increments=False)
|
|
|
|
|
>>> r_first = r_empty.view_more(increments=0)
|
|
|
|
|
>>> r_first_and_last = r.first.view_more(increments=-1)
|
2021-04-24 10:43:36 +05:30
|
|
|
|
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
2021-12-17 14:17:58 +05:30
|
|
|
|
what_, datasets_ = _view_transition(what,datasets,increments,times,phases,homogenizations,fields)
|
|
|
|
|
return self._manage_view('add',what_,datasets_)
|
2020-02-15 22:26:20 +05:30
|
|
|
|
|
|
|
|
|
|
2021-12-17 14:17:58 +05:30
|
|
|
|
def view_less(self,what=None,datasets=None,*,
|
|
|
|
|
increments=None,
|
|
|
|
|
times=None,
|
|
|
|
|
phases=None,
|
|
|
|
|
homogenizations=None,
|
|
|
|
|
fields=None):
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
2021-04-24 22:42:44 +05:30
|
|
|
|
Remove from view.
|
2020-02-15 19:43:56 +05:30
|
|
|
|
|
2021-12-17 14:17:58 +05:30
|
|
|
|
Wildcard matching with '?' and '*' is supported.
|
|
|
|
|
True is equivalent to '*', False is equivalent to [].
|
|
|
|
|
|
2020-02-21 12:15:05 +05:30
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2021-04-05 13:59:34 +05:30
|
|
|
|
what : {'increments', 'times', 'phases', 'homogenizations', 'fields'}
|
2021-12-17 14:17:58 +05:30
|
|
|
|
Attribute to change. DEPRECATED.
|
2021-04-06 21:09:44 +05:30
|
|
|
|
datasets : (list of) int (for increments), (list of) float (for times), (list of) str, or bool
|
2021-12-17 14:17:58 +05:30
|
|
|
|
Name of datasets; supports '?' and '*' wildcards. DEPRECATED.
|
2021-04-06 21:09:44 +05:30
|
|
|
|
True is equivalent to '*', False is equivalent to [].
|
2021-12-17 14:17:58 +05:30
|
|
|
|
increments: (list of) int, (list of) str, or bool, optional.
|
|
|
|
|
Number(s) of increments to select.
|
|
|
|
|
times: (list of) float, (list of) str, or bool, optional.
|
|
|
|
|
Simulation time(s) of increments to select.
|
|
|
|
|
phases: (list of) str, or bool, optional.
|
|
|
|
|
Name(s) of phases to select.
|
|
|
|
|
homogenizations: (list of) str, or bool, optional.
|
|
|
|
|
Name(s) of homogenizations to select.
|
|
|
|
|
fields: (list of) str, or bool, optional.
|
|
|
|
|
Name(s) of fields to select.
|
2019-10-19 16:40:46 +05:30
|
|
|
|
|
2021-04-23 22:50:07 +05:30
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
modified_view : damask.Result
|
2021-04-24 22:42:44 +05:30
|
|
|
|
View with fewer visible attributes.
|
2021-04-23 22:50:07 +05:30
|
|
|
|
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Examples
|
|
|
|
|
--------
|
2021-04-24 22:42:44 +05:30
|
|
|
|
Get a view that omits the undeformed configuration:
|
2021-04-24 10:43:36 +05:30
|
|
|
|
|
|
|
|
|
>>> import damask
|
|
|
|
|
>>> r_all = damask.Result('my_file.hdf5')
|
2021-12-17 14:17:58 +05:30
|
|
|
|
>>> r_deformed = r_all.view_less(increments=0)
|
2021-04-24 10:43:36 +05:30
|
|
|
|
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
2021-12-17 14:17:58 +05:30
|
|
|
|
what_, datasets_ = _view_transition(what,datasets,increments,times,phases,homogenizations,fields)
|
|
|
|
|
return self._manage_view('del',what_,datasets_)
|
2020-02-15 19:43:56 +05:30
|
|
|
|
|
2020-05-30 16:17:36 +05:30
|
|
|
|
|
2021-04-24 10:43:36 +05:30
|
|
|
|
def rename(self,name_src,name_dst):
|
2020-05-30 16:17:36 +05:30
|
|
|
|
"""
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Rename/move datasets (within the same group/folder).
|
|
|
|
|
|
|
|
|
|
This operation is discouraged because the history of the
|
2021-04-25 21:04:41 +05:30
|
|
|
|
data becomes untraceable and data integrity is not ensured.
|
2020-06-25 01:04:51 +05:30
|
|
|
|
|
2020-06-02 01:43:01 +05:30
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2021-04-24 10:43:36 +05:30
|
|
|
|
name_src : str
|
|
|
|
|
Name of the datasets to be renamed.
|
|
|
|
|
name_dst : str
|
|
|
|
|
New name of the datasets.
|
|
|
|
|
|
|
|
|
|
Examples
|
|
|
|
|
--------
|
|
|
|
|
Rename datasets containing the deformation gradient from 'F' to 'def_grad':
|
|
|
|
|
|
|
|
|
|
>>> import damask
|
|
|
|
|
>>> r = damask.Result('my_file.hdf5')
|
2021-12-17 15:01:41 +05:30
|
|
|
|
>>> r_unprotected = r.view(protected=False)
|
2021-04-24 10:43:36 +05:30
|
|
|
|
>>> r_unprotected.rename('F','def_grad')
|
2020-05-30 16:17:36 +05:30
|
|
|
|
|
|
|
|
|
"""
|
2021-12-17 15:07:45 +05:30
|
|
|
|
if self._protected:
|
2021-04-24 10:43:36 +05:30
|
|
|
|
raise PermissionError('Renaming datasets not permitted')
|
2020-05-30 16:17:36 +05:30
|
|
|
|
|
2021-04-04 23:14:06 +05:30
|
|
|
|
with h5py.File(self.fname,'a') as f:
|
|
|
|
|
for inc in self.visible['increments']:
|
2021-04-05 11:23:19 +05:30
|
|
|
|
for ty in ['phase','homogenization']:
|
|
|
|
|
for label in self.visible[ty+'s']:
|
2021-04-14 22:54:45 +05:30
|
|
|
|
for field in _match(self.visible['fields'],f['/'.join([inc,ty,label])].keys()):
|
2021-04-24 10:43:36 +05:30
|
|
|
|
path_src = '/'.join([inc,ty,label,field,name_src])
|
|
|
|
|
path_dst = '/'.join([inc,ty,label,field,name_dst])
|
|
|
|
|
if path_src in f.keys():
|
|
|
|
|
f[path_dst] = f[path_src]
|
|
|
|
|
f[path_dst].attrs['renamed'] = f'original name: {name_src}' if h5py3 else \
|
|
|
|
|
f'original name: {name_src}'.encode()
|
|
|
|
|
del f[path_src]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def remove(self,name):
|
|
|
|
|
"""
|
|
|
|
|
Remove/delete datasets.
|
|
|
|
|
|
|
|
|
|
This operation is discouraged because the history of the
|
2021-04-25 21:04:41 +05:30
|
|
|
|
data becomes untraceable and data integrity is not ensured.
|
2021-04-24 10:43:36 +05:30
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
name : str
|
|
|
|
|
Name of the datasets to be deleted.
|
|
|
|
|
|
|
|
|
|
Examples
|
|
|
|
|
--------
|
|
|
|
|
Delete the deformation gradient 'F':
|
|
|
|
|
|
|
|
|
|
>>> import damask
|
|
|
|
|
>>> r = damask.Result('my_file.hdf5')
|
2021-12-17 15:01:41 +05:30
|
|
|
|
>>> r_unprotected = r.view(protected=False)
|
2021-04-24 10:43:36 +05:30
|
|
|
|
>>> r_unprotected.remove('F')
|
|
|
|
|
|
|
|
|
|
"""
|
2021-12-17 15:07:45 +05:30
|
|
|
|
if self._protected:
|
2021-04-24 10:43:36 +05:30
|
|
|
|
raise PermissionError('Removing datasets not permitted')
|
|
|
|
|
|
|
|
|
|
with h5py.File(self.fname,'a') as f:
|
|
|
|
|
for inc in self.visible['increments']:
|
|
|
|
|
for ty in ['phase','homogenization']:
|
|
|
|
|
for label in self.visible[ty+'s']:
|
|
|
|
|
for field in _match(self.visible['fields'],f['/'.join([inc,ty,label])].keys()):
|
|
|
|
|
path = '/'.join([inc,ty,label,field,name])
|
|
|
|
|
if path in f.keys(): del f[path]
|
2021-04-04 23:14:06 +05:30
|
|
|
|
|
2020-05-30 16:17:36 +05:30
|
|
|
|
|
2020-02-21 12:15:05 +05:30
|
|
|
|
def list_data(self):
|
2022-02-17 22:35:13 +05:30
|
|
|
|
"""
|
|
|
|
|
Collect information on all active datasets in the file.
|
|
|
|
|
|
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
data : list of str
|
|
|
|
|
Line-formatted information about active datasets.
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
msg = []
|
2020-02-21 12:15:05 +05:30
|
|
|
|
with h5py.File(self.fname,'r') as f:
|
2021-04-04 15:57:39 +05:30
|
|
|
|
for inc in self.visible['increments']:
|
2022-02-17 22:35:13 +05:30
|
|
|
|
msg += [f'\n{inc} ({self.times[self.increments.index(inc)]} s)']
|
2021-04-05 11:23:19 +05:30
|
|
|
|
for ty in ['phase','homogenization']:
|
2022-02-17 22:35:13 +05:30
|
|
|
|
msg += [f' {ty}']
|
2021-04-05 11:23:19 +05:30
|
|
|
|
for label in self.visible[ty+'s']:
|
2022-02-17 22:35:13 +05:30
|
|
|
|
msg += [f' {label}']
|
2021-04-14 22:54:45 +05:30
|
|
|
|
for field in _match(self.visible['fields'],f['/'.join([inc,ty,label])].keys()):
|
2022-02-17 22:35:13 +05:30
|
|
|
|
msg += [f' {field}']
|
2021-04-05 11:23:19 +05:30
|
|
|
|
for d in f['/'.join([inc,ty,label,field])].keys():
|
|
|
|
|
dataset = f['/'.join([inc,ty,label,field,d])]
|
2022-02-17 22:35:13 +05:30
|
|
|
|
unit = dataset.attrs["unit"] if h5py3 else \
|
|
|
|
|
dataset.attrs["unit"].decode()
|
2021-04-14 10:36:24 +05:30
|
|
|
|
description = dataset.attrs['description'] if h5py3 else \
|
|
|
|
|
dataset.attrs['description'].decode()
|
2022-02-17 22:35:13 +05:30
|
|
|
|
msg += [f' {d} / {unit}: {description}']
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
2021-04-05 11:23:19 +05:30
|
|
|
|
return msg
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
|
2020-07-03 10:59:31 +05:30
|
|
|
|
def enable_user_function(self,func):
|
|
|
|
|
globals()[func.__name__]=func
|
|
|
|
|
print(f'Function {func.__name__} enabled in add_calculation.')
|
|
|
|
|
|
|
|
|
|
|
2020-07-31 20:20:01 +05:30
|
|
|
|
@property
|
2020-12-04 03:30:49 +05:30
|
|
|
|
def coordinates0_point(self):
|
2021-04-23 22:50:07 +05:30
|
|
|
|
"""Initial/undeformed cell center coordinates."""
|
2020-02-21 12:15:05 +05:30
|
|
|
|
if self.structured:
|
2020-12-04 03:30:49 +05:30
|
|
|
|
return grid_filters.coordinates0_point(self.cells,self.size,self.origin).reshape(-1,3,order='F')
|
2020-02-21 12:15:05 +05:30
|
|
|
|
else:
|
2020-02-21 22:17:47 +05:30
|
|
|
|
with h5py.File(self.fname,'r') as f:
|
2021-04-26 03:52:37 +05:30
|
|
|
|
return f['geometry/x_p'][()]
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
2020-07-31 20:20:01 +05:30
|
|
|
|
@property
|
2020-12-04 03:30:49 +05:30
|
|
|
|
def coordinates0_node(self):
|
2021-04-23 22:50:07 +05:30
|
|
|
|
"""Initial/undeformed nodal coordinates."""
|
2020-04-22 11:10:02 +05:30
|
|
|
|
if self.structured:
|
2020-12-04 03:30:49 +05:30
|
|
|
|
return grid_filters.coordinates0_node(self.cells,self.size,self.origin).reshape(-1,3,order='F')
|
2020-04-22 11:10:02 +05:30
|
|
|
|
else:
|
|
|
|
|
with h5py.File(self.fname,'r') as f:
|
|
|
|
|
return f['geometry/x_n'][()]
|
|
|
|
|
|
2021-04-03 12:28:22 +05:30
|
|
|
|
@property
|
|
|
|
|
def geometry0(self):
|
2021-04-23 22:50:07 +05:30
|
|
|
|
"""Initial/undeformed geometry."""
|
2021-04-03 12:28:22 +05:30
|
|
|
|
if self.structured:
|
2021-06-15 20:32:02 +05:30
|
|
|
|
return VTK.from_image_data(self.cells,self.size,self.origin)
|
2021-04-03 12:28:22 +05:30
|
|
|
|
else:
|
|
|
|
|
with h5py.File(self.fname,'r') as f:
|
|
|
|
|
return VTK.from_unstructured_grid(f['/geometry/x_n'][()],
|
|
|
|
|
f['/geometry/T_c'][()]-1,
|
|
|
|
|
f['/geometry/T_c'].attrs['VTK_TYPE'] if h5py3 else \
|
|
|
|
|
f['/geometry/T_c'].attrs['VTK_TYPE'].decode())
|
|
|
|
|
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
2020-02-21 23:54:26 +05:30
|
|
|
|
@staticmethod
|
|
|
|
|
def _add_absolute(x):
|
|
|
|
|
return {
|
|
|
|
|
'data': np.abs(x['data']),
|
2020-06-25 01:04:51 +05:30
|
|
|
|
'label': f'|{x["label"]}|',
|
2020-02-21 23:54:26 +05:30
|
|
|
|
'meta': {
|
2021-03-25 23:52:59 +05:30
|
|
|
|
'unit': x['meta']['unit'],
|
|
|
|
|
'description': f"absolute value of {x['label']} ({x['meta']['description']})",
|
|
|
|
|
'creator': 'add_absolute'
|
2020-02-21 23:54:26 +05:30
|
|
|
|
}
|
|
|
|
|
}
|
2020-02-21 12:15:05 +05:30
|
|
|
|
def add_absolute(self,x):
|
|
|
|
|
"""
|
|
|
|
|
Add absolute value.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
x : str
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Name of scalar, vector, or tensor dataset to take absolute value of.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
"""
|
2020-02-22 02:07:02 +05:30
|
|
|
|
self._add_generic_pointwise(self._add_absolute,{'x':x})
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
|
2020-02-21 23:54:26 +05:30
|
|
|
|
@staticmethod
|
|
|
|
|
def _add_calculation(**kwargs):
|
|
|
|
|
formula = kwargs['formula']
|
|
|
|
|
for d in re.findall(r'#(.*?)#',formula):
|
2020-06-25 01:04:51 +05:30
|
|
|
|
formula = formula.replace(f'#{d}#',f"kwargs['{d}']['data']")
|
2021-08-17 01:01:17 +05:30
|
|
|
|
data = eval(formula)
|
|
|
|
|
|
|
|
|
|
if not hasattr(data,'shape') or data.shape[0] != kwargs[d]['data'].shape[0]:
|
|
|
|
|
raise ValueError("'{}' results in invalid shape".format(kwargs['formula']))
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
2020-02-21 23:54:26 +05:30
|
|
|
|
return {
|
2021-08-17 01:01:17 +05:30
|
|
|
|
'data': data,
|
2020-02-21 23:54:26 +05:30
|
|
|
|
'label': kwargs['label'],
|
|
|
|
|
'meta': {
|
2021-03-25 23:52:59 +05:30
|
|
|
|
'unit': kwargs['unit'],
|
|
|
|
|
'description': f"{kwargs['description']} (formula: {kwargs['formula']})",
|
|
|
|
|
'creator': 'add_calculation'
|
2020-02-21 23:54:26 +05:30
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-25 11:17:00 +05:30
|
|
|
|
def add_calculation(self,formula,name,unit='n/a',description=None):
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
|
|
|
|
Add result of a general formula.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2020-02-21 17:33:50 +05:30
|
|
|
|
formula : str
|
2021-04-25 11:17:00 +05:30
|
|
|
|
Formula to calculate resulting dataset.
|
|
|
|
|
Existing datasets are referenced by '#TheirName#'.
|
|
|
|
|
name : str
|
|
|
|
|
Name of resulting dataset.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
unit : str, optional
|
2020-03-19 12:15:31 +05:30
|
|
|
|
Physical unit of the result.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
description : str, optional
|
2020-03-19 12:15:31 +05:30
|
|
|
|
Human-readable description of the result.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Examples
|
|
|
|
|
--------
|
|
|
|
|
Add total dislocation density, i.e. the sum of mobile dislocation
|
|
|
|
|
density 'rho_mob' and dislocation dipole density 'rho_dip' over
|
|
|
|
|
all slip systems:
|
|
|
|
|
|
|
|
|
|
>>> import damask
|
|
|
|
|
>>> r = damask.Result('my_file.hdf5')
|
2021-04-25 11:17:00 +05:30
|
|
|
|
>>> r.add_calculation('np.sum(#rho_mob#,axis=1)','rho_mob_total',
|
2021-04-24 10:43:36 +05:30
|
|
|
|
... '1/m²','total mobile dislocation density')
|
2021-04-25 21:04:41 +05:30
|
|
|
|
>>> r.add_calculation('np.sum(#rho_dip#,axis=1)','rho_dip_total',
|
2021-04-24 10:43:36 +05:30
|
|
|
|
... '1/m²','total dislocation dipole density')
|
2021-04-25 11:17:00 +05:30
|
|
|
|
>>> r.add_calculation('#rho_dip_total#+#rho_mob_total','rho_total',
|
2021-04-24 10:43:36 +05:30
|
|
|
|
... '1/m²','total dislocation density')
|
|
|
|
|
|
|
|
|
|
Add Mises equivalent of the Cauchy stress without storage of
|
|
|
|
|
intermediate results. Define a user function for better readability:
|
|
|
|
|
|
|
|
|
|
>>> import damask
|
|
|
|
|
>>> def equivalent_stress(F,P):
|
|
|
|
|
... sigma = damask.mechanics.stress_Cauchy(F=F,P=P)
|
|
|
|
|
... return damask.mechanics.equivalent_stress_Mises(sigma)
|
|
|
|
|
>>> r = damask.Result('my_file.hdf5')
|
|
|
|
|
>>> r.enable_user_function(equivalent_stress)
|
2021-04-25 11:17:00 +05:30
|
|
|
|
>>> r.add_calculation('equivalent_stress(#F#,#P#)','sigma_vM','Pa',
|
2021-04-24 10:43:36 +05:30
|
|
|
|
... 'Mises equivalent of the Cauchy stress')
|
|
|
|
|
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
2020-02-21 23:54:26 +05:30
|
|
|
|
dataset_mapping = {d:d for d in set(re.findall(r'#(.*?)#',formula))} # datasets used in the formula
|
2021-04-24 10:43:36 +05:30
|
|
|
|
args = {'formula':formula,'label':name,'unit':unit,'description':description}
|
2020-02-22 02:07:02 +05:30
|
|
|
|
self._add_generic_pointwise(self._add_calculation,dataset_mapping,args)
|
2020-02-21 23:54:26 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
2020-11-18 03:26:22 +05:30
|
|
|
|
def _add_stress_Cauchy(P,F):
|
2020-02-21 23:54:26 +05:30
|
|
|
|
return {
|
2020-11-18 03:26:22 +05:30
|
|
|
|
'data': mechanics.stress_Cauchy(P['data'],F['data']),
|
2020-02-21 23:54:26 +05:30
|
|
|
|
'label': 'sigma',
|
|
|
|
|
'meta': {
|
2021-03-25 23:52:59 +05:30
|
|
|
|
'unit': P['meta']['unit'],
|
|
|
|
|
'description': "Cauchy stress calculated "
|
|
|
|
|
f"from {P['label']} ({P['meta']['description']})"
|
|
|
|
|
f" and {F['label']} ({F['meta']['description']})",
|
|
|
|
|
'creator': 'add_stress_Cauchy'
|
2020-02-21 23:54:26 +05:30
|
|
|
|
}
|
|
|
|
|
}
|
2020-11-18 03:26:22 +05:30
|
|
|
|
def add_stress_Cauchy(self,P='P',F='F'):
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
2020-02-21 17:33:50 +05:30
|
|
|
|
Add Cauchy stress calculated from first Piola-Kirchhoff stress and deformation gradient.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
P : str, optional
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Name of the dataset containing the first Piola-Kirchhoff stress. Defaults to 'P'.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
F : str, optional
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Name of the dataset containing the deformation gradient. Defaults to 'F'.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
"""
|
2020-11-18 03:26:22 +05:30
|
|
|
|
self._add_generic_pointwise(self._add_stress_Cauchy,{'P':P,'F':F})
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
|
2020-02-21 23:54:26 +05:30
|
|
|
|
@staticmethod
|
|
|
|
|
def _add_determinant(T):
|
|
|
|
|
return {
|
|
|
|
|
'data': np.linalg.det(T['data']),
|
2020-06-25 01:04:51 +05:30
|
|
|
|
'label': f"det({T['label']})",
|
2020-02-21 23:54:26 +05:30
|
|
|
|
'meta': {
|
2021-03-25 23:52:59 +05:30
|
|
|
|
'unit': T['meta']['unit'],
|
|
|
|
|
'description': f"determinant of tensor {T['label']} ({T['meta']['description']})",
|
|
|
|
|
'creator': 'add_determinant'
|
2020-02-21 23:54:26 +05:30
|
|
|
|
}
|
|
|
|
|
}
|
2020-02-21 17:33:50 +05:30
|
|
|
|
def add_determinant(self,T):
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
|
|
|
|
Add the determinant of a tensor.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2020-02-21 17:33:50 +05:30
|
|
|
|
T : str
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Name of tensor dataset.
|
|
|
|
|
|
|
|
|
|
Examples
|
|
|
|
|
--------
|
|
|
|
|
Add the determinant of plastic deformation gradient 'F_p':
|
|
|
|
|
|
|
|
|
|
>>> import damask
|
|
|
|
|
>>> r = damask.Result('my_file.hdf5')
|
|
|
|
|
>>> r.add_determinant('F_p')
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
"""
|
2020-02-22 02:07:02 +05:30
|
|
|
|
self._add_generic_pointwise(self._add_determinant,{'T':T})
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
|
2020-02-21 23:54:26 +05:30
|
|
|
|
@staticmethod
|
|
|
|
|
def _add_deviator(T):
|
|
|
|
|
return {
|
2020-11-19 19:08:54 +05:30
|
|
|
|
'data': tensor.deviatoric(T['data']),
|
2020-06-25 01:04:51 +05:30
|
|
|
|
'label': f"s_{T['label']}",
|
2020-02-21 23:54:26 +05:30
|
|
|
|
'meta': {
|
2021-03-25 23:52:59 +05:30
|
|
|
|
'unit': T['meta']['unit'],
|
|
|
|
|
'description': f"deviator of tensor {T['label']} ({T['meta']['description']})",
|
|
|
|
|
'creator': 'add_deviator'
|
2020-02-21 23:54:26 +05:30
|
|
|
|
}
|
|
|
|
|
}
|
2020-02-21 17:33:50 +05:30
|
|
|
|
def add_deviator(self,T):
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
|
|
|
|
Add the deviatoric part of a tensor.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2020-02-21 17:33:50 +05:30
|
|
|
|
T : str
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Name of tensor dataset.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
2021-07-16 13:51:06 +05:30
|
|
|
|
Examples
|
|
|
|
|
--------
|
|
|
|
|
Add the deviatoric part of Cauchy stress 'sigma':
|
|
|
|
|
|
|
|
|
|
>>> import damask
|
|
|
|
|
>>> r = damask.Result('my_file.hdf5')
|
|
|
|
|
>>> r.add_deviator('sigma')
|
|
|
|
|
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
2020-02-22 02:07:02 +05:30
|
|
|
|
self._add_generic_pointwise(self._add_deviator,{'T':T})
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
|
2020-02-21 23:54:26 +05:30
|
|
|
|
@staticmethod
|
2020-05-27 21:06:30 +05:30
|
|
|
|
def _add_eigenvalue(T_sym,eigenvalue):
|
|
|
|
|
if eigenvalue == 'max':
|
2021-03-25 23:52:59 +05:30
|
|
|
|
label,p = 'maximum',2
|
2020-05-27 21:06:30 +05:30
|
|
|
|
elif eigenvalue == 'mid':
|
2021-03-25 23:52:59 +05:30
|
|
|
|
label,p = 'intermediate',1
|
2020-05-27 21:06:30 +05:30
|
|
|
|
elif eigenvalue == 'min':
|
2021-03-25 23:52:59 +05:30
|
|
|
|
label,p = 'minimum',0
|
2020-05-27 21:06:30 +05:30
|
|
|
|
|
2020-02-21 23:54:26 +05:30
|
|
|
|
return {
|
2020-11-16 03:44:46 +05:30
|
|
|
|
'data': tensor.eigenvalues(T_sym['data'])[:,p],
|
2020-06-25 01:04:51 +05:30
|
|
|
|
'label': f"lambda_{eigenvalue}({T_sym['label']})",
|
2020-02-21 23:54:26 +05:30
|
|
|
|
'meta' : {
|
2021-03-25 23:52:59 +05:30
|
|
|
|
'unit': T_sym['meta']['unit'],
|
|
|
|
|
'description': f"{label} eigenvalue of {T_sym['label']} ({T_sym['meta']['description']})",
|
|
|
|
|
'creator': 'add_eigenvalue'
|
2020-02-21 23:54:26 +05:30
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-27 21:06:30 +05:30
|
|
|
|
def add_eigenvalue(self,T_sym,eigenvalue='max'):
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
|
|
|
|
Add eigenvalues of symmetric tensor.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2020-03-03 03:44:59 +05:30
|
|
|
|
T_sym : str
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Name of symmetric tensor dataset.
|
2021-06-01 10:19:14 +05:30
|
|
|
|
eigenvalue : {'max', 'mid', 'min'}
|
|
|
|
|
Eigenvalue. Defaults to 'max'.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
2021-07-16 13:51:06 +05:30
|
|
|
|
Examples
|
|
|
|
|
--------
|
|
|
|
|
Add the minimum eigenvalue of Cauchy stress 'sigma':
|
|
|
|
|
|
|
|
|
|
>>> import damask
|
|
|
|
|
>>> r = damask.Result('my_file.hdf5')
|
|
|
|
|
>>> r.add_eigenvalue('sigma','min')
|
|
|
|
|
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
2020-05-27 21:06:30 +05:30
|
|
|
|
self._add_generic_pointwise(self._add_eigenvalue,{'T_sym':T_sym},{'eigenvalue':eigenvalue})
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
|
2020-02-21 23:54:26 +05:30
|
|
|
|
@staticmethod
|
2020-05-27 21:06:30 +05:30
|
|
|
|
def _add_eigenvector(T_sym,eigenvalue):
|
|
|
|
|
if eigenvalue == 'max':
|
|
|
|
|
label,p = 'maximum',2
|
|
|
|
|
elif eigenvalue == 'mid':
|
|
|
|
|
label,p = 'intermediate',1
|
|
|
|
|
elif eigenvalue == 'min':
|
|
|
|
|
label,p = 'minimum',0
|
2020-02-21 23:54:26 +05:30
|
|
|
|
return {
|
2020-11-16 03:44:46 +05:30
|
|
|
|
'data': tensor.eigenvectors(T_sym['data'])[:,p],
|
2020-06-25 01:04:51 +05:30
|
|
|
|
'label': f"v_{eigenvalue}({T_sym['label']})",
|
2020-02-21 23:54:26 +05:30
|
|
|
|
'meta' : {
|
2021-03-25 23:52:59 +05:30
|
|
|
|
'unit': '1',
|
|
|
|
|
'description': f"eigenvector corresponding to {label} eigenvalue"
|
|
|
|
|
f" of {T_sym['label']} ({T_sym['meta']['description']})",
|
|
|
|
|
'creator': 'add_eigenvector'
|
2020-02-21 23:54:26 +05:30
|
|
|
|
}
|
2020-06-25 01:04:51 +05:30
|
|
|
|
}
|
2020-05-27 21:06:30 +05:30
|
|
|
|
def add_eigenvector(self,T_sym,eigenvalue='max'):
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
2020-05-27 21:06:30 +05:30
|
|
|
|
Add eigenvector of symmetric tensor.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2020-03-03 03:44:59 +05:30
|
|
|
|
T_sym : str
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Name of symmetric tensor dataset.
|
2021-06-01 10:19:14 +05:30
|
|
|
|
eigenvalue : {'max', 'mid', 'min'}
|
2021-04-06 21:09:44 +05:30
|
|
|
|
Eigenvalue to which the eigenvector corresponds.
|
2021-06-01 10:19:14 +05:30
|
|
|
|
Defaults to 'max'.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
"""
|
2020-05-27 21:06:30 +05:30
|
|
|
|
self._add_generic_pointwise(self._add_eigenvector,{'T_sym':T_sym},{'eigenvalue':eigenvalue})
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
|
2020-02-21 23:54:26 +05:30
|
|
|
|
@staticmethod
|
2020-12-02 19:15:47 +05:30
|
|
|
|
def _add_IPF_color(l,q):
|
2020-06-20 20:45:13 +05:30
|
|
|
|
m = util.scale_to_coprime(np.array(l))
|
2021-06-01 10:19:14 +05:30
|
|
|
|
lattice = q['meta']['lattice']
|
|
|
|
|
o = Orientation(rotation = q['data'],lattice=lattice)
|
2020-02-21 23:54:26 +05:30
|
|
|
|
|
|
|
|
|
return {
|
2020-11-18 21:15:53 +05:30
|
|
|
|
'data': np.uint8(o.IPF_color(l)*255),
|
2021-04-04 22:35:58 +05:30
|
|
|
|
'label': 'IPFcolor_({} {} {})'.format(*m),
|
2020-02-21 23:54:26 +05:30
|
|
|
|
'meta' : {
|
2021-03-25 23:52:59 +05:30
|
|
|
|
'unit': '8-bit RGB',
|
|
|
|
|
'lattice': q['meta']['lattice'],
|
2021-04-04 22:35:58 +05:30
|
|
|
|
'description': 'Inverse Pole Figure (IPF) colors along sample direction ({} {} {})'.format(*m),
|
2021-03-25 23:52:59 +05:30
|
|
|
|
'creator': 'add_IPF_color'
|
2020-02-21 23:54:26 +05:30
|
|
|
|
}
|
|
|
|
|
}
|
2020-12-02 19:15:47 +05:30
|
|
|
|
def add_IPF_color(self,l,q='O'):
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
|
|
|
|
Add RGB color tuple of inverse pole figure (IPF) color.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2020-02-21 22:12:01 +05:30
|
|
|
|
l : numpy.array of shape (3)
|
2020-03-19 12:15:31 +05:30
|
|
|
|
Lab frame direction for inverse pole figure.
|
2020-12-02 19:15:47 +05:30
|
|
|
|
q : str
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Name of the dataset containing the crystallographic orientation as quaternions.
|
2020-12-02 19:15:47 +05:30
|
|
|
|
Defaults to 'O'.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Examples
|
|
|
|
|
--------
|
|
|
|
|
Add the IPF color along [0,1,1] for orientation 'O':
|
|
|
|
|
|
|
|
|
|
>>> import damask
|
|
|
|
|
>>> r = damask.Result('my_file.hdf5')
|
|
|
|
|
>>> r.add_IPF_color(np.array([0,1,1]))
|
|
|
|
|
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
2020-07-01 01:13:57 +05:30
|
|
|
|
self._add_generic_pointwise(self._add_IPF_color,{'q':q},{'l':l})
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
|
2020-02-21 23:54:26 +05:30
|
|
|
|
@staticmethod
|
2020-03-03 03:44:59 +05:30
|
|
|
|
def _add_maximum_shear(T_sym):
|
2020-02-21 23:54:26 +05:30
|
|
|
|
return {
|
2020-03-03 03:44:59 +05:30
|
|
|
|
'data': mechanics.maximum_shear(T_sym['data']),
|
2020-06-25 01:04:51 +05:30
|
|
|
|
'label': f"max_shear({T_sym['label']})",
|
2020-02-21 23:54:26 +05:30
|
|
|
|
'meta': {
|
2021-03-25 23:52:59 +05:30
|
|
|
|
'unit': T_sym['meta']['unit'],
|
|
|
|
|
'description': f"maximum shear component of {T_sym['label']} ({T_sym['meta']['description']})",
|
|
|
|
|
'creator': 'add_maximum_shear'
|
2020-02-21 23:54:26 +05:30
|
|
|
|
}
|
2020-02-21 12:15:05 +05:30
|
|
|
|
}
|
2020-03-03 03:44:59 +05:30
|
|
|
|
def add_maximum_shear(self,T_sym):
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
|
|
|
|
Add maximum shear components of symmetric tensor.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2020-03-03 03:44:59 +05:30
|
|
|
|
T_sym : str
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Name of symmetric tensor dataset.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
"""
|
2020-03-03 03:44:59 +05:30
|
|
|
|
self._add_generic_pointwise(self._add_maximum_shear,{'T_sym':T_sym})
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
|
2020-02-21 23:54:26 +05:30
|
|
|
|
@staticmethod
|
2020-11-18 03:26:22 +05:30
|
|
|
|
def _add_equivalent_Mises(T_sym,kind):
|
2020-11-06 04:17:37 +05:30
|
|
|
|
k = kind
|
|
|
|
|
if k is None:
|
2021-03-25 23:52:59 +05:30
|
|
|
|
if T_sym['meta']['unit'] == '1':
|
2020-11-05 10:18:12 +05:30
|
|
|
|
k = 'strain'
|
2021-03-25 23:52:59 +05:30
|
|
|
|
elif T_sym['meta']['unit'] == 'Pa':
|
2020-11-05 10:18:12 +05:30
|
|
|
|
k = 'stress'
|
|
|
|
|
if k not in ['stress', 'strain']:
|
2021-04-05 19:11:28 +05:30
|
|
|
|
raise ValueError(f'invalid von Mises kind {kind}')
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
2020-02-21 23:54:26 +05:30
|
|
|
|
return {
|
2020-11-18 03:26:22 +05:30
|
|
|
|
'data': (mechanics.equivalent_strain_Mises if k=='strain' else \
|
|
|
|
|
mechanics.equivalent_stress_Mises)(T_sym['data']),
|
2020-06-25 01:04:51 +05:30
|
|
|
|
'label': f"{T_sym['label']}_vM",
|
2020-02-21 23:54:26 +05:30
|
|
|
|
'meta': {
|
2021-03-25 23:52:59 +05:30
|
|
|
|
'unit': T_sym['meta']['unit'],
|
|
|
|
|
'description': f"Mises equivalent {k} of {T_sym['label']} ({T_sym['meta']['description']})",
|
|
|
|
|
'creator': 'add_Mises'
|
2020-02-21 23:54:26 +05:30
|
|
|
|
}
|
|
|
|
|
}
|
2020-11-18 03:26:22 +05:30
|
|
|
|
def add_equivalent_Mises(self,T_sym,kind=None):
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
|
|
|
|
Add the equivalent Mises stress or strain of a symmetric tensor.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2020-03-03 03:44:59 +05:30
|
|
|
|
T_sym : str
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Name of symmetric tensorial stress or strain dataset.
|
2020-11-05 10:18:12 +05:30
|
|
|
|
kind : {'stress', 'strain', None}, optional
|
|
|
|
|
Kind of the von Mises equivalent. Defaults to None, in which case
|
2021-04-06 21:09:44 +05:30
|
|
|
|
it is selected based on the unit of the dataset ('1' -> strain, 'Pa' -> stress).
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Examples
|
|
|
|
|
--------
|
|
|
|
|
Add the Mises equivalent of the Cauchy stress 'sigma':
|
|
|
|
|
|
|
|
|
|
>>> import damask
|
|
|
|
|
>>> r = damask.Result('my_file.hdf5')
|
|
|
|
|
>>> r.add_equivalent_Mises('sigma')
|
|
|
|
|
|
|
|
|
|
Add the Mises equivalent of the spatial logarithmic strain 'epsilon_V^0.0(F)':
|
|
|
|
|
|
|
|
|
|
>>> import damask
|
|
|
|
|
>>> r = damask.Result('my_file.hdf5')
|
|
|
|
|
>>> r.add_equivalent_Mises('epsilon_V^0.0(F)')
|
|
|
|
|
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
2020-11-18 03:26:22 +05:30
|
|
|
|
self._add_generic_pointwise(self._add_equivalent_Mises,{'T_sym':T_sym},{'kind':kind})
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
|
2020-02-21 23:54:26 +05:30
|
|
|
|
@staticmethod
|
|
|
|
|
def _add_norm(x,ord):
|
|
|
|
|
o = ord
|
|
|
|
|
if len(x['data'].shape) == 2:
|
|
|
|
|
axis = 1
|
|
|
|
|
t = 'vector'
|
|
|
|
|
if o is None: o = 2
|
|
|
|
|
elif len(x['data'].shape) == 3:
|
|
|
|
|
axis = (1,2)
|
|
|
|
|
t = 'tensor'
|
|
|
|
|
if o is None: o = 'fro'
|
|
|
|
|
else:
|
2022-01-15 17:52:15 +05:30
|
|
|
|
raise ValueError(f'invalid shape of {x["label"]}')
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
2020-02-21 23:54:26 +05:30
|
|
|
|
return {
|
|
|
|
|
'data': np.linalg.norm(x['data'],ord=o,axis=axis,keepdims=True),
|
2020-06-25 01:04:51 +05:30
|
|
|
|
'label': f"|{x['label']}|_{o}",
|
2020-02-21 23:54:26 +05:30
|
|
|
|
'meta': {
|
2021-03-25 23:52:59 +05:30
|
|
|
|
'unit': x['meta']['unit'],
|
|
|
|
|
'description': f"{o}-norm of {t} {x['label']} ({x['meta']['description']})",
|
|
|
|
|
'creator': 'add_norm'
|
2020-02-21 23:54:26 +05:30
|
|
|
|
}
|
|
|
|
|
}
|
2020-02-21 12:15:05 +05:30
|
|
|
|
def add_norm(self,x,ord=None):
|
|
|
|
|
"""
|
|
|
|
|
Add the norm of vector or tensor.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
x : str
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Name of vector or tensor dataset.
|
2021-04-06 21:09:44 +05:30
|
|
|
|
ord : {non-zero int, inf, -inf, 'fro', 'nuc'}, optional
|
2020-03-19 12:15:31 +05:30
|
|
|
|
Order of the norm. inf means NumPy’s inf object. For details refer to numpy.linalg.norm.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
"""
|
2020-02-22 02:07:02 +05:30
|
|
|
|
self._add_generic_pointwise(self._add_norm,{'x':x},{'ord':ord})
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
|
2020-02-21 23:54:26 +05:30
|
|
|
|
@staticmethod
|
2020-11-18 03:26:22 +05:30
|
|
|
|
def _add_stress_second_Piola_Kirchhoff(P,F):
|
2020-02-21 23:54:26 +05:30
|
|
|
|
return {
|
2020-11-18 03:26:22 +05:30
|
|
|
|
'data': mechanics.stress_second_Piola_Kirchhoff(P['data'],F['data']),
|
2020-02-21 23:54:26 +05:30
|
|
|
|
'label': 'S',
|
|
|
|
|
'meta': {
|
2021-03-25 23:52:59 +05:30
|
|
|
|
'unit': P['meta']['unit'],
|
|
|
|
|
'description': "second Piola-Kirchhoff stress calculated "
|
|
|
|
|
f"from {P['label']} ({P['meta']['description']})"
|
|
|
|
|
f" and {F['label']} ({F['meta']['description']})",
|
|
|
|
|
'creator': 'add_stress_second_Piola_Kirchhoff'
|
2020-02-21 23:54:26 +05:30
|
|
|
|
}
|
|
|
|
|
}
|
2020-11-18 03:26:22 +05:30
|
|
|
|
def add_stress_second_Piola_Kirchhoff(self,P='P',F='F'):
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
2020-06-25 01:04:51 +05:30
|
|
|
|
Add second Piola-Kirchhoff stress calculated from first Piola-Kirchhoff stress and deformation gradient.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
P : str, optional
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Name of first Piola-Kirchhoff stress dataset. Defaults to 'P'.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
F : str, optional
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Name of deformation gradient dataset. Defaults to 'F'.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
2021-04-25 21:04:41 +05:30
|
|
|
|
Notes
|
|
|
|
|
-----
|
2021-04-26 23:56:16 +05:30
|
|
|
|
The definition of the second Piola-Kirchhoff stress (S = [F^-1 P]_sym)
|
|
|
|
|
follows the standard definition in nonlinear continuum mechanics.
|
|
|
|
|
As such, no intermediate configuration, for instance that reached by F_p,
|
|
|
|
|
is taken into account.
|
2021-04-25 21:04:41 +05:30
|
|
|
|
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
2020-11-18 03:26:22 +05:30
|
|
|
|
self._add_generic_pointwise(self._add_stress_second_Piola_Kirchhoff,{'P':P,'F':F})
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
|
2021-07-18 21:33:36 +05:30
|
|
|
|
|
|
|
|
|
@staticmethod
|
2021-08-16 22:56:15 +05:30
|
|
|
|
def _add_pole(q,uvw,hkl,with_symmetry):
|
2021-07-18 21:33:36 +05:30
|
|
|
|
c = q['meta']['c/a'] if 'c/a' in q['meta'] else 1
|
2021-08-16 22:56:15 +05:30
|
|
|
|
pole = Orientation(q['data'],lattice=q['meta']['lattice'],a=1,c=c).to_pole(uvw=uvw,hkl=hkl,with_symmetry=with_symmetry)
|
2021-07-18 21:33:36 +05:30
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
'data': pole,
|
|
|
|
|
'label': 'p^[{} {} {}]'.format(*uvw) if uvw else 'p^({} {} {})'.format(*hkl),
|
|
|
|
|
'meta' : {
|
|
|
|
|
'unit': '1',
|
2021-08-16 22:56:15 +05:30
|
|
|
|
'description': 'lab frame vector along lattice ' \
|
|
|
|
|
+ ('direction' if uvw else 'plane') \
|
|
|
|
|
+ ('s' if with_symmetry else ''),
|
2021-07-18 21:33:36 +05:30
|
|
|
|
'creator': 'add_pole'
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-08-16 22:56:15 +05:30
|
|
|
|
def add_pole(self,q='O',*,uvw=None,hkl=None,with_symmetry=False):
|
2021-07-18 21:33:36 +05:30
|
|
|
|
"""
|
|
|
|
|
Add lab frame vector along lattice direction [uvw] or plane normal (hkl).
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
q : str
|
|
|
|
|
Name of the dataset containing the crystallographic orientation as quaternions.
|
|
|
|
|
Defaults to 'O'.
|
|
|
|
|
uvw|hkl : numpy.ndarray of shape (...,3)
|
|
|
|
|
Miller indices of crystallographic direction or plane normal.
|
2021-08-16 22:56:15 +05:30
|
|
|
|
with_symmetry : bool, optional
|
|
|
|
|
Calculate all N symmetrically equivalent vectors.
|
2021-07-18 21:33:36 +05:30
|
|
|
|
|
|
|
|
|
"""
|
2021-08-16 22:56:15 +05:30
|
|
|
|
self._add_generic_pointwise(self._add_pole,{'q':q},{'uvw':uvw,'hkl':hkl,'with_symmetry':with_symmetry})
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
|
2020-02-21 23:54:26 +05:30
|
|
|
|
@staticmethod
|
2020-11-20 03:16:52 +05:30
|
|
|
|
def _add_rotation(F):
|
2020-02-21 23:54:26 +05:30
|
|
|
|
return {
|
2020-11-20 03:16:52 +05:30
|
|
|
|
'data': mechanics.rotation(F['data']).as_matrix(),
|
2020-06-25 01:04:51 +05:30
|
|
|
|
'label': f"R({F['label']})",
|
2020-02-21 23:54:26 +05:30
|
|
|
|
'meta': {
|
2021-03-25 23:52:59 +05:30
|
|
|
|
'unit': F['meta']['unit'],
|
|
|
|
|
'description': f"rotational part of {F['label']} ({F['meta']['description']})",
|
|
|
|
|
'creator': 'add_rotation'
|
2020-02-21 23:54:26 +05:30
|
|
|
|
}
|
2020-02-21 12:15:05 +05:30
|
|
|
|
}
|
2020-11-20 03:16:52 +05:30
|
|
|
|
def add_rotation(self,F):
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
|
|
|
|
Add rotational part of a deformation gradient.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2021-04-24 10:43:36 +05:30
|
|
|
|
F : str
|
|
|
|
|
Name of deformation gradient dataset.
|
|
|
|
|
|
|
|
|
|
Examples
|
|
|
|
|
--------
|
|
|
|
|
Add the rotational part of deformation gradient 'F':
|
|
|
|
|
|
|
|
|
|
>>> import damask
|
|
|
|
|
>>> r = damask.Result('my_file.hdf5')
|
|
|
|
|
>>> r.add_rotation('F')
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
"""
|
2020-11-20 03:16:52 +05:30
|
|
|
|
self._add_generic_pointwise(self._add_rotation,{'F':F})
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
|
2020-02-21 23:54:26 +05:30
|
|
|
|
@staticmethod
|
|
|
|
|
def _add_spherical(T):
|
|
|
|
|
return {
|
2020-11-19 19:08:54 +05:30
|
|
|
|
'data': tensor.spherical(T['data'],False),
|
2020-06-25 01:04:51 +05:30
|
|
|
|
'label': f"p_{T['label']}",
|
2020-02-21 23:54:26 +05:30
|
|
|
|
'meta': {
|
2021-03-25 23:52:59 +05:30
|
|
|
|
'unit': T['meta']['unit'],
|
|
|
|
|
'description': f"spherical component of tensor {T['label']} ({T['meta']['description']})",
|
|
|
|
|
'creator': 'add_spherical'
|
2020-02-21 23:54:26 +05:30
|
|
|
|
}
|
|
|
|
|
}
|
2020-02-21 17:33:50 +05:30
|
|
|
|
def add_spherical(self,T):
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
|
|
|
|
Add the spherical (hydrostatic) part of a tensor.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2020-02-21 17:33:50 +05:30
|
|
|
|
T : str
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Name of tensor dataset.
|
|
|
|
|
|
|
|
|
|
Examples
|
|
|
|
|
--------
|
|
|
|
|
Add the hydrostatic part of the Cauchy stress 'sigma':
|
|
|
|
|
|
|
|
|
|
>>> import damask
|
|
|
|
|
>>> r = damask.Result('my_file.hdf5')
|
|
|
|
|
>>> r.add_spherical('sigma')
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
"""
|
2020-02-22 02:07:02 +05:30
|
|
|
|
self._add_generic_pointwise(self._add_spherical,{'T':T})
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
|
2020-02-21 23:54:26 +05:30
|
|
|
|
@staticmethod
|
2020-11-16 05:42:23 +05:30
|
|
|
|
def _add_strain(F,t,m):
|
2020-02-21 23:54:26 +05:30
|
|
|
|
return {
|
2020-11-16 05:42:23 +05:30
|
|
|
|
'data': mechanics.strain(F['data'],t,m),
|
2020-06-25 01:04:51 +05:30
|
|
|
|
'label': f"epsilon_{t}^{m}({F['label']})",
|
2020-02-21 23:54:26 +05:30
|
|
|
|
'meta': {
|
2021-03-25 23:52:59 +05:30
|
|
|
|
'unit': F['meta']['unit'],
|
|
|
|
|
'description': f"strain tensor of {F['label']} ({F['meta']['description']})",
|
|
|
|
|
'creator': 'add_strain'
|
2020-02-21 23:54:26 +05:30
|
|
|
|
}
|
|
|
|
|
}
|
2020-11-16 05:42:23 +05:30
|
|
|
|
def add_strain(self,F='F',t='V',m=0.0):
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
2020-02-21 17:33:50 +05:30
|
|
|
|
Add strain tensor of a deformation gradient.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
2021-04-06 21:09:44 +05:30
|
|
|
|
For details, see damask.mechanics.strain.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
F : str, optional
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Name of deformation gradient dataset. Defaults to 'F'.
|
2021-04-06 21:09:44 +05:30
|
|
|
|
t : {'V', 'U'}, optional
|
|
|
|
|
Type of the polar decomposition, 'V' for left stretch tensor and 'U' for right stretch tensor.
|
|
|
|
|
Defaults to 'V'.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
m : float, optional
|
2021-04-06 21:09:44 +05:30
|
|
|
|
Order of the strain calculation. Defaults to 0.0.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Examples
|
|
|
|
|
--------
|
|
|
|
|
Add the Biot strain based on the deformation gradient 'F':
|
|
|
|
|
|
|
|
|
|
>>> import damask
|
|
|
|
|
>>> r = damask.Result('my_file.hdf5')
|
|
|
|
|
>>> r.strain(t='U',m=0.5)
|
|
|
|
|
|
|
|
|
|
Add the plastic Euler-Almansi strain based on the
|
|
|
|
|
plastic deformation gradient 'F_p':
|
|
|
|
|
|
|
|
|
|
>>> import damask
|
|
|
|
|
>>> r = damask.Result('my_file.hdf5')
|
|
|
|
|
>>> r.strain('F_p','V',-1)
|
|
|
|
|
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
2020-11-16 05:42:23 +05:30
|
|
|
|
self._add_generic_pointwise(self._add_strain,{'F':F},{'t':t,'m':m})
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
|
2020-02-21 23:54:26 +05:30
|
|
|
|
@staticmethod
|
|
|
|
|
def _add_stretch_tensor(F,t):
|
|
|
|
|
return {
|
2020-11-16 05:31:32 +05:30
|
|
|
|
'data': (mechanics.stretch_left if t.upper() == 'V' else mechanics.stretch_right)(F['data']),
|
2020-06-25 01:04:51 +05:30
|
|
|
|
'label': f"{t}({F['label']})",
|
2020-02-21 23:54:26 +05:30
|
|
|
|
'meta': {
|
2021-03-25 23:52:59 +05:30
|
|
|
|
'unit': F['meta']['unit'],
|
2021-05-28 16:50:56 +05:30
|
|
|
|
'description': f"{'left' if t.upper() == 'V' else 'right'} stretch tensor "\
|
2021-05-28 16:55:28 +05:30
|
|
|
|
+f"of {F['label']} ({F['meta']['description']})", # noqa
|
2021-03-25 23:52:59 +05:30
|
|
|
|
'creator': 'add_stretch_tensor'
|
2020-02-21 23:54:26 +05:30
|
|
|
|
}
|
|
|
|
|
}
|
2020-02-21 17:33:50 +05:30
|
|
|
|
def add_stretch_tensor(self,F='F',t='V'):
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
2020-02-21 17:33:50 +05:30
|
|
|
|
Add stretch tensor of a deformation gradient.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
F : str, optional
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Name of deformation gradient dataset. Defaults to 'F'.
|
2021-04-06 21:09:44 +05:30
|
|
|
|
t : {'V', 'U'}, optional
|
|
|
|
|
Type of the polar decomposition, 'V' for left stretch tensor and 'U' for right stretch tensor.
|
|
|
|
|
Defaults to 'V'.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
"""
|
2020-02-22 02:07:02 +05:30
|
|
|
|
self._add_generic_pointwise(self._add_stretch_tensor,{'F':F},{'t':t})
|
2020-02-21 23:54:26 +05:30
|
|
|
|
|
|
|
|
|
|
2021-05-28 16:55:28 +05:30
|
|
|
|
@staticmethod
|
2021-05-29 00:27:44 +05:30
|
|
|
|
def _add_curl(f,size):
|
2021-05-28 16:55:28 +05:30
|
|
|
|
return {
|
|
|
|
|
'data': grid_filters.curl(size,f['data']),
|
|
|
|
|
'label': f"curl({f['label']})",
|
|
|
|
|
'meta': {
|
|
|
|
|
'unit': f['meta']['unit']+'/m',
|
|
|
|
|
'description': f"curl of {f['label']} ({f['meta']['description']})",
|
|
|
|
|
'creator': 'add_curl'
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-05-29 00:27:44 +05:30
|
|
|
|
def add_curl(self,f):
|
2021-05-28 16:55:28 +05:30
|
|
|
|
"""
|
|
|
|
|
Add curl of a field.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
f : str
|
|
|
|
|
Name of vector or tensor field dataset.
|
|
|
|
|
|
2021-05-29 00:27:44 +05:30
|
|
|
|
Notes
|
|
|
|
|
-----
|
|
|
|
|
This function is only available for structured grids,
|
|
|
|
|
i.e. results from the grid solver.
|
|
|
|
|
|
2021-05-28 16:55:28 +05:30
|
|
|
|
"""
|
2021-05-29 00:27:44 +05:30
|
|
|
|
self._add_generic_grid(self._add_curl,{'f':f},{'size':self.size})
|
2021-05-28 16:55:28 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
2021-05-29 00:27:44 +05:30
|
|
|
|
def _add_divergence(f,size):
|
2021-05-28 16:55:28 +05:30
|
|
|
|
return {
|
|
|
|
|
'data': grid_filters.divergence(size,f['data']),
|
|
|
|
|
'label': f"divergence({f['label']})",
|
|
|
|
|
'meta': {
|
|
|
|
|
'unit': f['meta']['unit']+'/m',
|
|
|
|
|
'description': f"divergence of {f['label']} ({f['meta']['description']})",
|
|
|
|
|
'creator': 'add_divergence'
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-05-29 00:27:44 +05:30
|
|
|
|
def add_divergence(self,f):
|
2021-05-28 16:55:28 +05:30
|
|
|
|
"""
|
|
|
|
|
Add divergence of a field.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
f : str
|
|
|
|
|
Name of vector or tensor field dataset.
|
|
|
|
|
|
2021-05-29 00:27:44 +05:30
|
|
|
|
Notes
|
|
|
|
|
-----
|
|
|
|
|
This function is only available for structured grids,
|
|
|
|
|
i.e. results from the grid solver.
|
|
|
|
|
|
2021-05-28 16:55:28 +05:30
|
|
|
|
"""
|
2021-05-29 00:27:44 +05:30
|
|
|
|
self._add_generic_grid(self._add_divergence,{'f':f},{'size':self.size})
|
2021-05-28 16:55:28 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
2021-05-29 00:27:44 +05:30
|
|
|
|
def _add_gradient(f,size):
|
2021-05-28 16:55:28 +05:30
|
|
|
|
return {
|
|
|
|
|
'data': grid_filters.gradient(size,f['data'] if len(f['data'].shape) == 4 else \
|
|
|
|
|
f['data'].reshape(f['data'].shape+(1,))),
|
|
|
|
|
'label': f"gradient({f['label']})",
|
|
|
|
|
'meta': {
|
|
|
|
|
'unit': f['meta']['unit']+'/m',
|
|
|
|
|
'description': f"gradient of {f['label']} ({f['meta']['description']})",
|
|
|
|
|
'creator': 'add_gradient'
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-05-29 00:27:44 +05:30
|
|
|
|
def add_gradient(self,f):
|
2021-05-28 16:55:28 +05:30
|
|
|
|
"""
|
|
|
|
|
Add gradient of a field.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
f : str
|
|
|
|
|
Name of scalar or vector field dataset.
|
|
|
|
|
|
2021-05-29 00:27:44 +05:30
|
|
|
|
Notes
|
|
|
|
|
-----
|
|
|
|
|
This function is only available for structured grids,
|
|
|
|
|
i.e. results from the grid solver.
|
|
|
|
|
|
2021-05-28 16:55:28 +05:30
|
|
|
|
"""
|
2021-05-29 00:27:44 +05:30
|
|
|
|
self._add_generic_grid(self._add_gradient,{'f':f},{'size':self.size})
|
2021-05-28 16:55:28 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _add_generic_grid(self,func,datasets,args={},constituents=None):
|
|
|
|
|
"""
|
|
|
|
|
General function to add data on a regular grid.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
func : function
|
|
|
|
|
Callback function that calculates a new dataset from one or
|
|
|
|
|
more datasets per HDF5 group.
|
|
|
|
|
datasets : dictionary
|
|
|
|
|
Details of the datasets to be used:
|
|
|
|
|
{arg (name to which the data is passed in func): label (in HDF5 file)}.
|
|
|
|
|
args : dictionary, optional
|
|
|
|
|
Arguments parsed to func.
|
|
|
|
|
|
|
|
|
|
"""
|
2021-08-18 14:14:04 +05:30
|
|
|
|
if len(datasets) != 1 or self.N_constituents != 1:
|
2021-05-28 16:55:28 +05:30
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
at_cell_ph,in_data_ph,at_cell_ho,in_data_ho = self._mappings()
|
|
|
|
|
|
|
|
|
|
with h5py.File(self.fname, 'a') as f:
|
|
|
|
|
for increment in self.place(datasets.values(),False).items():
|
|
|
|
|
for ty in increment[1].items():
|
|
|
|
|
for field in ty[1].items():
|
|
|
|
|
d = list(field[1].values())[0]
|
|
|
|
|
if np.any(d.mask): continue
|
|
|
|
|
dataset = {'f':{'data':np.reshape(d.data,tuple(self.cells)+d.data.shape[1:]),
|
|
|
|
|
'label':list(datasets.values())[0],
|
|
|
|
|
'meta':d.data.dtype.metadata}}
|
|
|
|
|
r = func(**dataset,**args)
|
|
|
|
|
result = r['data'].reshape((-1,)+r['data'].shape[3:])
|
|
|
|
|
for x in self.visible[ty[0]+'s']:
|
|
|
|
|
if ty[0] == 'phase':
|
|
|
|
|
result1 = result[at_cell_ph[0][x]]
|
|
|
|
|
if ty[0] == 'homogenization':
|
|
|
|
|
result1 = result[at_cell_ho[x]]
|
|
|
|
|
|
|
|
|
|
path = '/'.join(['/',increment[0],ty[0],x,field[0]])
|
|
|
|
|
dataset = f[path].create_dataset(r['label'],data=result1)
|
|
|
|
|
|
|
|
|
|
now = datetime.datetime.now().astimezone()
|
|
|
|
|
dataset.attrs['created'] = now.strftime('%Y-%m-%d %H:%M:%S%z') if h5py3 else \
|
|
|
|
|
now.strftime('%Y-%m-%d %H:%M:%S%z').encode()
|
|
|
|
|
|
|
|
|
|
for l,v in r['meta'].items():
|
|
|
|
|
dataset.attrs[l.lower()]=v if h5py3 else v.encode()
|
|
|
|
|
creator = dataset.attrs['creator'] if h5py3 else \
|
|
|
|
|
dataset.attrs['creator'].decode()
|
|
|
|
|
dataset.attrs['creator'] = f'damask.Result.{creator} v{damask.version}' if h5py3 else \
|
|
|
|
|
f'damask.Result.{creator} v{damask.version}'.encode()
|
|
|
|
|
|
|
|
|
|
|
2021-05-28 16:50:56 +05:30
|
|
|
|
def _job_pointwise(self,group,func,datasets,args,lock):
|
2020-02-22 04:29:33 +05:30
|
|
|
|
"""Execute job for _add_generic_pointwise."""
|
2020-02-21 23:54:26 +05:30
|
|
|
|
try:
|
2020-02-22 03:46:25 +05:30
|
|
|
|
datasets_in = {}
|
|
|
|
|
lock.acquire()
|
|
|
|
|
with h5py.File(self.fname,'r') as f:
|
2020-03-12 03:05:58 +05:30
|
|
|
|
for arg,label in datasets.items():
|
|
|
|
|
loc = f[group+'/'+label]
|
|
|
|
|
datasets_in[arg]={'data' :loc[()],
|
|
|
|
|
'label':label,
|
2021-07-30 23:39:47 +05:30
|
|
|
|
'meta': {k:(v.decode() if not h5py3 and type(v) is bytes else v) \
|
|
|
|
|
for k,v in loc.attrs.items()}}
|
2020-02-22 03:46:25 +05:30
|
|
|
|
lock.release()
|
|
|
|
|
r = func(**datasets_in,**args)
|
2020-02-21 23:54:26 +05:30
|
|
|
|
return [group,r]
|
|
|
|
|
except Exception as err:
|
2020-06-25 01:04:51 +05:30
|
|
|
|
print(f'Error during calculation: {err}.')
|
2021-05-28 16:55:28 +05:30
|
|
|
|
return [None,None]
|
2020-02-21 23:54:26 +05:30
|
|
|
|
|
2020-02-22 02:07:02 +05:30
|
|
|
|
def _add_generic_pointwise(self,func,datasets,args={}):
|
2020-02-22 03:46:25 +05:30
|
|
|
|
"""
|
|
|
|
|
General function to add pointwise data.
|
2020-02-21 23:54:26 +05:30
|
|
|
|
|
2020-02-22 03:46:25 +05:30
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
func : function
|
2020-06-03 23:54:18 +05:30
|
|
|
|
Callback function that calculates a new dataset from one or
|
|
|
|
|
more datasets per HDF5 group.
|
2020-02-22 03:46:25 +05:30
|
|
|
|
datasets : dictionary
|
2021-04-06 21:09:44 +05:30
|
|
|
|
Details of the datasets to be used:
|
|
|
|
|
{arg (name to which the data is passed in func): label (in HDF5 file)}.
|
2020-02-22 03:46:25 +05:30
|
|
|
|
args : dictionary, optional
|
2020-03-19 12:15:31 +05:30
|
|
|
|
Arguments parsed to func.
|
2020-02-22 04:29:33 +05:30
|
|
|
|
|
2020-02-22 03:46:25 +05:30
|
|
|
|
"""
|
2021-04-28 23:56:25 +05:30
|
|
|
|
pool = mp.Pool(int(os.environ.get('OMP_NUM_THREADS',4)))
|
2020-06-26 15:15:06 +05:30
|
|
|
|
lock = mp.Manager().Lock()
|
2020-02-22 03:46:25 +05:30
|
|
|
|
|
2021-04-04 22:02:17 +05:30
|
|
|
|
groups = []
|
|
|
|
|
with h5py.File(self.fname,'r') as f:
|
|
|
|
|
for inc in self.visible['increments']:
|
2021-04-05 11:23:19 +05:30
|
|
|
|
for ty in ['phase','homogenization']:
|
|
|
|
|
for label in self.visible[ty+'s']:
|
2021-04-14 22:54:45 +05:30
|
|
|
|
for field in _match(self.visible['fields'],f['/'.join([inc,ty,label])].keys()):
|
2021-04-05 11:23:19 +05:30
|
|
|
|
group = '/'.join([inc,ty,label,field])
|
2021-04-04 22:02:17 +05:30
|
|
|
|
if set(datasets.values()).issubset(f[group].keys()): groups.append(group)
|
|
|
|
|
|
2020-08-27 03:44:37 +05:30
|
|
|
|
if len(groups) == 0:
|
|
|
|
|
print('No matching dataset found, no data was added.')
|
|
|
|
|
return
|
|
|
|
|
|
2021-05-28 16:50:56 +05:30
|
|
|
|
default_arg = partial(self._job_pointwise,func=func,datasets=datasets,args=args,lock=lock)
|
2020-02-22 03:46:25 +05:30
|
|
|
|
|
2021-05-28 16:50:56 +05:30
|
|
|
|
for group,result in util.show_progress(pool.imap_unordered(default_arg,groups),len(groups)):
|
2020-03-09 18:09:20 +05:30
|
|
|
|
if not result:
|
|
|
|
|
continue
|
2020-02-22 03:46:25 +05:30
|
|
|
|
lock.acquire()
|
|
|
|
|
with h5py.File(self.fname, 'a') as f:
|
2020-05-25 22:42:31 +05:30
|
|
|
|
try:
|
2021-12-17 15:07:45 +05:30
|
|
|
|
if not self._protected and '/'.join([group,result['label']]) in f:
|
2021-05-28 16:50:56 +05:30
|
|
|
|
dataset = f['/'.join([group,result['label']])]
|
|
|
|
|
dataset[...] = result['data']
|
2021-03-25 23:52:59 +05:30
|
|
|
|
dataset.attrs['overwritten'] = True
|
2020-05-25 22:42:31 +05:30
|
|
|
|
else:
|
2021-08-14 18:37:01 +05:30
|
|
|
|
shape = result['data'].shape
|
2021-05-28 16:50:56 +05:30
|
|
|
|
if result['data'].size >= chunk_size*2:
|
2020-12-06 14:50:32 +05:30
|
|
|
|
chunks = (chunk_size//np.prod(shape[1:]),)+shape[1:]
|
2021-08-14 18:37:01 +05:30
|
|
|
|
compression = ('gzip',6)
|
2020-12-06 14:50:32 +05:30
|
|
|
|
else:
|
2021-08-14 18:37:01 +05:30
|
|
|
|
chunks = shape
|
|
|
|
|
compression = (None,None)
|
|
|
|
|
dataset = f[group].create_dataset(result['label'],data=result['data'],
|
|
|
|
|
maxshape=shape, chunks=chunks,
|
|
|
|
|
compression=compression[0], compression_opts=compression[1],
|
|
|
|
|
shuffle=True,fletcher32=True)
|
2020-05-25 23:27:32 +05:30
|
|
|
|
|
2020-05-25 22:20:31 +05:30
|
|
|
|
now = datetime.datetime.now().astimezone()
|
2021-03-25 23:52:59 +05:30
|
|
|
|
dataset.attrs['created'] = now.strftime('%Y-%m-%d %H:%M:%S%z') if h5py3 else \
|
2020-11-05 20:43:29 +05:30
|
|
|
|
now.strftime('%Y-%m-%d %H:%M:%S%z').encode()
|
2020-05-25 23:27:32 +05:30
|
|
|
|
|
2021-05-28 16:50:56 +05:30
|
|
|
|
for l,v in result['meta'].items():
|
2021-07-30 23:39:47 +05:30
|
|
|
|
dataset.attrs[l.lower()]=v.encode() if not h5py3 and type(v) is str else v
|
2021-03-25 23:52:59 +05:30
|
|
|
|
creator = dataset.attrs['creator'] if h5py3 else \
|
|
|
|
|
dataset.attrs['creator'].decode()
|
2021-04-03 11:38:48 +05:30
|
|
|
|
dataset.attrs['creator'] = f'damask.Result.{creator} v{damask.version}' if h5py3 else \
|
|
|
|
|
f'damask.Result.{creator} v{damask.version}'.encode()
|
2020-05-25 23:27:32 +05:30
|
|
|
|
|
2020-05-26 10:09:11 +05:30
|
|
|
|
except (OSError,RuntimeError) as err:
|
2020-06-25 01:04:51 +05:30
|
|
|
|
print(f'Could not add dataset: {err}.')
|
2020-02-22 03:46:25 +05:30
|
|
|
|
lock.release()
|
|
|
|
|
|
|
|
|
|
pool.close()
|
|
|
|
|
pool.join()
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
|
2021-06-17 21:56:37 +05:30
|
|
|
|
def export_XDMF(self,output='*'):
|
2020-05-05 13:27:22 +05:30
|
|
|
|
"""
|
2021-07-27 13:01:57 +05:30
|
|
|
|
Write XDMF file to directly visualize data from DADF5 file.
|
2020-05-05 13:27:22 +05:30
|
|
|
|
|
2021-04-24 10:43:36 +05:30
|
|
|
|
The XDMF format is only supported for structured grids
|
|
|
|
|
with single phase and single constituent.
|
2021-06-17 21:56:37 +05:30
|
|
|
|
For other cases use `export_VTK`.
|
2021-04-24 10:43:36 +05:30
|
|
|
|
|
2021-04-05 13:59:34 +05:30
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2021-04-06 21:09:44 +05:30
|
|
|
|
output : (list of) str
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Names of the datasets included in the XDMF file.
|
2021-04-06 21:09:44 +05:30
|
|
|
|
Defaults to '*', in which case all datasets are considered.
|
2021-04-05 13:59:34 +05:30
|
|
|
|
|
2020-05-05 13:27:22 +05:30
|
|
|
|
"""
|
2020-11-30 15:10:46 +05:30
|
|
|
|
if self.N_constituents != 1 or len(self.phases) != 1 or not self.structured:
|
2021-04-06 21:09:44 +05:30
|
|
|
|
raise TypeError('XDMF output requires structured grid with single phase and single constituent.')
|
2020-11-30 15:10:46 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
|
attribute_type_map = defaultdict(lambda:'Matrix', ( ((),'Scalar'), ((3,),'Vector'), ((3,3),'Tensor')) )
|
|
|
|
|
|
|
|
|
|
def number_type_map(dtype):
|
|
|
|
|
if dtype in np.sctypes['int']: return 'Int'
|
|
|
|
|
if dtype in np.sctypes['uint']: return 'UInt'
|
|
|
|
|
if dtype in np.sctypes['float']: return 'Float'
|
|
|
|
|
|
2020-05-05 13:27:22 +05:30
|
|
|
|
|
2021-04-06 21:09:44 +05:30
|
|
|
|
xdmf = ET.Element('Xdmf')
|
2020-05-07 22:42:05 +05:30
|
|
|
|
xdmf.attrib={'Version': '2.0',
|
2020-05-05 13:27:22 +05:30
|
|
|
|
'xmlns:xi': 'http://www.w3.org/2001/XInclude'}
|
|
|
|
|
|
2021-04-06 21:09:44 +05:30
|
|
|
|
domain = ET.SubElement(xdmf, 'Domain')
|
2020-05-05 13:27:22 +05:30
|
|
|
|
|
|
|
|
|
collection = ET.SubElement(domain, 'Grid')
|
|
|
|
|
collection.attrib={'GridType': 'Collection',
|
2021-04-10 13:02:21 +05:30
|
|
|
|
'CollectionType': 'Temporal',
|
|
|
|
|
'Name': 'Increments'}
|
2020-05-05 13:27:22 +05:30
|
|
|
|
|
|
|
|
|
time = ET.SubElement(collection, 'Time')
|
|
|
|
|
time.attrib={'TimeType': 'List'}
|
|
|
|
|
|
|
|
|
|
time_data = ET.SubElement(time, 'DataItem')
|
2021-04-05 13:59:34 +05:30
|
|
|
|
times = [self.times[self.increments.index(i)] for i in self.visible['increments']]
|
2020-05-07 22:42:05 +05:30
|
|
|
|
time_data.attrib={'Format': 'XML',
|
|
|
|
|
'NumberType': 'Float',
|
2021-04-05 13:59:34 +05:30
|
|
|
|
'Dimensions': f'{len(times)}'}
|
|
|
|
|
time_data.text = ' '.join(map(str,times))
|
2020-05-05 13:27:22 +05:30
|
|
|
|
|
|
|
|
|
attributes = []
|
|
|
|
|
data_items = []
|
|
|
|
|
|
2021-04-05 19:28:10 +05:30
|
|
|
|
with h5py.File(self.fname,'r') as f:
|
|
|
|
|
for inc in self.visible['increments']:
|
2020-05-05 13:27:22 +05:30
|
|
|
|
|
2021-04-06 21:09:44 +05:30
|
|
|
|
grid = ET.SubElement(collection,'Grid')
|
2021-04-05 19:28:10 +05:30
|
|
|
|
grid.attrib = {'GridType': 'Uniform',
|
|
|
|
|
'Name': inc}
|
2020-05-05 13:27:22 +05:30
|
|
|
|
|
2021-04-06 21:09:44 +05:30
|
|
|
|
topology = ET.SubElement(grid, 'Topology')
|
|
|
|
|
topology.attrib = {'TopologyType': '3DCoRectMesh',
|
2021-06-18 19:47:39 +05:30
|
|
|
|
'Dimensions': '{} {} {}'.format(*(self.cells[::-1]+1))}
|
2020-05-05 13:27:22 +05:30
|
|
|
|
|
2021-04-06 21:09:44 +05:30
|
|
|
|
geometry = ET.SubElement(grid, 'Geometry')
|
|
|
|
|
geometry.attrib = {'GeometryType':'Origin_DxDyDz'}
|
2020-05-05 13:27:22 +05:30
|
|
|
|
|
2021-04-06 21:09:44 +05:30
|
|
|
|
origin = ET.SubElement(geometry, 'DataItem')
|
|
|
|
|
origin.attrib = {'Format': 'XML',
|
|
|
|
|
'NumberType': 'Float',
|
|
|
|
|
'Dimensions': '3'}
|
2021-06-18 19:47:39 +05:30
|
|
|
|
origin.text = "{} {} {}".format(*self.origin[::-1])
|
2020-05-05 13:27:22 +05:30
|
|
|
|
|
2021-04-06 21:09:44 +05:30
|
|
|
|
delta = ET.SubElement(geometry, 'DataItem')
|
|
|
|
|
delta.attrib = {'Format': 'XML',
|
|
|
|
|
'NumberType': 'Float',
|
|
|
|
|
'Dimensions': '3'}
|
2021-06-18 19:47:39 +05:30
|
|
|
|
delta.text="{} {} {}".format(*(self.size/self.cells)[::-1])
|
2020-05-05 13:27:22 +05:30
|
|
|
|
|
|
|
|
|
attributes.append(ET.SubElement(grid, 'Attribute'))
|
2021-04-06 21:09:44 +05:30
|
|
|
|
attributes[-1].attrib = {'Name': 'u / m',
|
|
|
|
|
'Center': 'Node',
|
|
|
|
|
'AttributeType': 'Vector'}
|
2020-05-05 13:27:22 +05:30
|
|
|
|
data_items.append(ET.SubElement(attributes[-1], 'DataItem'))
|
2021-04-06 21:09:44 +05:30
|
|
|
|
data_items[-1].attrib = {'Format': 'HDF',
|
|
|
|
|
'Precision': '8',
|
2021-06-18 19:47:39 +05:30
|
|
|
|
'Dimensions': '{} {} {} 3'.format(*(self.cells[::-1]+1))}
|
2021-04-06 21:09:44 +05:30
|
|
|
|
data_items[-1].text = f'{os.path.split(self.fname)[1]}:/{inc}/geometry/u_n'
|
2020-05-05 13:27:22 +05:30
|
|
|
|
|
2021-04-05 11:23:19 +05:30
|
|
|
|
for ty in ['phase','homogenization']:
|
|
|
|
|
for label in self.visible[ty+'s']:
|
2021-04-14 22:54:45 +05:30
|
|
|
|
for field in _match(self.visible['fields'],f['/'.join([inc,ty,label])].keys()):
|
2021-04-05 13:43:08 +05:30
|
|
|
|
for out in _match(output,f['/'.join([inc,ty,label,field])].keys()):
|
2021-04-05 11:23:19 +05:30
|
|
|
|
name = '/'.join([inc,ty,label,field,out])
|
2020-05-05 13:27:22 +05:30
|
|
|
|
shape = f[name].shape[1:]
|
|
|
|
|
dtype = f[name].dtype
|
|
|
|
|
|
2021-04-14 10:36:24 +05:30
|
|
|
|
unit = f[name].attrs['unit'] if h5py3 else \
|
|
|
|
|
f[name].attrs['unit'].decode()
|
2020-05-05 13:27:22 +05:30
|
|
|
|
|
|
|
|
|
attributes.append(ET.SubElement(grid, 'Attribute'))
|
2021-04-10 13:02:21 +05:30
|
|
|
|
attributes[-1].attrib = {'Name': '/'.join([ty,field,out])+f' / {unit}',
|
2021-04-06 21:09:44 +05:30
|
|
|
|
'Center': 'Cell',
|
|
|
|
|
'AttributeType': attribute_type_map[shape]}
|
2020-05-05 13:27:22 +05:30
|
|
|
|
data_items.append(ET.SubElement(attributes[-1], 'DataItem'))
|
2021-04-06 21:09:44 +05:30
|
|
|
|
data_items[-1].attrib = {'Format': 'HDF',
|
|
|
|
|
'NumberType': number_type_map(dtype),
|
|
|
|
|
'Precision': f'{dtype.itemsize}',
|
2021-06-18 19:47:39 +05:30
|
|
|
|
'Dimensions': '{} {} {} {}'.format(*self.cells[::-1],1 if shape == () else
|
2021-04-06 21:09:44 +05:30
|
|
|
|
np.prod(shape))}
|
|
|
|
|
data_items[-1].text = f'{os.path.split(self.fname)[1]}:{name}'
|
2020-05-05 13:27:22 +05:30
|
|
|
|
|
2021-02-10 14:33:35 +05:30
|
|
|
|
with open(self.fname.with_suffix('.xdmf').name,'w',newline='\n') as f:
|
2020-05-05 13:27:22 +05:30
|
|
|
|
f.write(xml.dom.minidom.parseString(ET.tostring(xdmf).decode()).toprettyxml())
|
|
|
|
|
|
|
|
|
|
|
2021-04-05 19:11:28 +05:30
|
|
|
|
def _mappings(self):
|
2021-04-14 10:36:24 +05:30
|
|
|
|
"""Mappings to place data spatially."""
|
2021-04-05 19:11:28 +05:30
|
|
|
|
with h5py.File(self.fname,'r') as f:
|
|
|
|
|
|
|
|
|
|
at_cell_ph = []
|
|
|
|
|
in_data_ph = []
|
|
|
|
|
for c in range(self.N_constituents):
|
2021-04-25 00:54:26 +05:30
|
|
|
|
at_cell_ph.append({label: np.where(self.phase[:,c] == label)[0] \
|
2021-04-05 19:11:28 +05:30
|
|
|
|
for label in self.visible['phases']})
|
2021-04-14 10:36:24 +05:30
|
|
|
|
in_data_ph.append({label: f['/'.join(['cell_to','phase'])]['entry'][at_cell_ph[c][label]][:,c] \
|
2021-04-05 19:11:28 +05:30
|
|
|
|
for label in self.visible['phases']})
|
|
|
|
|
|
2021-04-25 00:54:26 +05:30
|
|
|
|
at_cell_ho = {label: np.where(self.homogenization[:] == label)[0] \
|
2021-04-05 19:11:28 +05:30
|
|
|
|
for label in self.visible['homogenizations']}
|
2021-04-14 10:36:24 +05:30
|
|
|
|
in_data_ho = {label: f['/'.join(['cell_to','homogenization'])]['entry'][at_cell_ho[label]] \
|
2021-04-05 19:11:28 +05:30
|
|
|
|
for label in self.visible['homogenizations']}
|
|
|
|
|
|
|
|
|
|
return at_cell_ph,in_data_ph,at_cell_ho,in_data_ho
|
|
|
|
|
|
|
|
|
|
|
2021-06-17 21:56:37 +05:30
|
|
|
|
def export_VTK(self,output='*',mode='cell',constituents=None,fill_float=np.nan,fill_int=0,parallel=True):
|
2020-02-21 12:15:05 +05:30
|
|
|
|
"""
|
2021-04-06 21:09:44 +05:30
|
|
|
|
Export to VTK cell/point data.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
2021-04-24 10:43:36 +05:30
|
|
|
|
One VTK file per visible increment is created.
|
2021-06-18 19:47:39 +05:30
|
|
|
|
For point data, the VTK format is poly data (.vtp).
|
2021-06-19 00:41:01 +05:30
|
|
|
|
For cell data, either an image (.vti) or unstructured (.vtu) dataset
|
2021-06-18 19:47:39 +05:30
|
|
|
|
is written for grid-based or mesh-based simulations, respectively.
|
2021-04-24 10:43:36 +05:30
|
|
|
|
|
2020-02-21 12:15:05 +05:30
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2021-04-06 21:09:44 +05:30
|
|
|
|
output : (list of) str, optional
|
2021-04-24 22:42:44 +05:30
|
|
|
|
Names of the datasets to export to the VTK file.
|
2021-04-06 21:09:44 +05:30
|
|
|
|
Defaults to '*', in which case all datasets are exported.
|
|
|
|
|
mode : {'cell', 'point'}
|
2020-03-19 12:15:31 +05:30
|
|
|
|
Export in cell format or point format.
|
|
|
|
|
Defaults to 'cell'.
|
2021-04-06 21:09:44 +05:30
|
|
|
|
constituents : (list of) int, optional
|
|
|
|
|
Constituents to consider.
|
|
|
|
|
Defaults to None, in which case all constituents are considered.
|
2021-04-02 15:51:27 +05:30
|
|
|
|
fill_float : float
|
|
|
|
|
Fill value for non-existent entries of floating point type.
|
2021-04-06 21:09:44 +05:30
|
|
|
|
Defaults to NaN.
|
2021-04-02 15:51:27 +05:30
|
|
|
|
fill_int : int
|
|
|
|
|
Fill value for non-existent entries of integer type.
|
|
|
|
|
Defaults to 0.
|
2021-04-05 13:59:34 +05:30
|
|
|
|
parallel : bool
|
2021-06-18 19:47:39 +05:30
|
|
|
|
Write VTK files in parallel in a separate background process.
|
2021-04-05 13:59:34 +05:30
|
|
|
|
Defaults to True.
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
|
|
|
|
"""
|
2020-02-21 17:33:50 +05:30
|
|
|
|
if mode.lower()=='cell':
|
2021-04-03 12:28:22 +05:30
|
|
|
|
v = self.geometry0
|
2020-02-21 23:22:58 +05:30
|
|
|
|
elif mode.lower()=='point':
|
2020-12-04 03:30:49 +05:30
|
|
|
|
v = VTK.from_poly_data(self.coordinates0_point)
|
2021-06-01 10:19:14 +05:30
|
|
|
|
else:
|
|
|
|
|
raise ValueError(f'invalid mode {mode}')
|
|
|
|
|
|
2021-06-17 21:56:37 +05:30
|
|
|
|
v.set_comments(util.execution_stamp('Result','export_VTK'))
|
2020-03-12 03:05:58 +05:30
|
|
|
|
|
2021-04-14 10:36:24 +05:30
|
|
|
|
N_digits = int(np.floor(np.log10(max(1,int(self.increments[-1][10:])))))+1
|
2020-02-21 12:15:05 +05:30
|
|
|
|
|
2021-04-02 15:51:27 +05:30
|
|
|
|
constituents_ = constituents if isinstance(constituents,Iterable) else \
|
|
|
|
|
(range(self.N_constituents) if constituents is None else [constituents])
|
|
|
|
|
|
|
|
|
|
suffixes = [''] if self.N_constituents == 1 or isinstance(constituents,int) else \
|
|
|
|
|
[f'#{c}' for c in constituents_]
|
|
|
|
|
|
2021-04-05 19:11:28 +05:30
|
|
|
|
at_cell_ph,in_data_ph,at_cell_ho,in_data_ho = self._mappings()
|
2021-04-02 15:51:27 +05:30
|
|
|
|
|
|
|
|
|
with h5py.File(self.fname,'r') as f:
|
2021-04-26 02:22:13 +05:30
|
|
|
|
if self.version_minor >= 13:
|
|
|
|
|
creator = f.attrs['creator'] if h5py3 else f.attrs['creator'].decode()
|
|
|
|
|
created = f.attrs['created'] if h5py3 else f.attrs['created'].decode()
|
|
|
|
|
v.add_comments(f'{creator} ({created})')
|
2021-04-02 15:51:27 +05:30
|
|
|
|
|
|
|
|
|
for inc in util.show_progress(self.visible['increments']):
|
|
|
|
|
|
2021-04-05 13:43:08 +05:30
|
|
|
|
u = _read(f['/'.join([inc,'geometry','u_n' if mode.lower() == 'cell' else 'u_p'])])
|
2021-04-02 15:51:27 +05:30
|
|
|
|
v.add(u,'u')
|
|
|
|
|
|
|
|
|
|
for ty in ['phase','homogenization']:
|
|
|
|
|
for field in self.visible['fields']:
|
2021-04-08 21:06:33 +05:30
|
|
|
|
outs = {}
|
2021-04-02 15:51:27 +05:30
|
|
|
|
for label in self.visible[ty+'s']:
|
2021-04-05 13:43:08 +05:30
|
|
|
|
if field not in f['/'.join([inc,ty,label])].keys(): continue
|
2021-04-02 15:51:27 +05:30
|
|
|
|
|
2021-04-05 13:43:08 +05:30
|
|
|
|
for out in _match(output,f['/'.join([inc,ty,label,field])].keys()):
|
|
|
|
|
data = ma.array(_read(f['/'.join([inc,ty,label,field,out])]))
|
2021-04-02 15:51:27 +05:30
|
|
|
|
|
|
|
|
|
if ty == 'phase':
|
|
|
|
|
if out+suffixes[0] not in outs.keys():
|
|
|
|
|
for c,suffix in zip(constituents_,suffixes):
|
|
|
|
|
outs[out+suffix] = \
|
2021-04-06 21:09:44 +05:30
|
|
|
|
_empty_like(data,self.N_materialpoints,fill_float,fill_int)
|
2021-04-02 15:51:27 +05:30
|
|
|
|
|
|
|
|
|
for c,suffix in zip(constituents_,suffixes):
|
|
|
|
|
outs[out+suffix][at_cell_ph[c][label]] = data[in_data_ph[c][label]]
|
|
|
|
|
|
|
|
|
|
if ty == 'homogenization':
|
|
|
|
|
if out not in outs.keys():
|
2021-04-06 21:09:44 +05:30
|
|
|
|
outs[out] = _empty_like(data,self.N_materialpoints,fill_float,fill_int)
|
2020-03-22 20:43:35 +05:30
|
|
|
|
|
2021-04-02 15:51:27 +05:30
|
|
|
|
outs[out][at_cell_ho[label]] = data[in_data_ho[label]]
|
2020-03-22 20:43:35 +05:30
|
|
|
|
|
2021-04-02 15:51:27 +05:30
|
|
|
|
for label,dataset in outs.items():
|
2021-04-05 13:43:08 +05:30
|
|
|
|
v.add(dataset,' / '.join(['/'.join([ty,field,label]),dataset.dtype.metadata['unit']]))
|
2020-03-22 20:43:35 +05:30
|
|
|
|
|
2021-04-14 10:36:24 +05:30
|
|
|
|
v.save(f'{self.fname.stem}_inc{inc[10:].zfill(N_digits)}',parallel=parallel)
|
2021-03-30 10:54:47 +05:30
|
|
|
|
|
|
|
|
|
|
2021-04-06 21:09:44 +05:30
|
|
|
|
def get(self,output='*',flatten=True,prune=True):
|
2021-03-31 20:41:53 +05:30
|
|
|
|
"""
|
2021-04-06 21:09:44 +05:30
|
|
|
|
Collect data per phase/homogenization reflecting the group/folder structure in the DADF5 file.
|
2021-03-31 20:41:53 +05:30
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2021-04-06 21:09:44 +05:30
|
|
|
|
output : (list of) str
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Names of the datasets to read.
|
2021-04-06 21:09:44 +05:30
|
|
|
|
Defaults to '*', in which case all datasets are read.
|
2021-04-02 03:03:45 +05:30
|
|
|
|
flatten : bool
|
|
|
|
|
Remove singular levels of the folder hierarchy.
|
2021-04-02 12:26:14 +05:30
|
|
|
|
This might be beneficial in case of single increment,
|
|
|
|
|
phase/homogenization, or field. Defaults to True.
|
2021-04-02 03:03:45 +05:30
|
|
|
|
prune : bool
|
|
|
|
|
Remove branches with no data. Defaults to True.
|
2021-03-31 20:41:53 +05:30
|
|
|
|
|
2021-04-06 21:09:44 +05:30
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
data : dict of numpy.ndarray
|
|
|
|
|
Datasets structured by phase/homogenization and according to selected view.
|
|
|
|
|
|
2021-03-31 20:41:53 +05:30
|
|
|
|
"""
|
2021-03-30 10:54:47 +05:30
|
|
|
|
r = {}
|
2021-03-31 18:49:23 +05:30
|
|
|
|
|
2021-03-30 10:54:47 +05:30
|
|
|
|
with h5py.File(self.fname,'r') as f:
|
2021-03-30 23:11:36 +05:30
|
|
|
|
for inc in util.show_progress(self.visible['increments']):
|
2021-03-31 15:35:51 +05:30
|
|
|
|
r[inc] = {'phase':{},'homogenization':{},'geometry':{}}
|
2021-03-31 18:49:23 +05:30
|
|
|
|
|
2021-04-05 13:43:08 +05:30
|
|
|
|
for out in _match(output,f['/'.join([inc,'geometry'])].keys()):
|
|
|
|
|
r[inc]['geometry'][out] = _read(f['/'.join([inc,'geometry',out])])
|
2021-04-01 18:26:17 +05:30
|
|
|
|
|
|
|
|
|
for ty in ['phase','homogenization']:
|
2021-04-01 19:22:43 +05:30
|
|
|
|
for label in self.visible[ty+'s']:
|
|
|
|
|
r[inc][ty][label] = {}
|
2021-04-05 13:43:08 +05:30
|
|
|
|
for field in _match(self.visible['fields'],f['/'.join([inc,ty,label])].keys()):
|
2021-04-01 19:22:43 +05:30
|
|
|
|
r[inc][ty][label][field] = {}
|
2021-04-05 13:43:08 +05:30
|
|
|
|
for out in _match(output,f['/'.join([inc,ty,label,field])].keys()):
|
|
|
|
|
r[inc][ty][label][field][out] = _read(f['/'.join([inc,ty,label,field,out])])
|
2021-03-30 23:11:36 +05:30
|
|
|
|
|
2021-04-02 03:03:45 +05:30
|
|
|
|
if prune: r = util.dict_prune(r)
|
|
|
|
|
if flatten: r = util.dict_flatten(r)
|
2021-04-01 18:26:17 +05:30
|
|
|
|
|
2021-04-10 13:03:45 +05:30
|
|
|
|
return None if (type(r) == dict and r == {}) else r
|
2021-03-30 10:54:47 +05:30
|
|
|
|
|
|
|
|
|
|
2021-04-06 21:09:44 +05:30
|
|
|
|
def place(self,output='*',flatten=True,prune=True,constituents=None,fill_float=np.nan,fill_int=0):
|
2021-03-31 20:41:53 +05:30
|
|
|
|
"""
|
2021-04-06 21:09:44 +05:30
|
|
|
|
Merge data into spatial order that is compatible with the damask.VTK geometry representation.
|
2021-03-31 20:41:53 +05:30
|
|
|
|
|
|
|
|
|
The returned data structure reflects the group/folder structure
|
2021-04-02 03:03:45 +05:30
|
|
|
|
in the DADF5 file.
|
2021-03-31 20:41:53 +05:30
|
|
|
|
|
2021-04-02 03:03:45 +05:30
|
|
|
|
Multi-phase data is fused into a single output.
|
2021-04-24 22:42:44 +05:30
|
|
|
|
`place` is equivalent to `get` if only one phase/homogenization
|
2021-04-02 15:51:27 +05:30
|
|
|
|
and one constituent is present.
|
2021-03-31 20:41:53 +05:30
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
2021-04-06 21:09:44 +05:30
|
|
|
|
output : (list of) str, optional
|
2021-04-24 10:43:36 +05:30
|
|
|
|
Names of the datasets to read.
|
2021-04-06 21:09:44 +05:30
|
|
|
|
Defaults to '*', in which case all datasets are placed.
|
2021-04-02 03:03:45 +05:30
|
|
|
|
flatten : bool
|
|
|
|
|
Remove singular levels of the folder hierarchy.
|
2021-04-06 21:09:44 +05:30
|
|
|
|
This might be beneficial in case of single increment or field.
|
|
|
|
|
Defaults to True.
|
2021-04-02 03:03:45 +05:30
|
|
|
|
prune : bool
|
|
|
|
|
Remove branches with no data. Defaults to True.
|
2021-04-06 21:09:44 +05:30
|
|
|
|
constituents : (list of) int, optional
|
|
|
|
|
Constituents to consider.
|
2021-04-24 22:42:44 +05:30
|
|
|
|
Defaults to None, in which case all constituents are considered.
|
2021-04-01 03:32:51 +05:30
|
|
|
|
fill_float : float
|
2021-04-02 03:03:45 +05:30
|
|
|
|
Fill value for non-existent entries of floating point type.
|
2021-04-06 21:09:44 +05:30
|
|
|
|
Defaults to NaN.
|
2021-04-01 03:32:51 +05:30
|
|
|
|
fill_int : int
|
2021-04-02 03:03:45 +05:30
|
|
|
|
Fill value for non-existent entries of integer type.
|
2021-04-01 03:32:51 +05:30
|
|
|
|
Defaults to 0.
|
2021-04-01 19:22:43 +05:30
|
|
|
|
|
2021-04-08 18:28:22 +05:30
|
|
|
|
Returns
|
|
|
|
|
-------
|
|
|
|
|
data : dict of numpy.ma.MaskedArray
|
|
|
|
|
Datasets structured by spatial position and according to selected view.
|
|
|
|
|
|
2021-03-31 20:41:53 +05:30
|
|
|
|
"""
|
2021-03-30 10:54:47 +05:30
|
|
|
|
r = {}
|
|
|
|
|
|
2021-04-02 11:17:03 +05:30
|
|
|
|
constituents_ = constituents if isinstance(constituents,Iterable) else \
|
|
|
|
|
(range(self.N_constituents) if constituents is None else [constituents])
|
2021-03-30 10:54:47 +05:30
|
|
|
|
|
2021-04-01 19:22:43 +05:30
|
|
|
|
suffixes = [''] if self.N_constituents == 1 or isinstance(constituents,int) else \
|
2021-03-31 20:41:53 +05:30
|
|
|
|
[f'#{c}' for c in constituents_]
|
2021-03-30 10:54:47 +05:30
|
|
|
|
|
2021-04-05 19:11:28 +05:30
|
|
|
|
at_cell_ph,in_data_ph,at_cell_ho,in_data_ho = self._mappings()
|
2021-03-30 10:54:47 +05:30
|
|
|
|
|
|
|
|
|
with h5py.File(self.fname,'r') as f:
|
|
|
|
|
|
2021-03-31 11:24:23 +05:30
|
|
|
|
for inc in util.show_progress(self.visible['increments']):
|
2021-03-31 15:35:51 +05:30
|
|
|
|
r[inc] = {'phase':{},'homogenization':{},'geometry':{}}
|
2021-03-31 18:49:23 +05:30
|
|
|
|
|
2021-04-05 13:43:08 +05:30
|
|
|
|
for out in _match(output,f['/'.join([inc,'geometry'])].keys()):
|
2021-04-06 01:48:18 +05:30
|
|
|
|
r[inc]['geometry'][out] = ma.array(_read(f['/'.join([inc,'geometry',out])]),fill_value = fill_float)
|
2021-03-31 11:24:23 +05:30
|
|
|
|
|
2021-04-01 19:22:43 +05:30
|
|
|
|
for ty in ['phase','homogenization']:
|
|
|
|
|
for label in self.visible[ty+'s']:
|
2021-04-05 13:43:08 +05:30
|
|
|
|
for field in _match(self.visible['fields'],f['/'.join([inc,ty,label])].keys()):
|
2021-04-01 19:22:43 +05:30
|
|
|
|
if field not in r[inc][ty].keys():
|
|
|
|
|
r[inc][ty][field] = {}
|
|
|
|
|
|
2021-04-05 13:43:08 +05:30
|
|
|
|
for out in _match(output,f['/'.join([inc,ty,label,field])].keys()):
|
|
|
|
|
data = ma.array(_read(f['/'.join([inc,ty,label,field,out])]))
|
2021-04-01 19:22:43 +05:30
|
|
|
|
|
|
|
|
|
if ty == 'phase':
|
|
|
|
|
if out+suffixes[0] not in r[inc][ty][field].keys():
|
2021-04-02 11:17:03 +05:30
|
|
|
|
for c,suffix in zip(constituents_,suffixes):
|
2021-04-01 19:22:43 +05:30
|
|
|
|
r[inc][ty][field][out+suffix] = \
|
2021-04-06 21:09:44 +05:30
|
|
|
|
_empty_like(data,self.N_materialpoints,fill_float,fill_int)
|
2021-04-01 19:22:43 +05:30
|
|
|
|
|
2021-04-02 12:26:14 +05:30
|
|
|
|
for c,suffix in zip(constituents_,suffixes):
|
2021-04-01 19:22:43 +05:30
|
|
|
|
r[inc][ty][field][out+suffix][at_cell_ph[c][label]] = data[in_data_ph[c][label]]
|
|
|
|
|
|
|
|
|
|
if ty == 'homogenization':
|
|
|
|
|
if out not in r[inc][ty][field].keys():
|
|
|
|
|
r[inc][ty][field][out] = \
|
2021-04-06 21:09:44 +05:30
|
|
|
|
_empty_like(data,self.N_materialpoints,fill_float,fill_int)
|
2021-04-01 19:22:43 +05:30
|
|
|
|
|
|
|
|
|
r[inc][ty][field][out][at_cell_ho[label]] = data[in_data_ho[label]]
|
2021-03-31 18:49:23 +05:30
|
|
|
|
|
2021-04-02 03:03:45 +05:30
|
|
|
|
if prune: r = util.dict_prune(r)
|
|
|
|
|
if flatten: r = util.dict_flatten(r)
|
2021-04-01 19:22:43 +05:30
|
|
|
|
|
2021-04-10 13:03:45 +05:30
|
|
|
|
return None if (type(r) == dict and r == {}) else r
|
2021-07-27 13:01:57 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def export_setup(self,output='*',overwrite=False):
|
|
|
|
|
"""
|
|
|
|
|
Export configuration files.
|
|
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
|
----------
|
|
|
|
|
output : (list of) str, optional
|
|
|
|
|
Names of the datasets to export to the file.
|
|
|
|
|
Defaults to '*', in which case all datasets are exported.
|
2022-01-13 03:43:38 +05:30
|
|
|
|
overwrite : bool, optional
|
2021-07-27 13:01:57 +05:30
|
|
|
|
Overwrite existing configuration files.
|
|
|
|
|
Defaults to False.
|
|
|
|
|
|
|
|
|
|
"""
|
2021-08-02 14:08:59 +05:30
|
|
|
|
def export(name,obj,output,overwrite):
|
|
|
|
|
if type(obj) == h5py.Dataset and _match(output,[name]):
|
|
|
|
|
d = obj.attrs['description'] if h5py3 else obj.attrs['description'].decode()
|
|
|
|
|
if not Path(name).exists() or overwrite:
|
2021-08-14 19:58:51 +05:30
|
|
|
|
with open(name,'w') as f_out: f_out.write(obj[0].decode())
|
2021-08-02 14:08:59 +05:30
|
|
|
|
print(f"Exported {d} to '{name}'.")
|
2021-07-27 13:01:57 +05:30
|
|
|
|
else:
|
2021-08-02 14:08:59 +05:30
|
|
|
|
print(f"'{name}' exists, {d} not exported.")
|
|
|
|
|
elif type(obj) == h5py.Group:
|
|
|
|
|
os.makedirs(name, exist_ok=True)
|
|
|
|
|
|
|
|
|
|
with h5py.File(self.fname,'r') as f_in:
|
|
|
|
|
f_in['setup'].visititems(partial(export,output=output,overwrite=overwrite))
|