def test_invariance(): for a, b, c in [(2.3, 1.2, 1.7), (1.5, 3.1, 3.1), (0.5, 1.0, 1.0), (0.5, -0.5, 0.5)]: q = np.linspace(0.0, 2 * np.pi / a, 101) x = np.array([-a, a]) h = np.array([b, c]) _, C1 = NonuniformLineScan(x, h).power_spectrum_from_profile( wavevectors=q, algorithm='brute-force', short_cutoff=None, window='None') x = np.array([-a, 0, a]) h = np.array([b, (b + c) / 2, c]) _, C2 = NonuniformLineScan(x, h).power_spectrum_from_profile( wavevectors=q, algorithm='brute-force', short_cutoff=None, window='None') x = np.array([-a, 0, a / 2, a]) h = np.array([b, (b + c) / 2, (3 * c + b) / 4, c]) _, C3 = NonuniformLineScan(x, h).power_spectrum_from_profile( wavevectors=q, algorithm='brute-force', short_cutoff=None, window='None') assert_array_almost_equal(C1, C2) assert_array_almost_equal(C2, C3)
def test_init_with_lists_calling_scale_and_detrend(self): # initialize with lists instead of arrays t = NonuniformLineScan(x=[1, 2, 3, 4], y=[2, 4, 6, 8]) # the following commands should be possible without errors st = t.scale(1) st.detrend(detrend_mode='center')
def test_squeeze(self): x = np.linspace(0, 4 * np.pi, 101) ** (1.3) h = np.sin(x) surface = NonuniformLineScan(x, h).scale(2.0) surface2 = surface.squeeze() self.assertTrue(isinstance(surface2, NonuniformLineScan)) np.testing.assert_allclose(surface.positions(), surface2.positions()) np.testing.assert_allclose(surface.heights(), surface2.heights())
def test_positions_and_heights(self): x = np.array((0, 1, 1.5, 2, 3)) h = 2 * x t = NonuniformLineScan(x, h) assert_array_equal(t.heights(), h) assert_array_equal(t.positions(), x) x2, h2 = t.positions_and_heights() assert_array_equal(x2, x) assert_array_equal(h2, h)
def test_nonuniform_triangle_autocorrelation(): a = 0.7 b = 3 x = np.array([0, b]) t = NonuniformLineScan(x, a * x) r, A = height_height_autocorrelation(t, distances=np.linspace(-4, 4, 101)) assert_almost_equal(A[np.abs(r) < 1e-6][0], a ** 2 * b ** 3 / 3) r3, A3 = height_height_autocorrelation(t.detrend(detrend_mode='center'), distances=[0]) s, = t.physical_sizes assert_almost_equal(A3[0], t.rms_height_from_profile() ** 2 * s) x = np.array([0, 1., 1.3, 1.7, 2.0, 2.5, 3.0]) t = NonuniformLineScan(x, a * x) r2, A2 = height_height_autocorrelation(t, distances=np.linspace(-4, 4, 101)) assert_array_almost_equal(A, A2) r, A = height_height_autocorrelation(t.detrend(detrend_mode='center'), distances=[0]) s, = t.physical_sizes assert_almost_equal(A[0], t.rms_height_from_profile() ** 2 * s)
def test_nonuniform_curvatures(self): radius = 400. center = 50. # generate a nonregular monotically increasing sery of x xs = np.cumsum(np.random.lognormal(size=20)) xs /= np.max(xs) xs *= 80. heights = 1 / (2 * radius) * (xs - center) ** 2 surface = NonuniformLineScan(xs, heights) detrended = surface.detrend(detrend_mode="curvature") self.assertAlmostEqual(abs(detrended.curvatures[0]), 1 / radius)
def test_slope_distribution_simple_line_scan(): x = np.array((1, 2, 3, 4)) y = -2 * x t = NonuniformLineScan(x, y).detrend(detrend_mode='center') topography = FakeTopographyModel(t) result = slope_distribution(topography, bins=3) assert sorted(result.keys()) == EXPECTED_KEYS_FOR_DIST_ANALYSIS assert result['name'] == 'Slope distribution' assert result['scalars'] == { 'Mean Slope (x direction)': dict(value=-2., unit='1'), # absolute value of slope 'RMS Slope (x direction)': dict(value=2., unit='1'), # absolute value of slope } assert result['xlabel'] == 'Slope' assert result['ylabel'] == 'Probability' assert result['xunit'] == '1' assert result['yunit'] == '1' assert len(result['series']) == 2 exp_bins = np.array([-2-1/1500, -2, -2+1/1500]) # for slopes exp_slope_dist_values = [0, 1500, 0] # integral with dx=1/3 results to 1 series0 = result['series'][0] np.testing.assert_almost_equal(series0['x'], exp_bins) np.testing.assert_almost_equal(series0['y'], exp_slope_dist_values)
def test_height_distribution_simple_line_scan(): x = np.array((1, 2, 3)) y = 2 * x info = dict(unit='nm') t = NonuniformLineScan(x, y, info=info).detrend(detrend_mode='center') topography = FakeTopographyModel(t) result = height_distribution(topography) assert sorted(result.keys()) == EXPECTED_KEYS_FOR_DIST_ANALYSIS assert result['name'] == 'Height distribution' assert result['scalars'] == { 'Mean Height': {'value': 0, 'unit': 'nm'}, 'RMS Height': {'value': math.sqrt(4. / 3), 'unit': 'nm'}, } assert result['xlabel'] == 'Height' assert result['ylabel'] == 'Probability' assert result['xunit'] == 'nm' assert result['yunit'] == 'nm⁻¹' assert len(result['series']) == 2 exp_bins = np.array([-1, 1]) # expected values for height bins exp_height_dist_values = [1. / 6, 2. / 6] # expected values series0 = result['series'][0] np.testing.assert_almost_equal(series0['x'], exp_bins) np.testing.assert_almost_equal(series0['y'], exp_height_dist_values)
def test_power_spectrum_simple_nonuniform_linescan(): unit = 'nm' x = np.arange(10) y = -2 * x ** 2 # constant curvature t = NonuniformLineScan(x, y, info=dict(unit=unit)).detrend(detrend_mode='center') topography = FakeTopographyModel(t) result = power_spectrum(topography) assert sorted(result.keys()) == EXPECTED_KEYS_FOR_PLOT_CARD_ANALYSIS assert result['name'] == 'Power-spectral density (PSD)' assert result['xlabel'] == 'Wavevector' assert result['ylabel'] == 'PSD' assert result['xunit'] == '{}⁻¹'.format(unit) assert result['yunit'] == '{}³'.format(unit) assert len(result['series']) == 2 s0, s1 = result['series'] assert s0['name'] == '1D PSD along x' assert s1['name'] == '1D PSD along x (incl. unreliable data)'
def test_rms_height_nonuniform(self): numerical = NonuniformLineScan(self.X, self.sinsurf).rms_height_from_profile() analytical = np.sqrt(self.hm**2 / 2) # numerical = np.sqrt(np.trapz(self.sinsurf**2, self.X)) self.assertAlmostEqual(numerical, analytical, self.precision)
def test_attribute_error(self): t = NonuniformLineScan([1, 2, 4], [2, 4, 8]) with self.assertRaises(AttributeError): t.scale_factor # a scaled line scan has a scale_factor self.assertEqual(t.scale(1).scale_factor, 1) # # This should also work after the topography has been pickled # pt = pickle.dumps(t) t2 = pickle.loads(pt) with self.assertRaises(AttributeError): t2.scale_factor # a scaled line scan has a scale_factor self.assertEqual(t2.scale(1).scale_factor, 1)
def test_contact_mechanics_incompatible_topography(): x = np.arange(10) arr = 2 * x info = dict(unit='nm') t = NonuniformLineScan(x, arr, info=info).detrend("center") topography = FakeTopographyModel(t) with pytest.raises(IncompatibleTopographyException): contact_mechanics(topography)
def test_nonuniform_quadratic(self): x = np.linspace(0, 10, 11) ** 1.3 a = 1.2 b = 1.8 c = 0.3 y = a + b * x + c * x * x / 2 surf = NonuniformLineScan(x, y) self.assertAlmostEqual(surf.rms_curvature_from_profile(), c) surf = surf.detrend(detrend_mode='height') self.assertAlmostEqual(surf.mean(), 0.0) surf.detrend_mode = 'curvature' self.assertAlmostEqual(surf.mean(), 0.0) self.assertAlmostEqual(surf.rms_slope_from_profile(), 0.0) self.assertAlmostEqual(surf.rms_curvature_from_profile(), 0.0)
def test_setting_info_dict(self): x = np.array((0, 1, 1.5, 2, 3)) h = 2 * x t = NonuniformLineScan(x, h) assert t.info == {} t = NonuniformLineScan(x, h, info=dict(unit='A')) assert t.info['unit'] == 'A' # # This info should be inherited in the pipeline # st = t.scale(2) assert st.info['unit'] == 'A' # # It should be also possible to set the info # st = t.scale(2, info=dict(unit='B')) assert st.info['unit'] == 'B' # # Again the info should be passed # dt = st.detrend(detrend_mode='center') assert dt.info['unit'] == 'B' # # Alternatively, it can be changed # dt = st.detrend(detrend_mode='center', info=dict(unit='C')) assert dt.info['unit'] == 'C'
def test_nonuniform_impulse_autocorrelation(): a = 3 b = 2 x = np.array([0, a]) t = NonuniformLineScan(x, b * np.ones_like(x)) r, A = height_height_autocorrelation(t, distances=np.linspace(-4, 4, 101)) A_ref = b ** 2 * (a - np.abs(r)) A_ref[A_ref < 0] = 0 assert_array_almost_equal(A, A_ref) a = 3 b = 2 x = np.array([-a, 0, 1e-9, a - 1e-9, a, 2 * a]) y = np.zeros_like(x) y[2] = b y[3] = b t = NonuniformLineScan(x, y) r, A = height_height_autocorrelation(t, distances=np.linspace(-4, 4, 101)) A_ref = b ** 2 * (a - np.abs(r)) A_ref[A_ref < 0] = 0 assert_array_almost_equal(A, A_ref) t = t.detrend(detrend_mode='center') r, A = height_height_autocorrelation(t, distances=np.linspace(0, 10, 201)) s, = t.physical_sizes assert_almost_equal(A[0], t.rms_height_from_profile() ** 2 * s)
def test_variable_bandwidth_simple_nonuniform_linescan(): x = np.arange(5) h = 2 * x info = dict(unit='nm') t = NonuniformLineScan(x, h, info=info).detrend('center') topography = FakeTopographyModel(t) result = variable_bandwidth(topography) assert sorted(result.keys()) == EXPECTED_KEYS_FOR_PLOT_CARD_ANALYSIS assert result['name'] == 'Variable-bandwidth analysis'
def nonuniform_line_scan(request): """Returns a nonuniform line scan, with all combinations of scaled and detrended. Detrended is always executed after scaling, if requested. Returns ------- A nonuniform line scan. """ x = np.array((0, 0.1, 0.2, 0.4, 0.5)) h = np.sin(x) nonuniform_line_scan = NonuniformLineScan(x, h) return apply_param(nonuniform_line_scan, request.param)
def test_power_spectrum_from_profile(self): # # this test was added, because there were issues calling # power spectrum 1D with a window given # t = NonuniformLineScan(x=[1, 2, 3, 4], y=[2, 4, 6, 8]) q1, C1 = t.power_spectrum_from_profile(window='hann') q1, C1 = t.detrend('center').power_spectrum_from_profile(window='hann') q1, C1 = t.detrend('center').power_spectrum_from_profile() q1, C1 = t.detrend('height').power_spectrum_from_profile(window='hann') q1, C1 = t.detrend('height').power_spectrum_from_profile()
def test_autocorrelation_simple_nonuniform_topography(): x = np.arange(5) h = 2 * x info = dict(unit='nm') t = NonuniformLineScan(x, h, info=info).detrend('center') topography = FakeTopographyModel(t) result = autocorrelation(topography) assert sorted(result.keys()) == EXPECTED_KEYS_FOR_PLOT_CARD_ANALYSIS assert result['name'] == 'Height-difference autocorrelation function (ACF)'
def test_setting_info_dict(self): x = np.array((0, 1, 1.5, 2, 3)) h = 2 * x t = NonuniformLineScan(x, h) assert t.info == {} with pytest.deprecated_call(): t = NonuniformLineScan(x, h, info=dict(unit='A')) t = NonuniformLineScan(x, h, unit='A') with pytest.deprecated_call(): assert t.info['unit'] == 'A' # # This info should be inherited in the pipeline # st = t.scale(2) with pytest.deprecated_call(): assert st.info['unit'] == 'A' # # It should be also possible to set the info # with pytest.deprecated_call(): st = t.scale(2, info=dict(unit='B')) st = t.scale(2, 1, unit='B') with pytest.deprecated_call(): assert st.info['unit'] == 'B' # # Again the info should be passed # dt = st.detrend(detrend_mode='center') with pytest.deprecated_call(): assert dt.info['unit'] == 'B' # # It can no longer be changed in detrend (you need to use scale) # with pytest.deprecated_call(): dt = st.detrend(detrend_mode='center', info=dict(unit='C'))
def test_rectangle(): for a, b in [(2.3, 1.45), (10.2, 0.1)]: x = np.array([-a, a]) h = np.array([b, b]) q = np.linspace(0.01, 8 * np.pi / a, 101) q, C = NonuniformLineScan(x, h).power_spectrum_1D(wavevectors=q, algorithm='brute-force', window='None') C_ana = (2 * b * np.sin(a * q) / q)**2 C_ana /= 2 * a assert_array_almost_equal(C, C_ana)
def test_triangle(): for a, b in [(0.5, -0.5), (1, 1), (2.3, 1.45), (10.2, 0.1)]: x = np.array([-a, a]) h = np.array([-b, b]) q = np.linspace(0.01, 8 * np.pi / a, 101) _, C = NonuniformLineScan(x, h).power_spectrum_from_profile( wavevectors=q, algorithm='brute-force', short_cutoff=None, window='None') C_ana = (2 * b * (a * q * np.cos(a * q) - np.sin(a * q)) / (a * q**2))**2 C_ana /= 2 * a assert_array_almost_equal(C, C_ana)
def test_rectangle_and_triangle(): for a, b, c, d in [(0.123, 1.45, 10.1, 9.3), (-0.1, 5.4, -0.1, 3.43), (-1, 1, 1, 1)]: x = np.array([a, b]) h = np.array([c, d]) q = np.linspace(0.01, 8 * np.pi / (b - a), 101) q, C = NonuniformLineScan(x, h).power_spectrum_from_profile( wavevectors=q, algorithm='brute-force', window='None') C_ana = np.exp(-1j * (a + b) * q) * (np.exp(1j * a * q) * (c - d + 1j * (a - b) * d * q) + np.exp(1j * b * q) * (d - c - 1j * (a - b) * c * q)) / ((a - b) * q**2) C_ana = np.abs(C_ana)**2 / (b - a) assert_array_almost_equal(C, C_ana)
def test_nonuniform2(self): x = np.array((1, 2, 3)) y = 2 * x surf = NonuniformLineScan(x, y) self.assertFalse(surf.is_uniform) self.assertEqual(surf.dim, 1) der = surf.derivative(n=1) assert_array_equal(der, [2, 2]) der = surf.derivative(n=2) assert_array_equal(der, [0]) surf = surf.detrend(detrend_mode='height') self.assertFalse(surf.is_uniform) self.assertEqual(surf.dim, 1) der = surf.derivative(n=1) assert_array_equal(der, [0, 0]) assert_array_equal(surf.heights(), np.zeros(y.shape)) p = surf.positions_and_heights() assert_array_equal(p[0], x) assert_array_equal(p[1], np.zeros(y.shape))
def simple_surface(): class WrapTopography: def __init__(self, t): self._t = t def topography(self): return self._t class WrapRequest: def __init__(self, c): self._c = c def all(self): return self._c class WrapSurface: def __init__(self, c): self._c = c @property def topography_set(self): return WrapRequest(self._c) nx, ny = 113, 123 sx, sy = 1, 1 lx = 0.3 topographies = [ Topography(np.resize(np.sin(np.arange(nx) * sx * 2 * np.pi / (nx * lx)), (nx, ny)), (sx, sy), periodic=False, unit='um') ] nx = 278 sx = 100 lx = 2 x = np.arange(nx) * sx / nx topographies += [ NonuniformLineScan(x, np.cos(x * np.pi / lx), unit='nm') ] return WrapSurface([WrapTopography(t) for t in topographies])
def test_curvature_distribution_simple_line_scan(): unit = 'nm' x = np.arange(10) y = -2 * x ** 2 # constant curvature t = NonuniformLineScan(x, y, info=dict(unit=unit)).detrend(detrend_mode='center') topography = FakeTopographyModel(t) bins = np.array((-4.75, -4.25, -3.75, -3.25)) # special for this test in order to know results result = curvature_distribution(topography, bins=bins) assert sorted(result.keys()) == EXPECTED_KEYS_FOR_DIST_ANALYSIS assert result['name'] == 'Curvature distribution' assert pytest.approx(result['scalars']['Mean Curvature']['value']) == -4 assert pytest.approx(result['scalars']['RMS Curvature']['value']) == 4 assert result['scalars']['Mean Curvature']['unit'] == '{}⁻¹'.format(unit) assert result['scalars']['RMS Curvature']['unit'] == '{}⁻¹'.format(unit) assert result['xlabel'] == 'Curvature' assert result['ylabel'] == 'Probability' assert result['xunit'] == '{}⁻¹'.format(unit) assert result['yunit'] == unit assert len(result['series']) == 2 exp_bins = (bins[1:] + bins[:-1]) / 2 exp_curv_dist_values = [0, 2, 0] # integral over dx= should be 1 assert np.trapz(exp_curv_dist_values, exp_bins) == pytest.approx(1) series0 = result['series'][0] np.testing.assert_almost_equal(series0['x'], exp_bins) np.testing.assert_almost_equal(series0['y'], exp_curv_dist_values)
def test_nonuniform3(self): x = np.array((1, 2, 3, 4)) y = -2 * x surf = NonuniformLineScan(x, y) self.assertFalse(surf.is_uniform) self.assertEqual(surf.dim, 1) der = surf.derivative(n=1) assert_array_equal(der, [-2, -2, -2]) der = surf.derivative(n=2) assert_array_equal(der, [0, 0]) # # Similar with detrend which substracts mean value # surf2 = surf.detrend(detrend_mode='center') self.assertFalse(surf2.is_uniform) self.assertEqual(surf.dim, 1) der = surf2.derivative(n=1) assert_array_equal(der, [-2, -2, -2]) # # Similar with detrend which eliminates slope # surf3 = surf.detrend(detrend_mode='height') self.assertFalse(surf3.is_uniform) self.assertEqual(surf.dim, 1) der = surf3.derivative(n=1) np.testing.assert_allclose(der, [0, 0, 0], atol=1e-14) np.testing.assert_allclose(surf3.heights(), np.zeros(y.shape), atol=1e-14) p = surf3.positions_and_heights() assert_array_equal(p[0], x) np.testing.assert_allclose(p[1], np.zeros(y.shape), atol=1e-14)
def test_nonuniform_linear(self): x = np.linspace(0, 10, 11) ** 2 y = 1.8 * x + 1.2 surf = NonuniformLineScan(x, y).detrend(detrend_mode='height') self.assertAlmostEqual(surf.mean(), 0.0) self.assertAlmostEqual(surf.rms_slope_from_profile(), 0.0)
def test_rms_slope_nonuniform(self): numerical = NonuniformLineScan(self.X, self.sinsurf).rms_slope_from_profile() analytical = np.sqrt(2 * np.pi**2 * self.hm**2 / self.L**2) # print(numerical-analytical) self.assertAlmostEqual(numerical, analytical, self.precision)
def test_properties(self): x = np.array((0, 1, 1.5, 2, 3)) h = 2 * x t = NonuniformLineScan(x, h) self.assertEqual(t.dim, 1)