DAMASK_EICMD/python/tests/test_Geom.py

423 lines
18 KiB
Python
Raw Normal View History

2019-11-23 17:29:41 +05:30
import pytest
import numpy as np
from damask import VTK
2019-11-23 17:29:41 +05:30
from damask import Geom
2020-10-29 12:12:41 +05:30
from damask import Table
from damask import Rotation
2020-06-25 01:04:51 +05:30
from damask import util
from damask import seeds
2020-10-29 12:12:41 +05:30
from damask import grid_filters
2019-11-23 17:29:41 +05:30
def geom_equal(a,b):
return np.all(a.material == b.material) and \
np.all(a.grid == b.grid) and \
np.allclose(a.size, b.size) and \
2020-08-25 04:29:41 +05:30
str(a.diff(b)) == str(b.diff(a))
2019-11-23 17:29:41 +05:30
@pytest.fixture
def default():
"""Simple geometry."""
x=np.concatenate((np.ones(40,dtype=int),
np.arange(2,42),
np.ones(40,dtype=int)*2,
2020-08-08 23:54:36 +05:30
np.arange(1,41))).reshape(8,5,4,order='F')
2019-11-23 17:29:41 +05:30
return Geom(x,[8e-6,5e-6,4e-6])
@pytest.fixture
def ref_path(ref_path_base):
2019-11-27 17:49:58 +05:30
"""Directory containing reference results."""
return ref_path_base/'Geom'
2019-11-23 17:29:41 +05:30
class TestGeom:
2020-03-29 23:37:09 +05:30
@pytest.fixture(autouse=True)
def _patch_execution_stamp(self, patch_execution_stamp):
print('patched damask.util.execution_stamp')
@pytest.fixture(autouse=True)
def _patch_datetime_now(self, patch_datetime_now):
print('patched datetime.datetime.now')
2020-08-25 04:29:41 +05:30
def test_diff_equal(self,default):
assert str(default.diff(default)) == ''
2020-08-25 04:29:41 +05:30
def test_diff_not_equal(self,default):
new = Geom(default.material[1:,1:,1:]+1,default.size*.9,np.ones(3)-default.origin,comments=['modified'])
2020-08-25 04:29:41 +05:30
assert str(default.diff(new)) != ''
2020-11-14 22:24:47 +05:30
def test_repr(self,default):
print(default)
2019-11-23 17:29:41 +05:30
2020-09-29 22:55:50 +05:30
def test_read_write_vtr(self,default,tmp_path):
default.save(tmp_path/'default')
new = Geom.load(tmp_path/'default.vtr')
assert geom_equal(new,default)
2020-09-29 22:55:50 +05:30
def test_invalid_vtr(self,tmp_path):
2020-10-27 18:12:49 +05:30
v = VTK.from_rectilinear_grid(np.random.randint(5,10,3)*2,np.random.random(3) + 1.0)
v.save(tmp_path/'no_materialpoint.vtr',parallel=False)
with pytest.raises(ValueError):
2020-09-29 22:55:50 +05:30
Geom.load(tmp_path/'no_materialpoint.vtr')
2020-09-25 02:29:31 +05:30
def test_invalid_material(self):
with pytest.raises(TypeError):
Geom(np.zeros((3,3,3),dtype='complex'),np.ones(3))
def test_cast_to_int(self):
g = Geom(np.zeros((3,3,3)),np.ones(3))
assert g.material.dtype in np.sctypes['int']
2020-08-09 00:05:50 +05:30
2020-05-25 02:22:00 +05:30
def test_invalid_size(self,default):
with pytest.raises(ValueError):
Geom(default.material[1:,1:,1:],
size=np.ones(2))
2020-11-14 22:24:47 +05:30
def test_save_load_ASCII(self,default,tmp_path):
default.save_ASCII(tmp_path/'ASCII')
default.material -= 1
2020-11-14 22:24:47 +05:30
assert geom_equal(Geom.load_ASCII(tmp_path/'ASCII'),default)
2020-05-25 02:22:00 +05:30
2020-08-08 23:12:34 +05:30
def test_invalid_origin(self,default):
2020-05-25 02:22:00 +05:30
with pytest.raises(ValueError):
Geom(default.material[1:,1:,1:],
size=np.ones(3),
origin=np.ones(4))
2020-08-08 23:12:34 +05:30
def test_invalid_materials_shape(self,default):
material = np.ones((3,3))
2020-08-08 23:12:34 +05:30
with pytest.raises(ValueError):
Geom(material,
size=np.ones(3))
2020-08-08 23:12:34 +05:30
2020-05-25 02:22:00 +05:30
def test_invalid_materials_type(self,default):
material = np.random.randint(1,300,(3,4,5))==1
2020-05-25 02:22:00 +05:30
with pytest.raises(TypeError):
Geom(material)
2020-05-25 02:22:00 +05:30
@pytest.mark.parametrize('directions,reflect',[
2019-11-25 18:31:40 +05:30
(['x'], False),
(['x','y','z'],True),
(['z','x','y'],False),
(['y','z'], False)
]
)
def test_mirror(self,default,update,ref_path,directions,reflect):
modified = default.mirror(directions,reflect)
tag = f'directions_{"-".join(directions)}+reflect_{reflect}'
reference = ref_path/f'mirror_{tag}.vtr'
if update: modified.save(reference)
assert geom_equal(Geom.load(reference),
modified)
2020-08-23 14:35:56 +05:30
@pytest.mark.parametrize('directions',[(1,2,'y'),('a','b','x'),[1]])
def test_mirror_invalid(self,default,directions):
with pytest.raises(ValueError):
default.mirror(directions)
2020-08-25 04:29:41 +05:30
@pytest.mark.parametrize('directions',[
['x'],
['x','y','z'],
['z','x','y'],
['y','z'],
]
)
def test_flip(self,default,update,ref_path,directions):
modified = default.flip(directions)
tag = f'directions_{"-".join(directions)}'
reference = ref_path/f'flip_{tag}.vtr'
if update: modified.save(reference)
assert geom_equal(Geom.load(reference),
modified)
2020-08-25 12:04:04 +05:30
def test_flip_invariant(self,default):
assert geom_equal(default,default.flip([]))
2020-08-25 20:47:49 +05:30
@pytest.mark.parametrize('direction',[['x'],['x','y']])
def test_flip_double(self,default,direction):
assert geom_equal(default,default.flip(direction).flip(direction))
2020-08-23 14:35:56 +05:30
@pytest.mark.parametrize('directions',[(1,2,'y'),('a','b','x'),[1]])
2020-08-25 04:29:41 +05:30
def test_flip_invalid(self,default,directions):
2020-08-23 14:35:56 +05:30
with pytest.raises(ValueError):
2020-08-25 04:29:41 +05:30
default.flip(directions)
2020-08-23 14:35:56 +05:30
2020-03-31 14:35:25 +05:30
@pytest.mark.parametrize('stencil',[1,2,3,4])
@pytest.mark.parametrize('selection',[None,[1],[1,2,3]])
@pytest.mark.parametrize('periodic',[True,False])
def test_clean(self,default,update,ref_path,stencil,selection,periodic):
current = default.clean(stencil,selection,periodic)
reference = ref_path/f'clean_{stencil}_{"+".join(map(str,[None] if selection is None else selection))}_{periodic}'
if update and stencil > 1:
current.save(reference)
assert geom_equal(Geom.load(reference) if stencil > 1 else default,
current
)
2019-11-25 18:31:40 +05:30
2019-11-25 18:31:40 +05:30
@pytest.mark.parametrize('grid',[
2020-03-31 14:35:25 +05:30
(10,11,10),
[10,13,10],
np.array((10,10,10)),
np.array((8, 10,12)),
np.array((5, 4, 20)),
np.array((10,20,2))
2019-11-25 18:31:40 +05:30
]
)
def test_scale(self,default,update,ref_path,grid):
modified = default.scale(grid)
tag = f'grid_{util.srepr(grid,"-")}'
reference = ref_path/f'scale_{tag}.vtr'
if update: modified.save(reference)
assert geom_equal(Geom.load(reference),
modified)
2020-03-29 22:42:23 +05:30
def test_renumber(self,default):
material = default.material.copy()
for m in np.unique(material):
material[material==m] = material.max() + np.random.randint(1,30)
2020-10-28 14:01:55 +05:30
default.material -= 1
modified = Geom(material,
default.size,
default.origin)
assert not geom_equal(modified,default)
assert geom_equal(default,
modified.renumber())
def test_substitute(self,default):
offset = np.random.randint(1,500)
modified = Geom(default.material + offset,
default.size,
default.origin)
assert not geom_equal(modified,default)
assert geom_equal(default,
modified.substitute(np.arange(default.material.max())+1+offset,
np.arange(default.material.max())+1))
def test_substitute_invariant(self,default):
f = np.unique(default.material.flatten())[:np.random.randint(1,default.material.max())]
t = np.random.permutation(f)
modified = default.substitute(f,t)
assert np.array_equiv(t,f) or (not geom_equal(modified,default))
assert geom_equal(default, modified.substitute(t,f))
def test_sort(self):
grid = np.random.randint(5,20,3)
m = Geom(np.random.randint(1,20,grid)*3,np.ones(3)).sort().material.flatten(order='F')
for i,v in enumerate(m):
2020-11-03 05:01:37 +05:30
assert i==0 or v > m[:i].max() or v in m[:i]
@pytest.mark.parametrize('axis_angle',[np.array([1,0,0,86.7]), np.array([0,1,0,90.4]), np.array([0,0,1,90]),
np.array([1,0,0,175]),np.array([0,-1,0,178]),np.array([0,0,1,180])])
2020-05-25 02:22:00 +05:30
def test_rotate360(self,default,axis_angle):
2020-08-22 23:25:18 +05:30
modified = default.copy()
for i in range(np.rint(360/axis_angle[3]).astype(int)):
modified.rotate(Rotation.from_axis_angle(axis_angle,degrees=True))
assert geom_equal(default,modified)
@pytest.mark.parametrize('Eulers',[[32.0,68.0,21.0],
[0.0,32.0,240.0]])
def test_rotate(self,default,update,ref_path,Eulers):
2020-11-18 03:26:22 +05:30
modified = default.rotate(Rotation.from_Euler_angles(Eulers,degrees=True))
tag = f'Eulers_{util.srepr(Eulers,"-")}'
reference = ref_path/f'rotate_{tag}.vtr'
if update: modified.save(reference)
assert geom_equal(Geom.load(reference),
modified)
2020-05-25 02:22:00 +05:30
def test_canvas(self,default):
grid = default.grid
2020-05-25 02:22:00 +05:30
grid_add = np.random.randint(0,30,(3))
modified = default.canvas(grid + grid_add)
assert np.all(modified.material[:grid[0],:grid[1],:grid[2]] == default.material)
2020-08-09 00:05:50 +05:30
@pytest.mark.parametrize('center1,center2',[(np.random.random(3)*.5,np.random.random()*8),
2020-08-10 02:44:32 +05:30
(np.random.randint(4,8,(3)),np.random.randint(9,12,(3)))])
2020-08-22 23:25:18 +05:30
@pytest.mark.parametrize('diameter',[np.random.random(3)*.5,
np.random.randint(4,10,(3)),
np.random.rand(),
np.random.randint(30)])
@pytest.mark.parametrize('exponent',[np.random.random(3)*.5,
np.random.randint(4,10,(3)),
np.random.rand()*4,
np.random.randint(20)])
def test_add_primitive_shift(self,center1,center2,diameter,exponent):
"""Same volume fraction for periodic geometries and different center."""
2020-08-22 23:25:18 +05:30
o = np.random.random(3)-.5
g = np.random.randint(8,32,(3))
s = np.random.random(3)+.5
G_1 = Geom(np.ones(g,'i'),s,o).add_primitive(diameter,center1,exponent)
G_2 = Geom(np.ones(g,'i'),s,o).add_primitive(diameter,center2,exponent)
assert np.count_nonzero(G_1.material!=2) == np.count_nonzero(G_2.material!=2)
2020-08-08 23:44:30 +05:30
@pytest.mark.parametrize('center',[np.random.randint(4,10,(3)),
np.random.randint(2,10),
np.random.rand()*4,
np.random.rand(3)*10])
@pytest.mark.parametrize('inverse',[True,False])
@pytest.mark.parametrize('periodic',[True,False])
def test_add_primitive_rotation(self,center,inverse,periodic):
2020-11-13 11:20:57 +05:30
"""Rotation should not change result for sphere."""
g = np.random.randint(8,32,(3))
s = np.random.random(3)+.5
fill = np.random.randint(10)+2
2020-11-13 11:20:57 +05:30
G_1 = Geom(np.ones(g,'i'),s).add_primitive(.3,center,1,fill,inverse=inverse,periodic=periodic)
G_2 = Geom(np.ones(g,'i'),s).add_primitive(.3,center,1,fill,Rotation.from_random(),inverse,periodic=periodic)
assert geom_equal(G_1,G_2)
2020-08-08 23:12:34 +05:30
@pytest.mark.parametrize('trigger',[[1],[]])
def test_vicinity_offset(self,trigger):
offset = np.random.randint(2,4)
vicinity = np.random.randint(2,4)
2020-08-09 00:05:50 +05:30
2020-08-10 02:44:32 +05:30
g = np.random.randint(28,40,(3))
m = np.ones(g,'i')
x = (g*np.random.permutation(np.array([.5,1,1]))).astype('i')
m[slice(0,x[0]),slice(0,x[1]),slice(0,x[2])] = 2
2020-08-22 23:25:18 +05:30
m2 = m.copy()
2020-08-08 23:12:34 +05:30
for i in [0,1,2]:
2020-08-10 02:44:32 +05:30
m2[(np.roll(m,+vicinity,i)-m)!=0] += offset
m2[(np.roll(m,-vicinity,i)-m)!=0] += offset
2020-08-08 23:12:34 +05:30
if len(trigger) > 0:
2020-08-10 02:44:32 +05:30
m2[m==1] = 1
2020-08-09 00:05:50 +05:30
geom = Geom(m,np.random.rand(3)).vicinity_offset(vicinity,offset,trigger=trigger)
2020-08-09 00:05:50 +05:30
assert np.all(m2==geom.material)
2020-08-08 23:12:34 +05:30
@pytest.mark.parametrize('periodic',[True,False])
def test_vicinity_offset_invariant(self,default,periodic):
offset = default.vicinity_offset(trigger=[default.material.max()+1,
default.material.min()-1])
assert np.all(offset.material==default.material)
2020-08-09 00:05:50 +05:30
2020-03-31 14:35:25 +05:30
@pytest.mark.parametrize('periodic',[True,False])
2020-03-29 23:58:54 +05:30
def test_tessellation_approaches(self,periodic):
2020-03-29 22:42:23 +05:30
grid = np.random.randint(10,20,3)
size = np.random.random(3) + 1.0
N_seeds= np.random.randint(10,30)
seeds = np.random.rand(N_seeds,3) * np.broadcast_to(size,(N_seeds,3))
2020-09-25 02:29:31 +05:30
Voronoi = Geom.from_Voronoi_tessellation( grid,size,seeds, np.arange(N_seeds)+5,periodic)
Laguerre = Geom.from_Laguerre_tessellation(grid,size,seeds,np.ones(N_seeds),np.arange(N_seeds)+5,periodic)
2020-03-29 22:42:23 +05:30
assert geom_equal(Laguerre,Voronoi)
2020-03-29 23:58:54 +05:30
def test_Laguerre_weights(self):
2020-03-29 22:42:23 +05:30
grid = np.random.randint(10,20,3)
size = np.random.random(3) + 1.0
N_seeds= np.random.randint(10,30)
seeds = np.random.rand(N_seeds,3) * np.broadcast_to(size,(N_seeds,3))
weights= np.full((N_seeds),-np.inf)
2020-10-10 13:27:18 +05:30
ms = np.random.randint(N_seeds)
weights[ms] = np.random.random()
2020-09-23 12:53:30 +05:30
Laguerre = Geom.from_Laguerre_tessellation(grid,size,seeds,weights,periodic=np.random.random()>0.5)
assert np.all(Laguerre.material == ms)
2020-03-29 23:58:54 +05:30
2020-03-31 14:35:25 +05:30
@pytest.mark.parametrize('approach',['Laguerre','Voronoi'])
2020-03-29 23:58:54 +05:30
def test_tessellate_bicrystal(self,approach):
grid = np.random.randint(5,10,3)*2
size = grid.astype(np.float)
seeds = np.vstack((size*np.array([0.5,0.25,0.5]),size*np.array([0.5,0.75,0.5])))
2020-10-10 13:27:18 +05:30
material = np.zeros(grid)
material[:,grid[1]//2:,:] = 1
2020-03-29 23:58:54 +05:30
if approach == 'Laguerre':
2020-09-23 12:53:30 +05:30
geom = Geom.from_Laguerre_tessellation(grid,size,seeds,np.ones(2),periodic=np.random.random()>0.5)
2020-03-29 23:58:54 +05:30
elif approach == 'Voronoi':
2020-09-23 12:53:30 +05:30
geom = Geom.from_Voronoi_tessellation(grid,size,seeds, periodic=np.random.random()>0.5)
assert np.all(geom.material == material)
2020-09-30 10:40:15 +05:30
@pytest.mark.parametrize('surface',['Schwarz P',
'Double Primitive',
'Schwarz D',
'Complementary D',
'Double Diamond',
'Dprime',
'Gyroid',
'Gprime',
'Karcher K',
'Lidinoid',
'Neovius',
'Fisher-Koch S',
])
2020-09-30 10:40:15 +05:30
def test_minimal_surface_basic_properties(self,surface):
grid = np.random.randint(60,100,3)
size = np.ones(3)+np.random.rand(3)
threshold = 2*np.random.rand()-1.
2020-09-30 10:40:15 +05:30
periods = np.random.randint(2)+1
materials = np.random.randint(0,40,2)
geom = Geom.from_minimal_surface(grid,size,surface,threshold,periods,materials)
assert set(geom.material.flatten()) | set(materials) == set(materials) \
2020-09-30 10:40:15 +05:30
and (geom.size == size).all() and (geom.grid == grid).all()
@pytest.mark.parametrize('surface,threshold',[('Schwarz P',0),
('Double Primitive',-1./6.),
('Schwarz D',0),
('Complementary D',0),
('Double Diamond',-0.133),
('Dprime',-0.0395),
('Gyroid',0),
('Gprime',0.22913),
('Karcher K',0.17045),
('Lidinoid',0.14455),
('Neovius',0),
('Fisher-Koch S',0),
])
def test_minimal_surface_volume(self,surface,threshold):
grid = np.ones(3,dtype=int)*64
geom = Geom.from_minimal_surface(grid,np.ones(3),surface,threshold)
assert np.isclose(np.count_nonzero(geom.material==1)/np.prod(geom.grid),.5,rtol=1e-3)
2020-11-25 17:21:52 +05:30
2020-10-29 12:12:41 +05:30
def test_from_table(self):
grid = np.random.randint(60,100,3)
size = np.ones(3)+np.random.rand(3)
coords = grid_filters.cell_coord0(grid,size).reshape(-1,3,order='F')
2020-10-29 22:22:16 +05:30
z=np.ones(grid.prod())
2020-10-29 23:05:22 +05:30
z[grid[:2].prod()*int(grid[2]/2):]=0
2020-10-29 22:22:16 +05:30
t = Table(np.column_stack((coords,z)),{'coords':3,'z':1})
g = Geom.from_table(t,'coords',['1_coords','z'])
2020-10-29 23:05:22 +05:30
assert g.N_materials == g.grid[0]*2 and (g.material[:,:,-1]-g.material[:,:,0] == grid[0]).all()
def test_from_table_recover(self,tmp_path):
grid = np.random.randint(60,100,3)
size = np.ones(3)+np.random.rand(3)
s = seeds.from_random(size,np.random.randint(60,100))
geom = Geom.from_Voronoi_tessellation(grid,size,s)
coords = grid_filters.cell_coord0(grid,size)
t = Table(np.column_stack((coords.reshape(-1,3,order='F'),geom.material.flatten(order='F'))),{'c':3,'m':1})
assert geom_equal(geom.sort().renumber(),Geom.from_table(t,'c',['m']))
2020-11-25 17:21:52 +05:30
@pytest.mark.parametrize('periodic',[True,False])
@pytest.mark.parametrize('direction',['x','y','z',['x','y'],'zy','xz',['x','y','z']])
def test_get_grain_boundaries(self,update,ref_path,periodic,direction):
geom=Geom.load(ref_path/'get_grain_boundaries_8g12x15x20.vtr')
2020-11-25 17:21:52 +05:30
current=geom.get_grain_boundaries(periodic,direction)
if update:
current.save(ref_path/f'get_grain_boundaries_8g12x15x20_{direction}_{periodic}.vtu',parallel=False)
reference=VTK.load(ref_path/f'get_grain_boundaries_8g12x15x20_{"".join(direction)}_{periodic}.vtu')
assert current.__repr__() == reference.__repr__()