def test_construction(): """ Test that we can construct a NEGF instance, without performing any operation. """ foo = pynegf.PyNegf() assert(foo is not None)
def test_current_conservation_dephasing(coupling=None): """ Test that we have current conservation at the electrodes """ currents = [] for orthogonal in [True, False]: for (ni, nf) in [(1, 2), (2, 1)]: negf = pynegf.PyNegf() # Build the sparse hamiltonian for the nearest-neighbor linear chain. mat_csr = utils.orthogonal_linear_chain(nsites=50, contact_size=10, coupling=1.0) if orthogonal: negf.set_hamiltonian(mat_csr) # Set an identity overlap matrix. negf.set_identity_overlap(50) else: negf.set_hamiltonian(mat_csr) mat_csr = utils.orthogonal_linear_chain(nsites=50, contact_size=10, coupling=0.1, onsite=1.0) # This is to make sure that S is positive definite. numpy.linalg.cholesky(mat_csr.todense()) # Set an identity overlap matrix. negf.set_overlap(mat_csr) # Initialize the system structure. negf.init_structure(2, numpy.array([39, 49]), numpy.array([29, 39])) # Initialize parameters relevant for the transmission. negf.params.ec = -5.0 negf.params.emin = -0.2 negf.params.emax = 0.2 negf.params.estep = 0.005 negf.params.mu[0] = 0.1 negf.params.mu[1] = -0.1 # IMPORTANT: ni and nf must be both defined. negf.params.ni[0] = ni negf.params.ni[1] = nf negf.params.nf[0] = nf negf.params.nf[1] = ni negf.params.kbt_t[1] = 0.01 negf.params.kbt_t[0] = 0.01 negf.params.np_real[0] = 50 negf.verbosity = 0 negf.set_params() coupling = 0.1 negf.set_diagonal_elph_dephasing(numpy.array([coupling] * 30)) negf.solve_landauer() tmp_currents = negf.currents() assert tmp_currents[0] == pytest.approx(-tmp_currents[1], 1e-9) currents.append(tmp_currents[0]) assert currents[0] == pytest.approx(-currents[1], 1e-9)
def test_density_linear_chain_neq_2d(): """ Test the density matrix calculation at non-equilibrium for a linear chain in full-band. """ negf = pynegf.PyNegf() # Build the sparse hamiltonian for the nearest-neighbor linear chain. mat_csr = utils.orthogonal_square_2d_lattice( nblocks=20, block_size=5, n_contact_blocks=2, coupling=1.0) negf.set_hamiltonian(mat_csr) # Set an identity overlap matrix. negf.set_identity_overlap(100) # Initialize the system structure. negf.init_structure( 2, numpy.array([89, 99]), numpy.array([79, 89])) # Set parameters relevant for the density matrix calculation. # We fully occupy all band and use mostly default values for # the integration contour. negf.params.ec = -5.0 negf.params.mu[0] = 0.1 negf.params.mu[1] = -0.1 negf.params.kbt_dm = (.001, .001) negf.params.g_spin = 2.0 # Not correctly initialized, setting explicitely. negf.params.np_real = tuple([50] * 11) negf.params.verbose = 0 negf.set_params() # Calculate the density matrix. negf.solve_density() density_matrix = negf.density_matrix() diagonal = density_matrix.diagonal() # The system is ballistic, therefore we should have identical # occupation all over the chain when checking equivalent sites. for i in range(5): assert diagonal[i:80:5] == pytest.approx(diagonal[i], abs=1e-4) # The occupation should 1. assert diagonal[0] == pytest.approx(1.0, abs=1e-3) # We should have 2 particles (due to degeneracy) per site. diagonal = density_matrix.diagonal() # The contact density matrix is ignored, therefore it should be zero. assert diagonal[80:] == pytest.approx(0.0)
def test_transmission_linear_chain(): """ Test that we can calculate the transmission for an ideal linear chain. """ negf = pynegf.PyNegf() # Build the sparse hamiltonian for the nearest-neighbor linear chain. mat_csr = utils.orthogonal_linear_chain( nsites=100, contact_size=20, coupling=1.0) negf.set_hamiltonian(mat_csr) # Set an identity overlap matrix. negf.set_identity_overlap(100) # Initialize the system structure. negf.init_structure( 2, numpy.array([79, 99]), numpy.array([59, 79]), numpy.array([14, 29, 44, 59]), numpy.array([3, 0])) # Initialize parameters relevant for the transmission. negf.params.g_spin = 1 negf.params.emin = -3.0 negf.params.emax = 3.0 negf.params.estep = 0.01 negf.params.mu[0] = -0.5 negf.params.mu[1] = 0.5 negf.set_params() # negf.print_tnegf() # Set also some local DOS intervals. negf.set_ldos_intervals(numpy.array([0, 30, 0]), numpy.array([59, 59, 29])) negf.solve_landauer() # Get transmission, dos and energies as numpy object energies = negf.energies() transmission = negf.transmission() ldos = negf.ldos() # The system is homogeneous, therefore the first LDOS should be equal to # the sum of the second and third. assert ldos[0, :] == pytest.approx(ldos[1, :] + ldos[2, :]) # Check ldos shape. _check_ldos_shape(ldos, energies) # The current in ballistic current units should be equal the # spin degeneracy we set, i.e. 1 currents = negf.currents() currents[0] == pytest.approx(1.0) # Check that the transmission has the right integer values. _check_transmission_values(transmission, energies)
def test_density_linear_chain_neq_bias(): """ Test the density matrix calculation at non-equilibrium for a linear chain in full-band. """ negf = pynegf.PyNegf() # Build the sparse hamiltonian for the nearest-neighbor linear chain. mat_csr = utils.orthogonal_linear_chain( nsites=100, contact_size=20, coupling=1.0) negf.set_hamiltonian(mat_csr) # Set an identity overlap matrix. negf.set_identity_overlap(100) # Initialize the system structure. negf.init_structure( 2, numpy.array([79, 99]), numpy.array([59, 79])) # Set parameters relevant for the density matrix calculation. # We fully occupy all band and use mostly default values for # the integration contour. negf.params.ec = -2.5 negf.params.mu[0] = 0.1 negf.params.mu[1] = -0.1 negf.params.kbt_dm = (.001, .001) negf.params.g_spin = 2.0 negf.params.np_real[0] = 50 negf.params.verbose = 0 negf.set_params() # Calculate the density matrix. negf.solve_density() density_matrix = negf.density_matrix() diagonal = density_matrix.diagonal() # The system is ballistic, therefore we should have identical # occupation all over the chain. assert diagonal[:60] == pytest.approx(diagonal[0], abs=1e-4) # The occupation should 1. assert diagonal[0] == pytest.approx(1.0, abs=1e-4) # We should have 2 particles (due to degeneracy) per site. diagonal = density_matrix.diagonal() # The contact density matrix is ignored, therefore it should be zero. assert diagonal[60:] == pytest.approx(0.0)
def test_transmission_automatic_partition_2d(): """ Test that we can calculate the transmission for a 2d hamiltonian. """ negf = pynegf.PyNegf() # Build the sparse hamiltonian for the nearest-neighbor linear chain. mat_csr = utils.orthogonal_square_2d_lattice( nblocks=20, block_size=5, n_contact_blocks=2, coupling=1.0) negf.set_hamiltonian(mat_csr) # Set an identity overlap matrix. negf.set_identity_overlap(100) # Initialize the system structure. negf.init_structure( 2, numpy.array([89, 99]), numpy.array([79, 89])) # Initialize parameters relevant for the transmission. negf.params.g_spin = 1 negf.params.emin = -5.0 negf.params.emax = 5.0 negf.params.estep = 0.01 negf.params.mu[0] = 0.05 negf.params.mu[0] = -0.05 negf.set_params() # negf.print_tnegf() # Set also some local DOS intervals. negf.set_ldos_intervals(numpy.array([0, 30, 0]), numpy.array([59, 59, 29])) negf.solve_landauer() # Get transmission, dos and energies as numpy object transmission = negf.transmission() ldos = negf.ldos() # The system is homogeneous, therefore the first LDOS should be equal to # the sum of the second and third. assert ldos[0, :] == pytest.approx(ldos[1, :] + ldos[2, :]) # The current in ballistic current units should be equal the # spin degeneracy we set times 5 (number of bands) times delta_mu, i.e. 0.5 currents = negf.currents() currents[0] == pytest.approx(0.5) # Check that the transmission peaks at 5.0. assert numpy.max(transmission[0, :]) == pytest.approx(5.0)
def _density_linear_chain_dephasing(coupling=None, orthogonal=True): """ Utility to calculate the density matrix in presence of diagonal dephasing for a nearest neighbor linear chain. """ negf = pynegf.PyNegf() # Build the sparse hamiltonian for the nearest-neighbor linear chain. mat_csr = utils.orthogonal_linear_chain(nsites=50, contact_size=10, coupling=1.0) if orthogonal: negf.set_hamiltonian(mat_csr) # Set an identity overlap matrix. negf.set_identity_overlap(50) else: negf.set_hamiltonian(mat_csr) mat_csr = utils.orthogonal_linear_chain(nsites=50, contact_size=10, coupling=0.1, onsite=1.0) # This is to make sure that S is positive definite. numpy.linalg.cholesky(mat_csr.todense()) # Set an identity overlap matrix. negf.set_overlap(mat_csr) # Initialize the system structure. negf.init_structure(2, numpy.array([39, 49]), numpy.array([29, 39])) # Initialize parameters relevant for the density matrix calculation. negf.params.ec = -3.5 negf.params.mu[0] = -0.1 negf.params.mu[1] = 0.1 negf.params.kbt_dm[0] = 0.001 negf.params.kbt_dm[1] = 0.001 negf.params.np_real[0] = 50 negf.params.verbose = 100 negf.set_params() if coupling is not None: negf.set_diagonal_elph_dephasing(numpy.array([coupling] * 30)) negf.solve_density() # Get the density matrix. density_matrix = negf.density_matrix() return density_matrix
def test_density_linear_chain_eq(): """ Test the density matrix calculation at equilibrium for a linear chain in full-band. """ negf = pynegf.PyNegf() # Build the sparse hamiltonian for the nearest-neighbor linear chain. mat_csr = utils.orthogonal_linear_chain( nsites=100, contact_size=20, coupling=1.0) negf.set_hamiltonian(mat_csr) # Set an identity overlap matrix. negf.set_identity_overlap(100) # Initialize the system structure. negf.init_structure( 2, numpy.array([79, 99]), numpy.array([59, 79])) # Set parameters relevant for the density matrix calculation. # We fully occupy all band and use mostly default values for # the integration contour. negf.params.ec = -2.5 negf.params.mu[0] = 0.0 negf.params.mu[1] = 0.0 negf.params.kbt_dm = (.001, .001) negf.params.g_spin = 2.0 # Not correctly initialized, setting explicitely. negf.params.np_real = tuple([0] * 11) negf.params.verbose = 0 negf.set_params() # Calculate the density matrix. negf.solve_density() density_matrix = negf.density_matrix() # We should have 1 particles (2 degeneracy, half band occupied) per site. diagonal = density_matrix.diagonal() assert diagonal[:60] == pytest.approx(1.0) # The contact density matrix is ignored, therefore it should be zero. assert diagonal[60:] == pytest.approx(0.0)
def _transmission_linear_chain_dephasing(coupling=None): """ Utility to calculate the transmission in presence of diagonal dephasing for a nearest neighbor linear chain. """ negf = pynegf.PyNegf() # Build the sparse hamiltonian for the nearest-neighbor linear chain. mat_csr = utils.orthogonal_linear_chain(nsites=100, contact_size=10, coupling=1.0) negf.set_hamiltonian(mat_csr) # Set an identity overlap matrix. negf.set_identity_overlap(100) # Initialize the system structure. negf.init_structure(2, numpy.array([89, 99]), numpy.array([79, 89])) # Initialize parameters relevant for the transmission. negf.params.g_spin = 1 negf.params.emin = -2.5 negf.params.emax = 2.5 negf.params.estep = 0.025 negf.params.mu[0] = 2.1 negf.params.mu[1] = -2.1 negf.verbosity = 0 negf.set_params() if coupling is not None: negf.set_diagonal_elph_dephasing(numpy.array([coupling] * 80)) negf.solve_landauer() # Get transmission, dos and energies as numpy object energies = negf.energies() if coupling is None: transmission = negf.transmission() else: transmission = negf.energy_current() return energies, transmission
def transmission_linear_chain(): """ Calculate the transmission for a linear chain model hamiltonian. """ # Start an instance of the library. negf = pynegf.PyNegf() # Build the sparse hamiltonian for the nearest-neighbor linear chain. mat = numpy.zeros(shape=(100, 100), dtype='complex128') for ii in range(80): mat[ii, ii - 1] = 1.0 mat[ii - 1, ii] = 1.0 for ii in range(81, 100): mat[ii, ii - 1] = 1.0 mat[ii - 1, ii] = 1.0 mat[0, 80] = 1.0 mat[80, 0] = 1.0 mat_csr = scipy.sparse.csr_matrix(mat) mat_csr.sort_indices() # Pass the hamiltonian to libnegf. negf.set_hamiltonian(mat_csr) # Set an identity overlap matrix. negf.set_identity_overlap(100) # Initialize the system structure. Here we specify the following # parameters: # number of contact: 2 # start-end index of first contact: numpy.array([80, 100]) # start-end index of second contact: numpy.array([60, 80]) # end-index of each layer, for the iterative algorithm: numpy.array([15, 30, 45, 60]) # index of bloks interacting with the contact: numpy.array([4, 1]) negf.init_structure( 2, numpy.array([80, 100]), numpy.array([60, 80]), numpy.array([15, 30, 45, 60]), numpy.array([4, 1])) # Initialize parameters relevant for the transmission. # the chemical potential mu is used for evaluating the current only. negf.params.emin = -3.0 negf.params.emax = 3.0 negf.params.estep = 0.01 negf.params.mu[0] = 0.1 negf.set_params() negf.print_tnegf() # Set also some local DOS intervals. negf.set_ldos_intervals(numpy.array([1, 31, 1]), numpy.array([60, 60, 30])) negf.solve_landauer() # Get transmission, dos and energies as numpy object. energies = negf.energies() print('energies', energies) trans = negf.transmission() ldos = negf.ldos() currents = negf.currents() print('Currents',currents) print('trans', trans)