def test_histogram_scale(self): # Make a sample AB and AB2 cell cell1 = crystal.Cell(numpy.eye(3), [numpy.zeros((1, 3)), 0.5 * numpy.ones((1, 3))]) cell2 = crystal.Cell(numpy.eye(3), [numpy.zeros((1, 3)), \ numpy.array([[0.35, 0.35, 0.35], [0.75, 0.75, 0.75]])]) # Check similarity metric self.assertTrue( similarity.Histogram(6, 0.1, 0.999)((cell1, None), (cell1, None))) self.assertTrue( similarity.Histogram(6, 0.1, 0.999)( (cell1, None), (crystal.CellTools.tile(cell1, (3, 2, 4)), None))) self.assertFalse( similarity.Histogram(6, 0.1, 0.999)((cell1, None), (cell2, None))) # Check numeric scaling histogram1 = similarity.Histogram(6, 0.1, 0.999)._compute_direct(cell1) histogram1s = similarity.Histogram(6, 0.1, 0.999)._compute_direct( crystal.CellTools.tile(cell1, (3, 2, 4))) histogram2 = similarity.Histogram(6, 0.1, 0.999)._compute_direct(cell2) self.assertTrue(numpy.all(numpy.isclose(histogram1, histogram1s))) self.assertTrue( numpy.all(numpy.isclose(histogram1[0, 1], histogram1[1, 0]))) self.assertFalse( numpy.all(numpy.isclose(histogram2[0, 1], histogram2[1, 0])))
def test_scaling_processor(self): # ScalingProcessor should wrap CellTools.scale and Cell.scale_factor cell = crystal.Cell(numpy.eye(3), [numpy.zeros((1, 3)), 0.5 * numpy.ones((1, 3))]) new_radii = (3.4, 5.6) self.assertEqual(crystal.CellTools.scale(cell, cell.vectors / cell.scale_factor(new_radii)), \ automation.ScalingProcessor(new_radii)(cell))
def test_histogram_correct(self): # Construct a histogram manually using cell RDF method cell = crystal.Cell( numpy.eye(3), [numpy.zeros((1, 3)), numpy.array([[0.3] * 3, [0.65] * 3])]) histogram = numpy.zeros((2, 2, 20)) for source in range(2): for target in range(2): for key, value in cell.rdf(source, target, 5).items(): factor = 0.5 + (4 * key) - int(numpy.round(4 * key)) histogram[source, target, max(0, int(numpy.round(4 * key)) - 1)] += value * (1 - factor) histogram[source, target, min(19, int(numpy.round(4 * key)))] += value * factor # Construct a fast histogram and compare self.assertTrue( numpy.all( numpy.isclose(histogram, potential._evaluate_fast(cell, None, 5, 0.25)[2])))
def test_cache(self): # Create a custom metric to keep track of cache hits cache_misses = [0] class CustomMetric(similarity.SimilarityMetric): def _compute_direct(self, cell): cache_misses[0] += 1 return cell[1] def __call__(self, cell_1, cell_2): return self._compute_cached(cell_1) == self._compute_cached( cell_2) # Create a dummy cell to make sure it can get hashed in cache cell = crystal.Cell(numpy.eye(3), [numpy.eye(3), numpy.eye(3)]) # Test cache proper operation metric = CustomMetric() self.assertFalse(metric((cell, 1), (cell, 2))) self.assertFalse(metric((cell, 3), (cell, 4))) self.assertEqual(cache_misses[0], 4) self.assertFalse(metric((cell, 1), (cell, 4))) self.assertEqual(cache_misses[0], 4) self.assertFalse(metric((cell, 1), (cell, 5))) self.assertEqual(cache_misses[0], 5) self.assertTrue(metric((cell, 2), (cell, 2))) self.assertTrue(metric((cell, 5), (cell, 5))) self.assertEqual(cache_misses[0], 5)
def test_histogram_binning(self): # Check for proper binning (number of bins) cell = crystal.Cell( numpy.eye(3), [numpy.zeros((1, 3)), numpy.array([[0.3] * 3, [0.65] * 3])]) self.assertEqual( potential._evaluate_fast(cell, None, 6, 0.25)[2].shape[2], 24) self.assertEqual( potential._evaluate_fast(cell, None, 6.01, 0.25)[2].shape[2], 25) self.assertEqual( potential._evaluate_fast(cell, None, 4, 5)[2].shape[2], 1)
def test_tile_wallpaper(self): # Create a chiral 2D cell reference_cell_1 = crystal.Cell(numpy.eye(2), \ [numpy.array([[0.25, 0.25]]), numpy.array([[0.75, 0.25]]), numpy.array([[0.25, 0.75]])], ["O", "X", "Y"]) a, b = 0.18469903125906464, 0.554097093777194 # to get nice contacts for right-angled 45-45-90 triangles reference_cell_2 = crystal.Cell(numpy.eye(2), \ [numpy.array([[a, a]]), numpy.array([[b, a]]), numpy.array([[a, b]])], ["O", "X", "Y"]) # Create the tilings (for possible visual inspection later) for group_index in range(17): group = wallpaper.WallpaperGroup(number=group_index + 1) # Determine what the cell vectors should look like length_ratio = group.ratio if group.ratio is not None else 1.25 unit_dot = group.dot if group.dot is not None else 0.25 vectors = numpy.array([[length_ratio, 0.0], [unit_dot, numpy.sqrt(1 - (unit_dot ** 2))]]) # Do it tiling_cell = crystal.CellTools.scale(reference_cell_2 if group.half else reference_cell_1, vectors) tiled_cell = wallpaper.tile_wallpaper(tiling_cell, group) with open("data/group_{}.cell".format(group_index + 1), "w") as data_file: crystal.CellCodecs.write_cell(tiled_cell, data_file)
def test_methods_jacobian(self): for cell, potentials, cutoff, expected in EvaluateTests._cases: # First, make sure Python and Cython algorithms give identical answers cell = crystal.CellTools.reduce(cell) energy_slow = crystal.CellTools.energy(cell, potentials, cutoff) energy_fast, jacobian_fast = potential._evaluate_fast( cell, potentials, cutoff) self.assertAlmostEqual(energy_slow, energy_fast) if expected is not None: self.assertTrue(numpy.isclose(energy_fast, expected)) # Now use second-order finite differences to make sure gradient is correct delta = 2.0**-26.0 index = 0 for type_index in range(cell.atom_types): for atom_index in range(cell.atom_count(type_index)): for component_index in range(cell.dimensions): low_lists, high_lists = cell.atom_lists, cell.atom_lists low_lists[type_index][atom_index][ component_index] -= delta high_lists[type_index][atom_index][ component_index] += delta low_energy = potential._evaluate_fast( crystal.CellTools.wrap( crystal.Cell(cell.vectors, low_lists)), potentials, cutoff)[0] high_energy = potential._evaluate_fast( crystal.CellTools.wrap( crystal.Cell(cell.vectors, high_lists)), potentials, cutoff)[0] derivative = (high_energy - low_energy) / (delta * 2) self.assertTrue( numpy.isclose(derivative, jacobian_fast[index], atol=1e-4, rtol=1e-4)) index += 1
def test_filtering_processor(self): # AutoFilteringProcessor should do absolutely nothing when called # It will get removed from the preprocessing chain and handled separately cell = crystal.Cell(numpy.eye(3), [numpy.eye(3)] * 3) self.assertEqual(cell, automation.AutoFilteringProcessor(1)(cell))
def test_tiling_processor(self): # TilingProcessor should wrap CellTools.tile cell = crystal.Cell(numpy.eye(3), [numpy.eye(3)] * 3) self.assertEqual(crystal.CellTools.tile(cell, (4, 4, 4)), automation.TilingProcessor((4, 4, 4))(cell))
def test_cell_processor(self): # CellProcessor should do absolutely nothing cell = crystal.Cell(numpy.eye(3), [numpy.eye(3)] * 3) self.assertEqual(cell, automation.CellProcessor()(cell))
#!/usr/bin/env python import itertools import numpy import unittest from paccs import crystal from paccs import visualization _cells = [ crystal.Cell(numpy.eye(3), [numpy.zeros((1, 3))]), crystal.Cell(numpy.eye(2), [numpy.zeros((1, 2)), 0.5 * numpy.ones( (1, 2))]), crystal.Cell(numpy.eye(3), [numpy.zeros((1, 3)), 0.5 * numpy.ones( (1, 3))]), crystal.Cell( numpy.eye(3), [numpy.concatenate([numpy.zeros( (1, 3)), numpy.ones((1, 3))])]) ] class Tests(unittest.TestCase): def test_cell(self): for cell in _cells: coordinates, colors, sizes, types, boxes = \ visualization._cell(cell, [1] * cell.dimensions, [i + 1 for i in range(cell.atom_types)]) # Make sure output is reasonable count = sum(cell.atom_counts) self.assertEqual(coordinates.shape[0], count)
class EvaluateTests(unittest.TestCase): # Try many different cells _cases = [ (crystal.Cell(2.1 * numpy.eye(3), [ numpy.array([[0.01, 0.02, 0.03]]), numpy.array([[0.98, 0.97, 0.99]]) ]), { (0, 0): potential.LennardJonesType(s=3.0**0.5, lambda_=-1), (1, 1): potential.LennardJonesType(s=3.0**0.5, lambda_=-1), (0, 1): potential.LennardJonesType(s=3.0**0.5) }, 6, None), (crystal.Cell(100 * numpy.eye(3), [numpy.array([[49, 50, 50], [51, 50, 50]])]), { (0, 0): potential.LennardJonesType(s=2) }, 3, -0.5), (crystal.Cell(100 * numpy.eye(3), [numpy.array([[49, 50, 50], [51, 50, 50]])]), { (0, 0): potential.LennardJonesType() }, 3, None), (crystal.Cell(100 * numpy.eye(3), [numpy.array([[99, 50, 50], [1, 50, 50]])]), { (0, 0): potential.LennardJonesType(s=2) }, 3, -0.5), (crystal.Cell(100 * numpy.eye(3), [numpy.array([[99, 50, 50], [1, 50, 50]])]), { (0, 0): potential.LennardJonesType() }, 3, None), (crystal.Cell(100 * numpy.eye(3), [ numpy.array([[49, 49, 50], [51, 51, 50], [49, 51, 50], [51, 49, 50]]) ]), { (0, 0): potential.LennardJonesType() }, 3, None), (crystal.Cell(100 * numpy.eye(3), [ numpy.array([[-1, -1, 50], [1, 1, 50], [-1, 1, 50], [1, -1, 50]]) ]), { (0, 0): potential.LennardJonesType() }, 3, None), (crystal.Cell(numpy.array([[100, 0, 0], [100, 100, 0], [0, 0, 100]]), [ numpy.array([[49, 49, 50], [51, 51, 50], [51, 49, 50], [149, 51, 50]]) ]), { (0, 0): potential.LennardJonesType() }, 3, None), (crystal.Cell(numpy.array([[100, 0, 0], [100, 100, 0], [0, 0, 100]]), [ numpy.array([[49, 49, 50], [51, 51, 50]]), numpy.array([[51, 49, 50], [149, 51, 50]]) ]), { (0, 0): potential.LennardJonesType(), (0, 1): potential.LennardJonesType(), (1, 1): potential.LennardJonesType(lambda_=-1) }, 3, None), (crystal.Cell( numpy.array([[100, 0, 0], [100, 100, 0], [100, 100, 100]]), [ numpy.array([[100, 100, 100], [101, 101, 101], [102, 102, 102], [103, 103, 103]]) ]), { (0, 0): potential.LennardJonesType() }, 3, None), (crystal.Cell(numpy.eye(3), [0.5 * numpy.zeros((1, 3))]), { (0, 0): potential.LennardJonesType(s=1) }, 6, None), (crystal.Cell(numpy.array([[1, 0, 0], [6, 1, 0], [0, 0, 1]]), [numpy.array([[3, 0.5, 0.5]])]), { (0, 0): potential.LennardJonesType(s=1) }, 6, None), (crystal.Cell( numpy.array([[1, 0.01, 0.02], [0.03, 1, 0.04], [0.05, 0.06, 1]]), [ numpy.array([[0.25, 0.26, 0.27]]), numpy.array([[0.75, 0.76, 0.77]]) ]), { (0, 0): potential.LennardJonesType(s=0.7, n=24), (1, 1): potential.LennardJonesType(s=0.8, n=12), (0, 1): potential.LennardJonesType(s=0.75, n=9) }, 6, None), (crystal.CellTools.wrap( crystal.Cell( numpy.array([[1, 0.01, 0.02], [0.03, 1, 0.04], [0.05, 0.06, 1]]), [ numpy.array([[0.95, 0.96, 0.97]]), numpy.array([[1.45, 1.46, 1.47]]) ])), { (0, 0): potential.LennardJonesType(s=0.7, n=24), (1, 1): potential.LennardJonesType(s=0.8, n=12), (0, 1): potential.LennardJonesType(s=0.75, n=9) }, 6, None), (crystal.Cell( numpy.array([[1.1, 0.2, 0.4], [0.6, 1, 0.8], [1.2, 1.4, 1]]), [numpy.array([[1, 1, 1.1], [1.6, 1.8, 1.7]])]), { (0, 0): potential.LennardJonesType(s=0.3, n=24) }, 6, None), (crystal.Cell( numpy.array([[2.66703688, -0.32850482, -0.12184191], [1.11351198, 1.61363475, 0.0948566], [0.98523977, 0.40819993, 1.64547459]]), [ numpy.array([[0.05395002, -0.09955472, -0.12233975]]), numpy.array([[0.76254656, 0.57095924, 0.45567309]]) ]), { (0, 0): potential.LennardJonesType(lambda_=-1), (1, 1): potential.LennardJonesType(lambda_=-1), (0, 1): potential.LennardJonesType() }, 6, -1.33496937438), (crystal.Cell( numpy.array([[2.6899529, 0, 0], [0.90266756, 1.74296159, 0], [0.85246376, 0.65541009, 1.63971167]]), [ numpy.array([[0.07118973, -0.1012279, -0.1116857]]), numpy.array([[0.66568335, 0.69580262, 0.43339827]]) ]), { (0, 0): potential.LennardJonesType(lambda_=-1), (1, 1): potential.LennardJonesType(lambda_=-1), (0, 1): potential.LennardJonesType() }, 6, -1.33496937438), ] def test_methods_jacobian(self): for cell, potentials, cutoff, expected in EvaluateTests._cases: # First, make sure Python and Cython algorithms give identical answers cell = crystal.CellTools.reduce(cell) energy_slow = crystal.CellTools.energy(cell, potentials, cutoff) energy_fast, jacobian_fast = potential._evaluate_fast( cell, potentials, cutoff) self.assertAlmostEqual(energy_slow, energy_fast) if expected is not None: self.assertTrue(numpy.isclose(energy_fast, expected)) # Now use second-order finite differences to make sure gradient is correct delta = 2.0**-26.0 index = 0 for type_index in range(cell.atom_types): for atom_index in range(cell.atom_count(type_index)): for component_index in range(cell.dimensions): low_lists, high_lists = cell.atom_lists, cell.atom_lists low_lists[type_index][atom_index][ component_index] -= delta high_lists[type_index][atom_index][ component_index] += delta low_energy = potential._evaluate_fast( crystal.CellTools.wrap( crystal.Cell(cell.vectors, low_lists)), potentials, cutoff)[0] high_energy = potential._evaluate_fast( crystal.CellTools.wrap( crystal.Cell(cell.vectors, high_lists)), potentials, cutoff)[0] derivative = (high_energy - low_energy) / (delta * 2) self.assertTrue( numpy.isclose(derivative, jacobian_fast[index], atol=1e-4, rtol=1e-4)) index += 1 def test_histogram_correct(self): # Construct a histogram manually using cell RDF method cell = crystal.Cell( numpy.eye(3), [numpy.zeros((1, 3)), numpy.array([[0.3] * 3, [0.65] * 3])]) histogram = numpy.zeros((2, 2, 20)) for source in range(2): for target in range(2): for key, value in cell.rdf(source, target, 5).items(): factor = 0.5 + (4 * key) - int(numpy.round(4 * key)) histogram[source, target, max(0, int(numpy.round(4 * key)) - 1)] += value * (1 - factor) histogram[source, target, min(19, int(numpy.round(4 * key)))] += value * factor # Construct a fast histogram and compare self.assertTrue( numpy.all( numpy.isclose(histogram, potential._evaluate_fast(cell, None, 5, 0.25)[2]))) def test_histogram_binning(self): # Check for proper binning (number of bins) cell = crystal.Cell( numpy.eye(3), [numpy.zeros((1, 3)), numpy.array([[0.3] * 3, [0.65] * 3])]) self.assertEqual( potential._evaluate_fast(cell, None, 6, 0.25)[2].shape[2], 24) self.assertEqual( potential._evaluate_fast(cell, None, 6.01, 0.25)[2].shape[2], 25) self.assertEqual( potential._evaluate_fast(cell, None, 4, 5)[2].shape[2], 1) def test_double(self): # Simple check with pairwise interaction direction energy, jacobian = potential._evaluate_fast( *EvaluateTests._cases[2][:3]) self.assertLess(jacobian[0], 0) self.assertGreater(jacobian[3], 0) def test_double_wrap(self): # Make sure wrapping is working properly energy, jacobian = potential._evaluate_fast( *EvaluateTests._cases[4][:3]) self.assertLess(jacobian[0], 0) self.assertGreater(jacobian[3], 0) def test_skewed(self): # Make sure that highly distorted cells evaluate properly energy_1, jacobian_1 = potential._evaluate_fast( *EvaluateTests._cases[10][:3]) energy_2, jacobian_2 = potential._evaluate_fast( *EvaluateTests._cases[11][:3]) self.assertTrue(numpy.all(numpy.isclose(energy_1, energy_2))) self.assertTrue(numpy.all(numpy.isclose(jacobian_1, jacobian_2))) def test_translated(self): # Make sure that an arbitrary translation does not affect anything energy_1, jacobian_1 = potential._evaluate_fast( *EvaluateTests._cases[12][:3]) energy_2, jacobian_2 = potential._evaluate_fast( *EvaluateTests._cases[13][:3]) self.assertTrue(numpy.all(numpy.isclose(energy_1, energy_2))) self.assertTrue(numpy.all(numpy.isclose(jacobian_1, jacobian_2)))