out of place behavior

This commit is contained in:
Martin Diehl 2021-04-05 07:53:19 +02:00
parent fc409fcf08
commit 62c85db745
2 changed files with 84 additions and 182 deletions

View File

@ -2,6 +2,7 @@ import multiprocessing as mp
import re
import fnmatch
import os
import copy
import datetime
import xml.etree.ElementTree as ET
import xml.dom.minidom
@ -27,11 +28,13 @@ h5py3 = h5py.__version__[0] == '3'
def _read(dataset):
"""Read a dataset and its metadata into a numpy.ndarray."""
metadata = {k:(v if h5py3 else v.decode()) for k,v in dataset.attrs.items()}
dtype = np.dtype(dataset.dtype,metadata=metadata)
return np.array(dataset,dtype=dtype)
def _match(requested,existing):
"""Find matches among two sets of labels"""
def flatten_list(list_of_lists):
return [e for e_ in list_of_lists for e in e_]
@ -47,6 +50,7 @@ def _match(requested,existing):
key=util.natural_sort)
def _empty(dataset,N_materialpoints,fill_float,fill_int):
"""Create empty numpy.ma.MaskedArray."""
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)
@ -123,17 +127,21 @@ class Result:
self._allow_modification = False
def __copy__(self):
"""Create deep copy."""
return copy.deepcopy(self)
copy = __copy__
def __repr__(self):
"""Show summary of file content."""
visible_increments = self.visible['increments']
self.view('increments',visible_increments[0:1])
first = self.list_data()
first = self.view('increments',visible_increments[0:1]).list_data()
self.view('increments',visible_increments[-1:])
last = '' if len(visible_increments) < 2 else self.list_data()
self.view('increments',visible_increments)
last = '' if len(visible_increments) < 2 else \
self.view('increments',visible_increments[-1:]).list_data()
in_between = '' if len(visible_increments) < 3 else \
''.join([f'\n{inc}\n ...\n' for inc in visible_increments[1:-1]])
@ -186,24 +194,31 @@ class Result:
valid = _match(choice,getattr(self,what))
existing = set(self.visible[what])
dup = self.copy()
if action == 'set':
self.visible[what] = sorted(set(valid), key=util.natural_sort)
dup.visible[what] = sorted(set(valid), key=util.natural_sort)
elif action == 'add':
add = existing.union(valid)
self.visible[what] = sorted(add, key=util.natural_sort)
dup.visible[what] = sorted(add, key=util.natural_sort)
elif action == 'del':
diff = existing.difference(valid)
self.visible[what] = sorted(diff, key=util.natural_sort)
dup.visible[what] = sorted(diff, key=util.natural_sort)
return dup
def allow_modification(self):
"""Allow to overwrite existing data."""
print(util.warn('Warning: Modification of existing datasets allowed!'))
self._allow_modification = True
dup = self.copy()
dup._allow_modification = True
return dup
def disallow_modification(self):
"""Disallow to overwrite existing data (default case)."""
self._allow_modification = False
dup = self.copy()
dup._allow_modification = False
return dup
def increments_in_range(self,start,end):
@ -247,28 +262,6 @@ class Result:
return selected
def iterate(self,what):
"""
Iterate over visible items and view them independently.
Parameters
----------
what : str
Attribute to change (must be from self.visible).
"""
datasets = self.visible[what]
last_view = datasets.copy()
for dataset in datasets:
if last_view != self.visible[what]:
self._manage_view('set',what,datasets)
raise Exception
self._manage_view('set',what,dataset)
last_view = self.visible[what]
yield dataset
self._manage_view('set',what,datasets)
def view(self,what,datasets):
"""
Set view.
@ -279,10 +272,10 @@ class Result:
Attribute to change (must be from self.visible).
datasets : list of str or bool
Name of datasets as list; supports ? and * wildcards.
True is equivalent to [*], False is equivalent to [].
True is equivalent to *, False is equivalent to [].
"""
self._manage_view('set',what,datasets)
return self._manage_view('set',what,datasets)
def view_more(self,what,datasets):
@ -295,10 +288,10 @@ class Result:
Attribute to change (must be from self.visible).
datasets : list of str or bool
Name of datasets as list; supports ? and * wildcards.
True is equivalent to [*], False is equivalent to [].
True is equivalent to *, False is equivalent to [].
"""
self._manage_view('add',what,datasets)
return self._manage_view('add',what,datasets)
def view_less(self,what,datasets):
@ -311,10 +304,10 @@ class Result:
Attribute to change (must be from self.visible).
datasets : list of str or bool
Name of datasets as list; supports ? and * wildcards.
True is equivalent to [*], False is equivalent to [].
True is equivalent to *, False is equivalent to [].
"""
self._manage_view('del',what,datasets)
return self._manage_view('del',what,datasets)
def rename(self,name_old,name_new):
@ -334,11 +327,11 @@ class Result:
with h5py.File(self.fname,'a') as f:
for inc in self.visible['increments']:
for ty in ['phases','homogenizations']:
for label in self.visible[ty]:
for ty in ['phase','homogenization']:
for label in self.visible[ty+'s']:
for field in self.visible['fields']:
path_old = '/'.join([inc,ty[:-1],label,field,name_old])
path_new = '/'.join([inc,ty[:-1],label,field,name_new])
path_old = '/'.join([inc,ty,label,field,name_old])
path_new = '/'.join([inc,ty,label,field,name_new])
if path_old in f.keys():
f[path_new] = f[path_old]
f[path_new].attrs['renamed'] = f'original name: {name_old}' if h5py3 else \
@ -351,48 +344,25 @@ class Result:
# compatibility hack
de = 'Description' if self.version_minor < 12 else 'description'
un = 'Unit' if self.version_minor < 12 else 'unit'
message = ''
msg = ''
with h5py.File(self.fname,'r') as f:
for inc in self.visible['increments']:
''.join([message,f'\n{inc} ({self.times[self.increments.index(inc)]}s)\n'])
for ty in ['phases','homogenizations']:
' '.join([message,f'{ty[:-1]}\n'])
for label in self.visible[ty]:
' '.join([message,f'{label}\n'])
msg = ''.join([msg,f'\n{inc} ({self.times[self.increments.index(inc)]}s)\n'])
for ty in ['phase','homogenization']:
msg = ' '.join([msg,f'{ty}\n'])
for label in self.visible[ty+'s']:
msg = ' '.join([msg,f'{label}\n'])
for field in self.visible['fields']:
' '.join([message,f'{field}\n'])
for d in f['/'.join([inc,ty[:-1],label,field])].keys():
dataset = f['/'.join([inc,ty[:-1],label,field,d])]
msg = ' '.join([msg,f'{field}\n'])
for d in f['/'.join([inc,ty,label,field])].keys():
dataset = f['/'.join([inc,ty,label,field,d])]
unit = f' / {dataset.attrs[un]}' if h5py3 else \
f' / {dataset.attrs[un].decode()}'
description = dataset.attrs[de] if h5py3 else \
dataset.attrs[de].decode()
' '.join([message,f'{d}{unit}: {description}\n'])
msg = ' '.join([msg,f'{d}{unit}: {description}\n'])
return message
def get_dataset_location(self,label):
"""Return the location of all active datasets with given label."""
path = []
with h5py.File(self.fname,'r') as f:
for i in self.visible['increments']:
k = '/'.join([i,'geometry',label])
try:
f[k]
path.append(k)
except KeyError:
pass
for o,p in zip(['phases','homogenizations'],['fields','fields']):
for oo in self.visible[o]:
for pp in self.visible[p]:
k = '/'.join([i,o[:-1],oo,pp,label])
try:
f[k]
path.append(k)
except KeyError:
pass
return path
return msg
def enable_user_function(self,func):
@ -400,60 +370,6 @@ class Result:
print(f'Function {func.__name__} enabled in add_calculation.')
def read_dataset(self,path,c=0,plain=False):
"""
Dataset for all points/cells.
If more than one path is given, the dataset is composed of the individual contributions.
Parameters
----------
path : list of strings
The name of the datasets to consider.
c : int, optional
The constituent to consider. Defaults to 0.
plain: boolean, optional
Convert into plain numpy datatype.
Only relevant for compound datatype, e.g. the orientation.
Defaults to False.
"""
# compatibility hack
name = 'Name' if self.version_minor < 12 else 'label'
member = 'Position' if self.version_minor < 12 else 'entry'
grp = 'mapping' if self.version_minor < 12 else 'cell_to'
with h5py.File(self.fname,'r') as f:
shape = (self.N_materialpoints,) + np.shape(f[path[0]])[1:]
if len(shape) == 1: shape = shape +(1,)
dataset = np.full(shape,np.nan,dtype=np.dtype(f[path[0]]))
for pa in path:
label = pa.split('/')[2]
if pa.split('/')[1] == 'geometry':
dataset = np.array(f[pa])
continue
p = np.where(f[f'{grp}/phase'][:,c][name] == str.encode(label))[0]
if len(p)>0:
u = (f[f'{grp}/phase'][member][p,c])
a = np.array(f[pa])
if len(a.shape) == 1:
a=a.reshape([a.shape[0],1])
dataset[p,:] = a[u,:]
p = np.where(f[f'{grp}/homogenization'][name] == str.encode(label))[0]
if len(p)>0:
u = (f[f'{grp}/homogenization'][member][p.tolist()])
a = np.array(f[pa])
if len(a.shape) == 1:
a=a.reshape([a.shape[0],1])
dataset[p,:] = a[u,:]
if plain and dataset.dtype.names is not None:
return dataset.view(('float64',len(dataset.dtype.names)))
else:
return dataset
@property
def coordinates0_point(self):
"""Return initial coordinates of the cell centers."""
@ -1044,10 +960,10 @@ class Result:
groups = []
with h5py.File(self.fname,'r') as f:
for inc in self.visible['increments']:
for ty in ['phases','homogenizations']:
for label in self.visible[ty]:
for ty in ['phase','homogenization']:
for label in self.visible[ty+'s']:
for field in self.visible['fields']:
group = '/'.join([inc,ty[:-1],label,field])
group = '/'.join([inc,ty,label,field])
if set(datasets.values()).issubset(f[group].keys()): groups.append(group)
if len(groups) == 0:
@ -1176,11 +1092,11 @@ class Result:
'Dimensions': '{} {} {} 3'.format(*(self.cells+1))}
data_items[-1].text=f'{os.path.split(self.fname)[1]}:/{inc}/geometry/u_n'
for ty in ['phases','homogenizations']:
for label in self.visible[ty]:
for ty in ['phase','homogenization']:
for label in self.visible[ty+'s']:
for field in self.visible['fields']:
for out in _match(output,f['/'.join((inc,ty[:-1],label,field))].keys()):
name = '/'.join([inc,ty[:-1],label,field,out])
for out in _match(output,f['/'.join((inc,ty,label,field))].keys()):
name = '/'.join([inc,ty,label,field,out])
shape = f[name].shape[1:]
dtype = f[name].dtype

View File

@ -24,8 +24,7 @@ def default(tmp_path,ref_path):
fname = '12grains6x7x8_tensionY.hdf5'
shutil.copy(ref_path/fname,tmp_path)
f = Result(tmp_path/fname)
f.view('times',20.0)
return f
return f.view('times',20.0)
@pytest.fixture
def single_phase(tmp_path,ref_path):
@ -58,49 +57,37 @@ class TestResult:
def test_view_all(self,default):
default.view('increments',True)
a = default.read('F')
a = default.view('increments',True).read('F')
default.view('increments','*')
assert dict_equal(a,default.read('F'))
default.view('increments',default.increments_in_range(0,np.iinfo(int).max))
assert dict_equal(a,default.read('F'))
assert dict_equal(a,default.view('increments','*').read('F'))
assert dict_equal(a,default.view('increments',default.increments_in_range(0,np.iinfo(int).max)).read('F'))
default.view('times',True)
assert dict_equal(a,default.read('F'))
default.view('times','*')
assert dict_equal(a,default.read('F'))
default.view('times',default.times_in_range(0.0,np.inf))
assert dict_equal(a,default.read('F'))
assert dict_equal(a,default.view('times',True).read('F'))
assert dict_equal(a,default.view('times','*').read('F'))
assert dict_equal(a,default.view('times',default.times_in_range(0.0,np.inf)).read('F'))
@pytest.mark.parametrize('what',['increments','times','phases']) # ToDo: discuss homogenizations
def test_view_none(self,default,what):
default.view(what,False)
a = default.read('F')
default.view(what,[])
b = default.read('F')
a = default.view(what,False).read('F')
b = default.view(what,[]).read('F')
assert a == b == {}
@pytest.mark.parametrize('what',['increments','times','phases']) # ToDo: discuss homogenizations
def test_view_more(self,default,what):
default.view(what,False)
default.view_more(what,'*')
a = default.read('F')
empty = default.view(what,False)
default.view(what,True)
b = default.read('F')
a = empty.view_more(what,'*').read('F')
b = empty.view_more(what,True).read('F')
assert dict_equal(a,b)
@pytest.mark.parametrize('what',['increments','times','phases']) # ToDo: discuss homogenizations
def test_view_less(self,default,what):
default.view(what,True)
default.view_less(what,'*')
a = default.read('F')
full = default.view(what,True)
default.view(what,False)
b = default.read('F')
a = full.view_less(what,'*').read('F')
b = full.view_less(what,True).read('F')
assert a == b == {}
@ -279,41 +266,41 @@ class TestResult:
@pytest.mark.parametrize('overwrite',['off','on'])
def test_add_overwrite(self,default,overwrite):
default.view('times',default.times_in_range(0,np.inf)[-1])
last = default.view('times',default.times_in_range(0,np.inf)[-1])
default.add_stress_Cauchy()
with h5py.File(default.fname,'r') as f:
last.add_stress_Cauchy()
with h5py.File(last.fname,'r') as f:
created_first = default.place('sigma').dtype.metadata['created']
created_first = datetime.strptime(created_first,'%Y-%m-%d %H:%M:%S%z')
if overwrite == 'on':
default.allow_modification()
last = last.allow_modification()
else:
default.disallow_modification()
last = last.disallow_modification()
time.sleep(2.)
try:
default.add_calculation('sigma','#sigma#*0.0+311.','not the Cauchy stress')
last.add_calculation('sigma','#sigma#*0.0+311.','not the Cauchy stress')
except ValueError:
pass
with h5py.File(default.fname,'r') as f:
created_second = default.place('sigma').dtype.metadata['created']
with h5py.File(last.fname,'r') as f:
created_second = last.place('sigma').dtype.metadata['created']
created_second = datetime.strptime(created_second,'%Y-%m-%d %H:%M:%S%z')
if overwrite == 'on':
assert created_first < created_second and np.allclose(default.place('sigma'),311.)
assert created_first < created_second and np.allclose(last.place('sigma'),311.)
else:
assert created_first == created_second and not np.allclose(default.place('sigma'),311.)
assert created_first == created_second and not np.allclose(last.place('sigma'),311.)
@pytest.mark.parametrize('allowed',['off','on'])
def test_rename(self,default,allowed):
if allowed == 'on':
F = default.place('F')
default.allow_modification()
default = default.allow_modification()
default.rename('F','new_name')
assert np.all(F == default.place('new_name'))
default.disallow_modification()
default = default.disallow_modification()
with pytest.raises(PermissionError):
default.rename('P','another_new_name')
@ -333,8 +320,7 @@ class TestResult:
@pytest.mark.parametrize('fname',['12grains6x7x8_tensionY.hdf5'],ids=range(1))
@pytest.mark.parametrize('inc',[4,0],ids=range(2))
def test_vtk(self,request,tmp_path,ref_path,update,output,fname,inc):
result = Result(ref_path/fname)
result.view('increments',inc)
result = Result(ref_path/fname).view('increments',inc)
os.chdir(tmp_path)
result.save_VTK(output)
fname = fname.split('.')[0]+f'_inc{(inc if type(inc) == int else inc[0]):0>2}.vtr'
@ -387,7 +373,7 @@ class TestResult:
def test_read(self,update,request,ref_path,view,output,compress,strip):
result = Result(ref_path/'4grains2x4x3_compressionY.hdf5')
for key,value in view.items():
result.view(key,value)
result = result.view(key,value)
fname = request.node.name
cur = result.read(output,compress,strip)
@ -412,7 +398,7 @@ class TestResult:
def test_place(self,update,request,ref_path,view,output,compress,strip,constituents):
result = Result(ref_path/'4grains2x4x3_compressionY.hdf5')
for key,value in view.items():
result.view(key,value)
result = result.view(key,value)
fname = request.node.name
cur = result.place(output,compress,strip,constituents)