From 2cc0bff055319bfb9f20ea6d8a75aa6c285889bc Mon Sep 17 00:00:00 2001 From: Anton Akhmerov <anton.akhmerov@gmail.com> Date: Sat, 25 Feb 2012 15:47:46 +0100 Subject: [PATCH] add solver option to return modes (for testing and superconducting systems) --- kwant/solvers/sparse.py | 48 +++++++++++++++++++++++------- kwant/solvers/tests/test_sparse.py | 10 +++++++ 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/kwant/solvers/sparse.py b/kwant/solvers/sparse.py index 05b6c010..c418b325 100644 --- a/kwant/solvers/sparse.py +++ b/kwant/solvers/sparse.py @@ -71,7 +71,7 @@ def factorized(A, piv_tol=1.0, sym_piv_tol=1.0): return solve def make_linear_sys(sys, out_leads, in_leads, energy=0, - force_realspace=False): + force_realspace=False, return_modes=False): """ Make a sparse linear system of equations defining a scattering problem. @@ -90,16 +90,22 @@ def make_linear_sys(sys, out_leads, in_leads, energy=0, calculate Green's function between the outermost lead sites, instead of lead modes. This is almost always more computationally expensive and less stable. + return_modes : bool + additionally return the wave functions of modes in the leads, as + returned by `kwant.physics.selfenergy.modes`. Returns ------- - (h_sys, rhs, keep_vars, num_modes) : tuple of inhomogeneous data + (h_sys, rhs, keep_vars, num_modes, modes) : tuple of inhomogeneous data `h_sys` is a numpy.sparse.csc_matrix, containing the right hand side of the system of equations, `rhs` is the list of matrices with the left hand side, `keep_vars` is a list with numbers of variables in the solution that have to be stored (typically a small part of the - complete solution). Finally, `num_modes` is a list with number of - propagating modes or lattice points in each lead. + complete solution), and `num_modes` is a list with number of + propagating modes or lattice points in each lead. Finally, if + `return_modes == True`, a list `modes` is also returned, which + contains mode wave functions in each lead that is defined as a + tight-binding system, and the lead self-energy otherwise. Notes ----- @@ -123,6 +129,8 @@ def make_linear_sys(sys, out_leads, in_leads, energy=0, keep_vars = [] rhs = [] num_modes = [] + if return_modes: + mode_wave_functions = [] for leadnum, lead_neighbors in enumerate(sys.lead_neighbor_seqs): lead = sys.leads[leadnum] if isinstance(lead, system.InfiniteSystem) and not force_realspace: @@ -131,10 +139,13 @@ def make_linear_sys(sys, out_leads, in_leads, energy=0, v = lead.inter_slice_hopping() if not np.any(v): num_modes.append(0) + if return_modes: + mode_wave_functions.append(physics.modes(h, v)) continue u, ulinv, nprop, svd = physics.modes(h, v) - + if return_modes: + mode_wave_functions.append((u, ulinv, nprop, svd)) num_modes.append(nprop) if leadnum in out_leads: @@ -177,6 +188,8 @@ def make_linear_sys(sys, out_leads, in_leads, energy=0, rhs.append(sp.bmat([[vdaguin_sp], [lead_mat_in]])) else: sigma = lead.self_energy(energy) + if return_modes: + mode_wave_functions.append(sigma) num_modes.append(sigma) indices = np.r_[tuple(range(offsets[i], offsets[i + 1]) for i in lead_neighbors)] @@ -192,7 +205,10 @@ def make_linear_sys(sys, out_leads, in_leads, energy=0, rhs.append(sp.coo_matrix((-np.ones(l), [indices, np.arange(l)]))) - return h_sys, rhs, keep_vars, num_modes + result = (h_sys, rhs, keep_vars, num_modes) + if return_modes: + result += (mode_wave_functions,) + return result def solve_linsys(a, b, keep_vars=None): @@ -235,7 +251,7 @@ def solve_linsys(a, b, keep_vars=None): def solve(sys, energy=0, out_leads=None, in_leads=None, - force_realspace=False): + force_realspace=False, return_modes=False): """ Calculate a Green's function of a system. @@ -254,12 +270,18 @@ def solve(sys, energy=0, out_leads=None, in_leads=None, calculate Green's function between the outermost lead sites, instead of lead modes. This is almost always more computationally expensive and less stable. + return_modes : bool + additionally return the wave functions of modes in the leads, as + returned by `kwant.physics.selfenergy.modes`. Returns ------- - output: `BlockResult` + output : `BlockResult` see notes below and `BlockResult` docstring for more information about the output format. + modes : list + a list with wave functions of the lead modes (only returned if + `return_modes == True`). Notes ----- @@ -294,13 +316,19 @@ def solve(sys, energy=0, out_leads=None, in_leads=None, if len(in_leads) == 0 or len(out_leads) == 0: raise ValueError('No output is requested.') linsys = make_linear_sys(sys, out_leads, in_leads, energy, - force_realspace) + force_realspace, return_modes) + if return_modes: + mode_wave_functions = linsys[-1] + linsys = linsys[: -1] out_modes = [len(i) for i in linsys[2]] in_modes = [i.shape[1] for i in linsys[1]] result = BlockResult(solve_linsys(*linsys[: -1]), linsys[3]) result.in_leads = in_leads result.out_leads = out_leads - return result + if not return_modes: + return result + else: + return result, mode_wave_functions class BlockResult(namedtuple('BlockResultTuple', ['data', 'num_modes'])): diff --git a/kwant/solvers/tests/test_sparse.py b/kwant/solvers/tests/test_sparse.py index 7a03c297..06ef4d3f 100644 --- a/kwant/solvers/tests/test_sparse.py +++ b/kwant/solvers/tests/test_sparse.py @@ -42,6 +42,16 @@ def test_output(): assert_almost_equal(s1, s2) assert_almost_equal(s.H * s, np.identity(s.shape[0])) assert_raises(ValueError, solve, fsys, 0, []) + modes = solve(fsys, return_modes=True)[1] + h = fsys.leads[0].slice_hamiltonian() + t = fsys.leads[0].inter_slice_hopping() + modes1 = kwant.physics.modes(h, t) + h = fsys.leads[1].slice_hamiltonian() + t = fsys.leads[1].inter_slice_hopping() + modes2 = kwant.physics.modes(h, t) + print modes1, modes + assert_almost_equal(modes1[0], modes[0][0]) + assert_almost_equal(modes2[1], modes[1][1]) # Test that a system with one lead has unitary scattering matrix. -- GitLab