def test_a(self): """Test whether SimPhony gives the same result *at* grid points. This test should always pass, since the BZGridQcomplex object is filled by calculating eigen-energies and eigen-vectors using SimPhony. Unfortunately, due to rounding errors, this test can fail to run if an out-of-bounds interpolation point is required by brille. The moveinto keyword is what makes it possible to verify the "interpolation" result against the SimPhony result. This same keyword also opens up the possibility of rounding-errors leading to a runtime error of brille. With the moveinto keyword omitted (or set to True), out-of-zone grid points *will* likely have different polarization vectors compared to the SimPhony result as one interpolates at q=Q-τ and the other calculates at Q. It's unclear if this difference is important for intensity calculations. """ i_data = load_interpolation_data('nb') symsim = BrEu(i_data, halfN=(2, 2, 2)) q_rlu = symsim.grid.rlu int_freq, int_vecs = symsim.frqs_vecs(q_rlu, interpolate=True, moveinto=False) sim_freq, sim_vecs = symsim.frqs_vecs(q_rlu, interpolate=False) ad_freq = np.abs(int_freq - sim_freq).magnitude as_freq = np.abs(int_freq + sim_freq).magnitude # Check if |interpolated-simulated|/|interpolated+simulated|>1e-14 # AND if |interpolated-simulated|>1e-14 unequal_freq = (ad_freq > 1e-14 * as_freq) * (ad_freq > 1e-14) self.assertFalse(unequal_freq.any()) # The vectors only need to be equal up to an arbitrary phase # which is equivalent to saying that the inner product between # equal ion eigenvectors for each branch should have ||ϵ⋅ϵ||²≡1 n_pt, n_br, n_io, n_d = int_vecs.shape int_vecs = int_vecs.reshape(n_pt * n_br * n_io, n_d) sim_vecs = sim_vecs.reshape(n_pt * n_br * n_io, n_d) product = [hermitian_product(x, y) for x, y in zip(int_vecs, sim_vecs)] self.assertTrue(np.isclose(np.abs(product), 1).all())
def test_NaCl(self): # fetch and load the NaCl.castep_bin file from the brille repository idata = get_InterpolationData_object('NaCl') # Do not sort the modes on neighbouring trellis vertices to make comparison with Euphonic easier breu = BrEu(idata, sort=False, trellis=True, max_volume=0.1, parallel=False) # pick a trellis Q vertex inside the irreducible polyhedron, away from the boundaries: q_ir = breu.grid.rlu[6:7] # shape = (1,3) # pull together the pointgroup operations ptgr = b.PointSymmetry(breu.grid.BrillouinZone.lattice.hall) # and apply each to q_ir to find q_nu = R^T_nu q_ir q_nu = np.einsum('xji,aj->xi', ptgr.W, q_ir) # use brille to find eigenenergies and eigenvectors at each q_nu # this *should* be only an application of the rotation, so # all omega_nu are expected to be identical # and all epsilon_nu will be permuted as Gamma(q|nu) dictates br_omega_nu, br_epsilon_nu = breu.frqs_vecs(q_nu, interpolate=True) # use Euphonic to diagonalise the dynamical matrix at each q_nu eu_omega_nu, eu_epsilon_nu = breu.frqs_vecs(q_nu, interpolate=False) # verify that all brille eigenvalues are identical for each q_nu self.assertTrue(np.allclose(np.diff(br_omega_nu.magnitude, axis=0), 0.)) # and that these results match the Euphonic results self.assertTrue(np.allclose(br_omega_nu.magnitude, eu_omega_nu.magnitude)) # Now that we're sure the permutation is the same, we can verify the eigenvectors # brille stores and returns eigenvectors expressed in units of the conventional direct lattice # while euphonic calculates, returns, and stores them in a cartesian coordinate system defined # relative to the lattice by its lat_vec matrix. # A brille.spglib method handles conversion between these descriptions: br_epsilon_nu_xyz = breu.brspgl.conventional_to_orthogonal_eigenvectors(br_epsilon_nu) # The eigenvectors returned by brille and those from Euphonic should be the same up to an overall complex phase factor # the eigenvectors are normalised, so the phase is calculated directly antiphase_per_q_per_mode = np.exp(-1J*np.angle(np.einsum('qmij,qmij->qm', np.conj(eu_epsilon_nu), br_epsilon_nu_xyz))) # and removed from the cartesian coordinate eigenvectors br_epsilon_nu_xyz_phased = np.array([[x0*y0 for x0,y0 in zip(x,y)] for x,y in zip(antiphase_per_q_per_mode, br_epsilon_nu_xyz)]) # now all eigenvectors returned from brille should be the same as # calculated by Euphonic directly self.assertTrue(np.allclose(br_epsilon_nu_xyz_phased, eu_epsilon_nu))