Skip to content

Commit

Permalink
Merge branch '476-controlling-random-seed-for-tests' into 'development'
Browse files Browse the repository at this point in the history
control and report random state for tests

Closes #476

See merge request damask/DAMASK!1015
  • Loading branch information
eisenlohr committed Jan 13, 2025
2 parents faf5929 + c4a64fd commit e4fe719
Show file tree
Hide file tree
Showing 15 changed files with 577 additions and 560 deletions.
18 changes: 13 additions & 5 deletions python/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ def pytest_addoption(parser):
help='Update reference results.')
parser.addoption('--damaskroot',
help='DAMASK root directory.')
parser.addoption('--rng_entropy',
help='Entropy for random seed generator.')

@pytest.fixture
def update(pytestconfig):
Expand Down Expand Up @@ -81,12 +83,18 @@ def res_path_base():
"""Directory containing testing resources."""
return Path(__file__).parent/'resources'

@pytest.fixture
def np_rng(pytestconfig):
"""Instance of numpy.random.Generator."""
e = pytestconfig.getoption('--rng_entropy')
print('\nrng entropy: ',sq := np.random.SeedSequence(e if e is None else int(e)).entropy)
return np.random.default_rng(seed=sq)

@pytest.fixture
def set_of_quaternions():
def set_of_quaternions(np_rng):
"""A set of n random rotations."""
def random_quaternions(N):
r = np.random.rand(N,3)
def random_quaternions(N,rng):
r = np_rng.random((N,3))

A = np.sqrt(r[:,2])
B = np.sqrt(1.0-r[:,2])
Expand Down Expand Up @@ -163,11 +171,11 @@ def random_quaternions(N):
[1.0,-1.0,-1.0,-1.0],
])
specials /= np.linalg.norm(specials,axis=1).reshape(-1,1)
specials_scatter = specials + np.broadcast_to((np.random.rand(4)*2.-1.)*scatter,specials.shape)
specials_scatter = specials + np.broadcast_to((np_rng.random(4)*2.-1.)*scatter,specials.shape)
specials_scatter /= np.linalg.norm(specials_scatter,axis=1).reshape(-1,1)
specials_scatter[specials_scatter[:,0]<0]*=-1

return np.vstack((specials,
specials_scatter,
random_quaternions(n-2*len(specials)),
random_quaternions(n-2*len(specials),np_rng),
))
24 changes: 12 additions & 12 deletions python/tests/test_Colormap.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def _patch_execution_stamp(self, patch_execution_stamp):
def test_repr(self,patch_plt_show):
print(Colormap.from_predefined('stress'))

def test_conversion(self):
def test_conversion(self,np_rng):
specials = np.array([[0.,0.,0.],
[1.,0.,0.],
[0.,1.,0.],
Expand All @@ -33,7 +33,7 @@ def test_conversion(self):
[1.,0.,1.],
[1.,1.,1.]
])
rgbs = np.vstack((specials,np.random.rand(100,3)))
rgbs = np.vstack((specials,np_rng.random((100,3))))
for rgb in rgbs:
print('rgb',rgb)

Expand Down Expand Up @@ -90,16 +90,16 @@ def test_from_range_types(self,low,high):

@pytest.mark.parametrize('format',['ASCII','paraview','GOM','gmsh'])
@pytest.mark.parametrize('model',['rgb','hsv','hsl','xyz','lab','msh'])
def test_from_range(self,model,format,tmp_path):
N = np.random.randint(2,256)
c = Colormap.from_range(np.random.rand(3),np.random.rand(3),model=model,N=N) # noqa
def test_from_range(self,np_rng,model,format,tmp_path):
N = np_rng.integers(2,256)
c = Colormap.from_range(np_rng.random(3),np_rng.random(3),model=model,N=N) # noqa
eval(f'c.save_{format}(tmp_path/"color_out")')

@pytest.mark.parametrize('format',['ASCII','paraview','GOM','gmsh'])
@pytest.mark.parametrize('name',['strain','gnuplot','Greys','PRGn','viridis'])
def test_from_predefined(self,name,format,tmp_path):
N = np.random.randint(2,256)
c = Colormap.from_predefined(name,N) # noqa
def test_from_predefined(self,np_rng,name,format,tmp_path):
N = np_rng.integers(2,256)
c = Colormap.from_predefined(name,N) # noqa
os.chdir(tmp_path)
eval(f'c.save_{format}()')

Expand All @@ -109,19 +109,19 @@ def test_from_predefined(self,name,format,tmp_path):
('gmsh','test.msh')
])
def test_write_filehandle(self,format,name,tmp_path):
c = Colormap.from_predefined('Dark2') # noqa
c = Colormap.from_predefined('Dark2') # noqa
fname = tmp_path/name
with open(fname,'w') as f: # noqa
with open(fname,'w') as f: # noqa
eval(f'c.save_{format}(f)')
for i in range(10):
if fname.exists(): return
time.sleep(.5)
assert False

@pytest.mark.parametrize('model',['rgb','hsv','hsl','lab','invalid'])
def test_invalid_color(self,model):
def test_invalid_color(self,np_rng,model):
with pytest.raises(ValueError):
c = Colormap.from_range(-2.+np.random.rand(3),np.random.rand(3),N=10,model=model) # noqa
c = Colormap.from_range(-2.+np_rng.random(3),np_rng.random(3),N=10,model=model) # noqa

def test_reversed(self):
c_1 = Colormap.from_predefined('stress')
Expand Down
24 changes: 13 additions & 11 deletions python/tests/test_ConfigMaterial.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ def test_empty_homogenization(self,res_path):
material_config['homogenization'] = None
assert not material_config.is_complete

def test_from_table(self):
N = np.random.randint(3,10)
def test_from_table(self,np_rng):
N = np_rng.integers(3,10)
a = np.vstack((np.hstack((np.arange(N),np.arange(N)[::-1])),
np.zeros(N*2),np.ones(N*2),np.zeros(N*2),np.zeros(N*2),
np.ones(N*2),
Expand All @@ -115,8 +115,8 @@ def test_updated_dicts(self,res_path):
assert m1['phase'].get('Aluminum') is None
assert m1['homogenization'].get('SX') is None

def test_from_table_with_constant(self):
N = np.random.randint(3,10)
def test_from_table_with_constant(self,np_rng):
N = np_rng.integers(3,10)
a = np.vstack((np.hstack((np.arange(N),np.arange(N)[::-1])),
np.zeros(N*2),np.ones(N*2),np.zeros(N*2),np.zeros(N*2),
np.ones(N*2),
Expand All @@ -129,28 +129,30 @@ def test_from_table_with_constant(self):

@pytest.mark.parametrize('N,n,kw',[
(1,1,{'phase':'Gold',
'O':[1,0,0,0],
'O':None,
'V_e':np.eye(3),
'homogenization':'SX'}),
(3,1,{'phase':'Gold',
'O':Rotation.from_random(3),
'O':3,
'V_e':np.broadcast_to(np.eye(3),(3,3,3)),
'homogenization':'SX'}),
(2,3,{'phase':np.broadcast_to(['a','b','c'],(2,3)),
'O':Rotation.from_random((2,3)),
'O':(2,3),
'V_e':np.broadcast_to(np.eye(3),(2,3,3,3)),
'homogenization':['SX','PX']}),
])
def test_material_add(self,kw,N,n):
kw['O'] = Rotation.from_random(kw['O'])
m = ConfigMaterial().material_add(**kw)
assert len(m['material']) == N
assert len(m['material'][0]['constituents']) == n

@pytest.mark.parametrize('shape',[(),(4,),(5,2)])
@pytest.mark.parametrize('kw',[{'V_e':np.random.rand(3,3)},
{'O':np.random.rand(4)},
{'v':np.array(2)}])
def test_material_add_invalid(self,kw,shape):
@pytest.mark.parametrize('kw',[{'V_e':(3,3)},
{'O':4},
{'v':np.array([2])}])
def test_material_add_invalid(self,np_rng,kw,shape):
kw = {arg:(np_rng.random(val) if not type(val) is np.ndarray else val) for arg,val in kw.items()}
kw = {arg:np.broadcast_to(val,shape+val.shape) for arg,val in kw.items()}
with pytest.raises(ValueError):
ConfigMaterial().material_add(**kw)
Expand Down
14 changes: 7 additions & 7 deletions python/tests/test_Crystal.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ def test_invalid_init(self,lattice,family):
with pytest.raises(KeyError):
Crystal(family=family,lattice=lattice)

def test_eq(self):
family = np.random.choice(list(damask._crystal.lattice_symmetries.values()))
def test_eq(self,np_rng):
family = np_rng.choice(list(damask._crystal.lattice_symmetries.values()))
assert Crystal(family=family) == Crystal(family=family)

def test_double_to_lattice(self):
Expand Down Expand Up @@ -46,9 +46,9 @@ def test_basis_invalid(self):
with pytest.raises(KeyError):
Crystal(family='cubic').basis_real

def test_basis_real(self):
for gamma in np.random.random(2**8)*np.pi:
basis = np.tril(np.random.random((3,3))+1e-6)
def test_basis_real(self,np_rng):
for gamma in np_rng.random(2**8)*np.pi:
basis = np.tril(np_rng.random((3,3))+1e-6)
basis[1,:2] = basis[1,1]*np.array([np.cos(gamma),np.sin(gamma)])
basis[2,:2] = basis[2,:2]*2-1
lengths = np.linalg.norm(basis,axis=-1)
Expand Down Expand Up @@ -139,8 +139,8 @@ def test_related(self,crystal):
@pytest.mark.parametrize('crystal', [Crystal(lattice='cF'),
Crystal(lattice='cI'),
Crystal(lattice='hP')])
def test_related_invalid_target(self,crystal):
relationship = np.random.choice(crystal.orientation_relationships)
def test_related_invalid_target(self,np_rng,crystal):
relationship = np_rng.choice(crystal.orientation_relationships)
with pytest.raises(ValueError):
crystal.relation_operations(relationship,crystal)

Expand Down
Loading

0 comments on commit e4fe719

Please sign in to comment.