Merge branch 'development' into Fortran-cleaning
This commit is contained in:
commit
d47c116ebd
|
@ -136,8 +136,7 @@ class Orientation(Rotation):
|
|||
|
||||
Rotation.__init__(self) if rotation is None else Rotation.__init__(self,rotation=rotation)
|
||||
|
||||
if ( lattice is not None
|
||||
and lattice not in self.lattice_symmetries
|
||||
if ( lattice not in self.lattice_symmetries
|
||||
and lattice not in self.crystal_families):
|
||||
raise KeyError(f'Lattice "{lattice}" is unknown')
|
||||
|
||||
|
@ -206,7 +205,7 @@ class Orientation(Rotation):
|
|||
def __repr__(self):
|
||||
"""Represent."""
|
||||
return '\n'.join(([] if self.lattice is None else [f'Bravais lattice {self.lattice}'])
|
||||
+ ([] if self.family is None else [f'Crystal family {self.family}'])
|
||||
+ ([f'Crystal family {self.family}'])
|
||||
+ [super().__repr__()])
|
||||
|
||||
|
||||
|
@ -239,7 +238,7 @@ class Orientation(Rotation):
|
|||
"""
|
||||
matching_type = all([hasattr(other,attr) and getattr(self,attr) == getattr(other,attr)
|
||||
for attr in ['family','lattice','parameters']])
|
||||
return np.logical_and(super().__eq__(other),matching_type)
|
||||
return np.logical_and(matching_type,super(self.__class__,self.reduced).__eq__(other.reduced))
|
||||
|
||||
def __ne__(self,other):
|
||||
"""
|
||||
|
@ -254,6 +253,57 @@ class Orientation(Rotation):
|
|||
return np.logical_not(self==other)
|
||||
|
||||
|
||||
def isclose(self,other,rtol=1e-5,atol=1e-8,equal_nan=True):
|
||||
"""
|
||||
Report where values are approximately equal to corresponding ones of other Orientation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : Orientation
|
||||
Orientation to compare against.
|
||||
rtol : float, optional
|
||||
Relative tolerance of equality.
|
||||
atol : float, optional
|
||||
Absolute tolerance of equality.
|
||||
equal_nan : bool, optional
|
||||
Consider matching NaN values as equal. Defaults to True.
|
||||
|
||||
Returns
|
||||
-------
|
||||
mask : numpy.ndarray bool
|
||||
Mask indicating where corresponding orientations are close.
|
||||
|
||||
"""
|
||||
matching_type = all([hasattr(other,attr) and getattr(self,attr) == getattr(other,attr)
|
||||
for attr in ['family','lattice','parameters']])
|
||||
return np.logical_and(matching_type,super(self.__class__,self.reduced).isclose(other.reduced))
|
||||
|
||||
|
||||
|
||||
def allclose(self,other,rtol=1e-5,atol=1e-8,equal_nan=True):
|
||||
"""
|
||||
Test whether all values are approximately equal to corresponding ones of other Orientation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : Orientation
|
||||
Orientation to compare against.
|
||||
rtol : float, optional
|
||||
Relative tolerance of equality.
|
||||
atol : float, optional
|
||||
Absolute tolerance of equality.
|
||||
equal_nan : bool, optional
|
||||
Consider matching NaN values as equal. Defaults to True.
|
||||
|
||||
Returns
|
||||
-------
|
||||
answer : bool
|
||||
Whether all values are close between both orientations.
|
||||
|
||||
"""
|
||||
return np.all(self.isclose(other,rtol,atol,equal_nan))
|
||||
|
||||
|
||||
def __mul__(self,other):
|
||||
"""
|
||||
Compose this orientation with other.
|
||||
|
@ -491,9 +541,6 @@ class Orientation(Rotation):
|
|||
is added to the left of the Rotation array.
|
||||
|
||||
"""
|
||||
if self.family is None:
|
||||
raise ValueError('Missing crystal symmetry')
|
||||
|
||||
o = self.symmetry_operations.broadcast_to(self.symmetry_operations.shape+self.shape,mode='right')
|
||||
return self.copy(rotation=o*Rotation(self.quaternion).broadcast_to(o.shape,mode='left'))
|
||||
|
||||
|
@ -501,9 +548,6 @@ class Orientation(Rotation):
|
|||
@property
|
||||
def reduced(self):
|
||||
"""Select symmetrically equivalent orientation that falls into fundamental zone according to symmetry."""
|
||||
if self.family is None:
|
||||
raise ValueError('Missing crystal symmetry')
|
||||
|
||||
eq = self.equivalent
|
||||
ok = eq.in_FZ
|
||||
ok &= np.cumsum(ok,axis=0) == 1
|
||||
|
@ -532,9 +576,6 @@ class Orientation(Rotation):
|
|||
https://doi.org/10.1107/S0108767391006864
|
||||
|
||||
"""
|
||||
if self.family is None:
|
||||
raise ValueError('Missing crystal symmetry')
|
||||
|
||||
rho_abs = np.abs(self.as_Rodrigues_vector(compact=True))*(1.-1.e-9)
|
||||
|
||||
with np.errstate(invalid='ignore'):
|
||||
|
@ -575,9 +616,6 @@ class Orientation(Rotation):
|
|||
https://doi.org/10.1107/S0108767391006864
|
||||
|
||||
"""
|
||||
if self.family is None:
|
||||
raise ValueError('Missing crystal symmetry')
|
||||
|
||||
rho = self.as_Rodrigues_vector(compact=True)*(1.0-1.0e-9)
|
||||
|
||||
with np.errstate(invalid='ignore'):
|
||||
|
@ -728,8 +766,6 @@ class Orientation(Rotation):
|
|||
'beta': np.pi/2.,
|
||||
'gamma': np.pi/2.,
|
||||
}
|
||||
else:
|
||||
raise KeyError(f'Crystal family "{self.family}" is unknown')
|
||||
|
||||
|
||||
@property
|
||||
|
@ -1026,8 +1062,6 @@ class Orientation(Rotation):
|
|||
Bunge Eulers / deg: (11.40, 21.86, 0.60)
|
||||
|
||||
"""
|
||||
if self.family is None or other.family is None:
|
||||
raise ValueError('missing crystal symmetry')
|
||||
if self.family != other.family:
|
||||
raise NotImplementedError('disorientation between different crystal families')
|
||||
|
||||
|
@ -1084,9 +1118,6 @@ class Orientation(Rotation):
|
|||
https://doi.org/10.1107/S0021889801003077
|
||||
|
||||
"""
|
||||
if self.family is None:
|
||||
raise ValueError('Missing crystal symmetry')
|
||||
|
||||
eq = self.equivalent
|
||||
m = eq.misorientation(self[...,0].reshape((1,)+self.shape[:-1]+(1,))
|
||||
.broadcast_to(eq.shape))\
|
||||
|
@ -1128,9 +1159,6 @@ class Orientation(Rotation):
|
|||
Index of symmetrically equivalent orientation that rotated vector to SST.
|
||||
|
||||
"""
|
||||
if self.family is None:
|
||||
raise ValueError('Missing crystal symmetry')
|
||||
|
||||
eq = self.equivalent
|
||||
blend = util.shapeblender(eq.shape,np.array(vector).shape[:-1])
|
||||
poles = eq.broadcast_to(blend,mode='right') @ np.broadcast_to(np.array(vector),blend+(3,))
|
||||
|
|
|
@ -1044,7 +1044,8 @@ class Result:
|
|||
|
||||
collection = ET.SubElement(domain, 'Grid')
|
||||
collection.attrib={'GridType': 'Collection',
|
||||
'CollectionType': 'Temporal'}
|
||||
'CollectionType': 'Temporal',
|
||||
'Name': 'Increments'}
|
||||
|
||||
time = ET.SubElement(collection, 'Time')
|
||||
time.attrib={'TimeType': 'List'}
|
||||
|
@ -1106,7 +1107,7 @@ class Result:
|
|||
unit = f[name].attrs[u] if h5py3 else f[name].attrs[u].decode()
|
||||
|
||||
attributes.append(ET.SubElement(grid, 'Attribute'))
|
||||
attributes[-1].attrib = {'Name': name.split('/',2)[2]+f' / {unit}',
|
||||
attributes[-1].attrib = {'Name': '/'.join([ty,field,out])+f' / {unit}',
|
||||
'Center': 'Cell',
|
||||
'AttributeType': attribute_type_map[shape]}
|
||||
data_items.append(ET.SubElement(attributes[-1], 'DataItem'))
|
||||
|
@ -1265,7 +1266,7 @@ class Result:
|
|||
if prune: r = util.dict_prune(r)
|
||||
if flatten: r = util.dict_flatten(r)
|
||||
|
||||
return r
|
||||
return None if (type(r) == dict and r == {}) else r
|
||||
|
||||
|
||||
def place(self,output='*',flatten=True,prune=True,constituents=None,fill_float=np.nan,fill_int=0):
|
||||
|
@ -1352,4 +1353,4 @@ class Result:
|
|||
if prune: r = util.dict_prune(r)
|
||||
if flatten: r = util.dict_flatten(r)
|
||||
|
||||
return r
|
||||
return None if (type(r) == dict and r == {}) else r
|
||||
|
|
|
@ -103,29 +103,20 @@ class Rotation:
|
|||
"""
|
||||
Equal to other.
|
||||
|
||||
Equality is determined taking limited floating point precision into account.
|
||||
See numpy.allclose for details.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : Rotation
|
||||
Rotation to check for equality.
|
||||
|
||||
"""
|
||||
s = self.quaternion
|
||||
o = other.quaternion
|
||||
if self.shape == () == other.shape:
|
||||
return np.allclose(s,o) or (np.isclose(s[0],0.0) and np.allclose(s,-1.0*o))
|
||||
else:
|
||||
return np.all(np.isclose(s,o),-1) + np.all(np.isclose(s,-1.0*o),-1) * np.isclose(s[...,0],0.0)
|
||||
return np.logical_or(np.all(self.quaternion == other.quaternion,axis=-1),
|
||||
np.all(self.quaternion == -1.0*other.quaternion,axis=-1))
|
||||
|
||||
|
||||
def __ne__(self,other):
|
||||
"""
|
||||
Not equal to other.
|
||||
|
||||
Equality is determined taking limited floating point precision into
|
||||
account. See numpy.allclose for details.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : Rotation
|
||||
|
@ -135,6 +126,57 @@ class Rotation:
|
|||
return np.logical_not(self==other)
|
||||
|
||||
|
||||
def isclose(self,other,rtol=1e-5,atol=1e-8,equal_nan=True):
|
||||
"""
|
||||
Report where values are approximately equal to corresponding ones of other Rotation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : Rotation
|
||||
Rotation to compare against.
|
||||
rtol : float, optional
|
||||
Relative tolerance of equality.
|
||||
atol : float, optional
|
||||
Absolute tolerance of equality.
|
||||
equal_nan : bool, optional
|
||||
Consider matching NaN values as equal. Defaults to True.
|
||||
|
||||
Returns
|
||||
-------
|
||||
mask : numpy.ndarray bool
|
||||
Mask indicating where corresponding rotations are close.
|
||||
|
||||
"""
|
||||
s = self.quaternion
|
||||
o = other.quaternion
|
||||
return np.logical_or(np.all(np.isclose(s, o,rtol,atol,equal_nan),axis=-1),
|
||||
np.all(np.isclose(s,-1.0*o,rtol,atol,equal_nan),axis=-1))
|
||||
|
||||
|
||||
def allclose(self,other,rtol=1e-5,atol=1e-8,equal_nan=True):
|
||||
"""
|
||||
Test whether all values are approximately equal to corresponding ones of other Rotation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other : Rotation
|
||||
Rotation to compare against.
|
||||
rtol : float, optional
|
||||
Relative tolerance of equality.
|
||||
atol : float, optional
|
||||
Absolute tolerance of equality.
|
||||
equal_nan : bool, optional
|
||||
Consider matching NaN values as equal. Defaults to True.
|
||||
|
||||
Returns
|
||||
-------
|
||||
answer : bool
|
||||
Whether all values are close between both rotations.
|
||||
|
||||
"""
|
||||
return np.all(self.isclose(other,rtol,atol,equal_nan))
|
||||
|
||||
|
||||
def __array__(self):
|
||||
"""Initializer for numpy."""
|
||||
return self.quaternion
|
||||
|
|
|
@ -137,7 +137,7 @@ def show_progress(iterable,N_iter=None,prefix='',bar_length=50):
|
|||
Character length of bar. Defaults to 50.
|
||||
|
||||
"""
|
||||
if N_iter == 1 or (hasattr(iterable,'__len__') and len(iterable) == 1):
|
||||
if N_iter in [0,1] or (hasattr(iterable,'__len__') and len(iterable) <= 1):
|
||||
for item in iterable:
|
||||
yield item
|
||||
else:
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -54,6 +54,6 @@ class TestConfig:
|
|||
def test_abstract_is_complete(self):
|
||||
assert Config().is_complete is None
|
||||
|
||||
@pytest.mark.parametrize('data',[Rotation.from_random(),Orientation.from_random()])
|
||||
@pytest.mark.parametrize('data',[Rotation.from_random(),Orientation.from_random(lattice='cI')])
|
||||
def test_rotation_orientation(self,data):
|
||||
assert str(Config(a=data)) == str(Config(a=data.as_quaternion()))
|
||||
|
|
|
@ -140,6 +140,5 @@ class TestConfigMaterial:
|
|||
if update:
|
||||
cur.save(ref_path/'measured.material.yaml')
|
||||
for i,m in enumerate(ref['material']):
|
||||
assert Rotation(m['constituents'][0]['O']) == \
|
||||
Rotation(cur['material'][i]['constituents'][0]['O'])
|
||||
assert Rotation(m['constituents'][0]['O']).isclose(Rotation(cur['material'][i]['constituents'][0]['O']))
|
||||
assert cur.is_valid and cur['phase'] == ref['phase'] and cur['homogenization'] == ref['homogenization']
|
||||
|
|
|
@ -37,9 +37,15 @@ class TestOrientation:
|
|||
assert not ( Orientation(R,lattice) != Orientation(R,lattice) if shape is None else \
|
||||
(Orientation(R,lattice) != Orientation(R,lattice)).any())
|
||||
|
||||
@pytest.mark.parametrize('lattice',Orientation.crystal_families)
|
||||
@pytest.mark.parametrize('shape',[None,5,(4,6)])
|
||||
def test_close(self,lattice,shape):
|
||||
R = Orientation.from_random(lattice=lattice,shape=shape)
|
||||
assert R.isclose(R.reduced).all() and R.allclose(R.reduced)
|
||||
|
||||
@pytest.mark.parametrize('a,b',[
|
||||
(dict(rotation=[1,0,0,0]),
|
||||
dict(rotation=[0.5,0.5,0.5,0.5])),
|
||||
(dict(rotation=[1,0,0,0],lattice='triclinic'),
|
||||
dict(rotation=[0.5,0.5,0.5,0.5],lattice='triclinic')),
|
||||
|
||||
(dict(rotation=[1,0,0,0],lattice='cubic'),
|
||||
dict(rotation=[1,0,0,0],lattice='hexagonal')),
|
||||
|
@ -222,7 +228,7 @@ class TestOrientation:
|
|||
blend = util.shapeblender(o.shape,p.shape)
|
||||
for loc in np.random.randint(0,blend,(10,len(blend))):
|
||||
assert o[tuple(loc[:len(o.shape)])].disorientation(p[tuple(loc[-len(p.shape):])]) \
|
||||
== o.disorientation(p)[tuple(loc)]
|
||||
.isclose(o.disorientation(p)[tuple(loc)])
|
||||
|
||||
@pytest.mark.parametrize('lattice',Orientation.crystal_families)
|
||||
def test_disorientation360(self,lattice):
|
||||
|
@ -335,33 +341,9 @@ class TestOrientation:
|
|||
o.family = invalid_family
|
||||
o.symmetry_operations # noqa
|
||||
|
||||
def test_missing_symmetry_equivalent(self):
|
||||
with pytest.raises(ValueError):
|
||||
Orientation(lattice=None).equivalent # noqa
|
||||
|
||||
def test_missing_symmetry_reduced(self):
|
||||
with pytest.raises(ValueError):
|
||||
Orientation(lattice=None).reduced # noqa
|
||||
|
||||
def test_missing_symmetry_in_FZ(self):
|
||||
with pytest.raises(ValueError):
|
||||
Orientation(lattice=None).in_FZ # noqa
|
||||
|
||||
def test_missing_symmetry_in_disorientation_FZ(self):
|
||||
with pytest.raises(ValueError):
|
||||
Orientation(lattice=None).in_disorientation_FZ # noqa
|
||||
|
||||
def test_missing_symmetry_disorientation(self):
|
||||
with pytest.raises(ValueError):
|
||||
Orientation(lattice=None).disorientation(Orientation(lattice=None)) # noqa
|
||||
|
||||
def test_missing_symmetry_average(self):
|
||||
with pytest.raises(ValueError):
|
||||
Orientation(lattice=None).average() # noqa
|
||||
|
||||
def test_missing_symmetry_to_SST(self):
|
||||
with pytest.raises(ValueError):
|
||||
Orientation(lattice=None).to_SST(np.zeros(3)) # noqa
|
||||
def test_invalid_rot(self):
|
||||
with pytest.raises(TypeError):
|
||||
Orientation.from_random(lattice='cubic') * np.ones(3)
|
||||
|
||||
def test_missing_symmetry_immutable(self):
|
||||
with pytest.raises(KeyError):
|
||||
|
|
|
@ -65,14 +65,17 @@ class TestResult:
|
|||
assert dict_equal(a,default.view('times','*').get('F'))
|
||||
assert dict_equal(a,default.view('times',default.times_in_range(0.0,np.inf)).get('F'))
|
||||
|
||||
@pytest.mark.parametrize('what',['increments','times','phases']) # ToDo: discuss homogenizations
|
||||
@pytest.mark.parametrize('what',['increments','times','phases','fields']) # ToDo: discuss homogenizations
|
||||
def test_view_none(self,default,what):
|
||||
a = default.view(what,False).get('F')
|
||||
b = default.view(what,[]).get('F')
|
||||
n0 = default.view(what,False)
|
||||
n1 = default.view(what,[])
|
||||
|
||||
assert a == b == {}
|
||||
label = 'increments' if what == 'times' else what
|
||||
|
||||
@pytest.mark.parametrize('what',['increments','times','phases']) # ToDo: discuss homogenizations
|
||||
assert n0.get('F') is n1.get('F') is None and \
|
||||
len(n0.visible[label]) == len(n1.visible[label]) == 0
|
||||
|
||||
@pytest.mark.parametrize('what',['increments','times','phases','fields']) # ToDo: discuss homogenizations
|
||||
def test_view_more(self,default,what):
|
||||
empty = default.view(what,False)
|
||||
|
||||
|
@ -81,14 +84,17 @@ class TestResult:
|
|||
|
||||
assert dict_equal(a,b)
|
||||
|
||||
@pytest.mark.parametrize('what',['increments','times','phases']) # ToDo: discuss homogenizations
|
||||
@pytest.mark.parametrize('what',['increments','times','phases','fields']) # ToDo: discuss homogenizations
|
||||
def test_view_less(self,default,what):
|
||||
full = default.view(what,True)
|
||||
|
||||
a = full.view_less(what,'*').get('F')
|
||||
b = full.view_less(what,True).get('F')
|
||||
n0 = full.view_less(what,'*')
|
||||
n1 = full.view_less(what,True)
|
||||
|
||||
assert a == b == {}
|
||||
label = 'increments' if what == 'times' else what
|
||||
|
||||
assert n0.get('F') is n1.get('F') is None and \
|
||||
len(n0.visible[label]) == len(n1.visible[label]) == 0
|
||||
|
||||
def test_view_invalid(self,default):
|
||||
with pytest.raises(AttributeError):
|
||||
|
@ -189,7 +195,7 @@ class TestResult:
|
|||
default.add_stress_Cauchy('P','F')
|
||||
default.add_calculation('sigma_y','#sigma#',unit='y')
|
||||
default.add_equivalent_Mises('sigma_y')
|
||||
assert default.get('sigma_y_vM') == {}
|
||||
assert default.get('sigma_y_vM') is None
|
||||
|
||||
def test_add_Mises_stress_strain(self,default):
|
||||
default.add_stress_Cauchy('P','F')
|
||||
|
@ -380,8 +386,8 @@ class TestResult:
|
|||
pickle.dump(cur,f)
|
||||
|
||||
with bz2.BZ2File((ref_path/'get'/fname).with_suffix('.pbz2')) as f:
|
||||
assert dict_equal(cur,pickle.load(f))
|
||||
|
||||
ref = pickle.load(f)
|
||||
assert cur is None if ref is None else dict_equal(cur,ref)
|
||||
|
||||
@pytest.mark.parametrize('view,output,flatten,constituents,prune',
|
||||
[({},['F','P','F','L_p','F_e','F_p'],True,True,None),
|
||||
|
@ -405,4 +411,5 @@ class TestResult:
|
|||
pickle.dump(cur,f)
|
||||
|
||||
with bz2.BZ2File((ref_path/'place'/fname).with_suffix('.pbz2')) as f:
|
||||
assert dict_equal(cur,pickle.load(f))
|
||||
ref = pickle.load(f)
|
||||
assert cur is None if ref is None else dict_equal(cur,ref)
|
||||
|
|
|
@ -960,7 +960,7 @@ class TestRotation:
|
|||
if axis_angle[3] > np.pi:
|
||||
axis_angle[3] -= 2.*np.pi
|
||||
axis_angle *= -1
|
||||
assert R**pwr == Rotation.from_axis_angle(axis_angle)
|
||||
assert (R**pwr).isclose(Rotation.from_axis_angle(axis_angle))
|
||||
|
||||
def test_rotate_inverse(self):
|
||||
R = Rotation.from_random()
|
||||
|
@ -1027,7 +1027,7 @@ class TestRotation:
|
|||
|
||||
def test_invariant(self):
|
||||
R = Rotation.from_random()
|
||||
assert R/R == R*R**(-1) == Rotation()
|
||||
assert (R/R).isclose(R*R**(-1)) and (R/R).isclose(Rotation())
|
||||
|
||||
@pytest.mark.parametrize('item',[np.ones(3),np.ones((3,3)), np.ones((3,3,3,3))])
|
||||
def test_apply(self,item):
|
||||
|
|
Loading…
Reference in New Issue