Exemplo n.º 1
0
        /// <summary>
        /// checks to see whether the solution number eigenvalue and eigenvector is unphysical and therefore shouldn't be added
        /// to the boundary condition matrix
        /// </summary>
        bool Add_BC_EigenSolution(int solution_no, DoubleComplexEigDecomp eig_decomp, Boundary boundary)
        {
            double eigenvalue_norm = DoubleComplex.Norm(eig_decomp.EigenValue(solution_no));

            // check for propagating solutions
            if (Math.Abs(eigenvalue_norm - 1.0) < propagating_mode_error)
            {
                if (eig_decomp.EigenValue(solution_no).Imag > 0 && boundary == Boundary.left)
                {
                    return(true);
                }
                else if (eig_decomp.EigenValue(solution_no).Imag < 0 && boundary == Boundary.right)
                {
                    return(true);
                }
                else if (eig_decomp.EigenValue(solution_no).Imag == 0)
                {
                    throw new NotImplementedException();
                }
            }
            // for evanescent solutions, check for unphysical solutions, ie. |alpha| > 1 for left lead
            else if (eigenvalue_norm > 1.0 && boundary == Boundary.left)
            {
                return(true);
            }
            // and |alpha| < 1 for right lead
            else if (eigenvalue_norm < 1.0 && boundary == Boundary.right)
            {
                return(true);
            }

            // if these properties are not satisfied, do not add the eigenvalue and eigenvector to the BC (default behaviour)
            return(false);
        }
Exemplo n.º 2
0
        /// <summary>
        /// returns an eigenvector decomposition for the x-direction after having calculated the density in the
        /// y-direction and storing it in "charge_density"
        /// </summary>
        /// <param name="dft_pot"></param>
        /// <param name="charge_density"></param>
        /// <returns></returns>
        private DoubleHermitianEigDecomp Solve_Eigenvector_Problem(Band_Data dft_pot, ref SpinResolved_Data charge_density)
        {
            DoubleHermitianEigDecomp eig_decomp;

            double[]     x_energy  = new double[nx];
            DoubleMatrix dens_up   = new DoubleMatrix(nx, ny, 0.0);
            DoubleMatrix dens_down = new DoubleMatrix(nx, ny, 0.0);

            // cycle over x-direction calculating the ground state energy and eigenstate in the y-direction
            for (int i = 0; i < nx; i++)
            {
                // pull out the chemical potential for this slice
                double[] y_dft_pot = new double[ny];
                for (int j = 0; j < ny; j++)
                {
                    y_dft_pot[j] = dft_pot.mat[i, j];
                }

                // calculate its eigendecomposition
                DoubleHermitianMatrix h_y = Create_Hamiltonian(y_dft_pot, ty, ny);

                eig_decomp = new DoubleHermitianEigDecomp(h_y);

                // insert the eigenstate into density and record the local confinement energy
                x_energy[i] = eig_decomp.EigenValue(0);
                for (int j = 0; j < ny; j++)
                {
                    double dens_tot = DoubleComplex.Norm(eig_decomp.EigenVector(0)[j]) * DoubleComplex.Norm(eig_decomp.EigenVector(0)[j]);
                    dens_up[i, j]   = 0.5 * dens_tot;
                    dens_down[i, j] = 0.5 * dens_tot;
                }
            }

            // check whether to enforce symmetry in the transverse direction
            if (force_symmetry)
            {
                for (int i = nx / 2 + 1; i < nx; i++)
                {
                    x_energy[i] = x_energy[nx - i - 1];
                    for (int j = 0; j < ny; j++)
                    {
                        dens_up[i, j]   = dens_up[nx - i - 1, j];
                        dens_down[i, j] = dens_down[nx - i - 1, j];
                    }
                }
            }

            // calculate the eigenstates in the x-direction
            DoubleHermitianMatrix h_x = Create_Hamiltonian(x_energy, tx, nx);

            eig_decomp = new DoubleHermitianEigDecomp(h_x);
            energies   = eig_decomp.EigenValues;

            // put the calculated densities into charge_density
            charge_density = new SpinResolved_Data(new Band_Data(dens_up), new Band_Data(dens_down));

            return(eig_decomp);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Calculate the charge density for this potential
        /// NOTE!!! that the boundary potential is (essentially) set to infty by ringing the density with a set of zeros.
        ///         this prevents potential solvers from extrapolating any residual density at the edge of the eigenstate
        ///         solution out of the charge density calculation domain
        /// </summary>
        /// <param name="layers"></param>
        /// <param name="charge_density"></param>
        /// <param name="chem_pot"></param>
        public override void Get_ChargeDensity(ILayer[] layers, ref SpinResolved_Data charge_density, Band_Data chem_pot)
        {
            // convert the chemical potential into a quantum mechanical potential
            Band_Data dft_pot = chem_pot.DeepenThisCopy();

            Get_Potential(ref dft_pot, layers);

            DoubleHermitianMatrix          hamiltonian = Create_Hamiltonian(layers, dft_pot);
            DoubleHermitianEigDecompServer eig_server  = new DoubleHermitianEigDecompServer();

            eig_server.ComputeEigenValueRange(dft_pot.mat.Min(), no_kb_T * Physics_Base.kB * temperature);
            eig_server.ComputeVectors = true;
            DoubleHermitianEigDecomp eig_decomp = eig_server.Factor(hamiltonian);

            int max_wavefunction = 0;

            if (eig_decomp.EigenValues.Length != 0)
            {
                double min_eigval = eig_decomp.EigenValues.Min();
                max_wavefunction = (from val in eig_decomp.EigenValues
                                    where val < no_kb_T * Physics_Base.kB * temperature
                                    select val).ToArray().Length;
            }

            for (int i = 0; i < nx; i++)
            {
                for (int j = 0; j < ny; j++)
                {
                    double dens_val = 0.0;

                    // do not add anything to the density if on the edge of the domain
                    if (i == 0 || i == nx - 1 || j == 0 || j == ny - 1)
                    {
                        charge_density.Spin_Up.mat[i, j]   = 0.0;
                        charge_density.Spin_Down.mat[i, j] = 0.0;
                        continue;
                    }

                    for (int k = 0; k < max_wavefunction; k++)
                    {
                        // and integrate the density of states at this position for this eigenvector from the minimum energy to
                        // (by default) 50 * k_b * T above mu = 0
                        dens_val += DoubleComplex.Norm(eig_decomp.EigenVector(k)[i * ny + j]) * DoubleComplex.Norm(eig_decomp.EigenVector(k)[i * ny + j]) * Get_OneD_DoS(eig_decomp.EigenValue(k), no_kb_T);
                    }

                    // just share the densities (there is no spin polarisation)
                    charge_density.Spin_Up.mat[i, j]   = 0.5 * dens_val;
                    charge_density.Spin_Down.mat[i, j] = 0.5 * dens_val;
                }
            }

            // and multiply the density by -e to get the charge density (as these are electrons)
            charge_density = unit_charge * charge_density;
        }
Exemplo n.º 4
0
        /// <summary>
        /// Calculate the charge density for this potential
        /// NOTE!!! that the boundary potential is (essentially) set to infty by ringing the density with a set of zeros.
        ///         this prevents potential solvers from extrapolating any residual density at the edge of the eigenstate
        ///         solution out of the charge density calculation domain
        /// </summary>
        /// <param name="layers"></param>
        /// <param name="charge_density_deriv"></param>
        /// <param name="chem_pot"></param>
        public void Get_ChargeDensity_Deriv(ILayer[] layers, ref SpinResolved_Data charge_density_deriv, Band_Data chem_pot)
        {
            // interpolate the input charge density and chemical potential onto a reduced domain to simplify DFT solve
            SpinResolved_Data dft_dens_deriv = new SpinResolved_Data(nz);

            Get_ChargeDensity(layers, ref charge_density_deriv, chem_pot);
            Band_Data dft_pot = new Band_Data(new DoubleVector(nz));

            Interpolate_DFT_Grid(ref dft_dens_deriv, ref dft_pot, charge_density_deriv, chem_pot);
            Get_Potential(ref dft_pot, layers);

            //     // set dft_dens to zero so that the hamiltonian doesn't include the XC term
            //     dft_dens = 0.0 * dft_dens;

            DoubleHermitianMatrix    hamiltonian = Create_Hamiltonian(layers, dft_pot);
            DoubleHermitianEigDecomp eig_decomp  = new DoubleHermitianEigDecomp(hamiltonian);

            double min_eigval = eig_decomp.EigenValues.Min();

            max_wavefunction = (from val in eig_decomp.EigenValues
                                where val < no_kb_T * Physics_Base.kB * temperature
                                select val).ToArray().Length;

            DoubleVector dens_up_deriv   = new DoubleVector(nz, 0.0);
            DoubleVector dens_down_deriv = new DoubleVector(nz, 0.0);

            for (int j = 0; j < nz; j++)
            {
                double dens_val = 0.0;
                for (int i = 0; i < max_wavefunction; i++)
                {
                    // and integrate the density of states at this position for this eigenvector from the minimum energy to
                    // (by default) 50 * k_b * T above mu = 0
                    //dens_val += dens_of_states.Integrate(min_eigval, no_kb_T * Physics_Base.kB * temperature);
                    dens_val += DoubleComplex.Norm(eig_decomp.EigenVector(i)[j]) * DoubleComplex.Norm(eig_decomp.EigenVector(i)[j]) * Get_TwoD_DoS_Deriv(eig_decomp.EigenValue(i), no_kb_T);// *mass / (2.0 * Math.PI * Physics_Base.hbar * Physics_Base.hbar);
                }

                // just share the densities (there is no spin polarisation)
                dens_up_deriv[j]   = 0.5 * dens_val;
                dens_down_deriv[j] = 0.5 * dens_val;
            }

            // and multiply the density derivative by e to get the charge density and by e to convert it to d/dphi (as increasing phi decreases the charge: dn/dphi*-e^2 )
            dft_dens_deriv = -1.0 * unit_charge * unit_charge * new SpinResolved_Data(new Band_Data(dens_up_deriv), new Band_Data(dens_down_deriv));

            Insert_DFT_Charge(ref charge_density_deriv, dft_dens_deriv);
        }
        /// <summary>
        /// returns an eigenvector decomposition for the xy-plane after having calculated the density in the
        /// z-direction and storing it in "charge_density"
        /// </summary>
        /// <param name="dft_pot"></param>
        /// <param name="charge_density"></param>
        /// <returns></returns>
        private double[,] Solve_Eigenvector_Problem(Band_Data dft_pot, ref SpinResolved_Data charge_density)
        {
            DoubleHermitianEigDecomp eig_decomp;

            double[,] xy_energy = new double[nx, ny];
            Band_Data dens_up   = new Band_Data(nx, ny, nz, 0.0);
            Band_Data dens_down = new Band_Data(nx, ny, nz, 0.0);

            // cycle over xy-plane calculating the ground state energy and eigenstate in the growth direction
            for (int i = 0; i < nx; i++)
            {
                for (int j = 0; j < ny; j++)
                {
                    // pull out the chemical potential for this slice
                    double[] z_dft_pot = new double[nz];
                    for (int k = 0; k < nz; k++)
                    {
                        z_dft_pot[k] = dft_pot.vol[k][i, j];
                    }

                    // calculate its eigendecomposition
                    DoubleHermitianMatrix h_z = Create_Hamiltonian(z_dft_pot, tz, nz);

                    eig_decomp = new DoubleHermitianEigDecomp(h_z);

                    // insert the eigenstate into density and record the local confinement energy
                    xy_energy[i, j] = eig_decomp.EigenValue(0);
                    for (int k = 0; k < nz; k++)
                    {
                        double dens_tot = DoubleComplex.Norm(eig_decomp.EigenVector(0)[k]) * DoubleComplex.Norm(eig_decomp.EigenVector(0)[k]);
                        dens_up.vol[k][i, j]   = 0.5 * dens_tot;
                        dens_down.vol[k][i, j] = 0.5 * dens_tot;
                    }
                }
            }

            // calculate the eigenstates in the xy-plane

            /*DoubleHermitianMatrix h_xy = Create_2DEG_Hamiltonian(xy_energy, tx, ty, nx, ny, true, false);
             * eig_decomp = new DoubleHermitianEigDecomp(h_xy);
             * energies = eig_decomp.EigenValues;*/

            // put the calculated densities into charge_density
            charge_density = new SpinResolved_Data(dens_up, dens_down);

            return(xy_energy);
        }
Exemplo n.º 6
0
        /// <summary>
        /// Calculate the charge density for this potential
        /// NOTE!!! that the boundary potential is (essentially) set to infty by ringing the density with a set of zeros.
        ///         this prevents potential solvers from extrapolating any residual density at the edge of the eigenstate
        ///         solution out of the charge density calculation domain
        /// </summary>
        /// <param name="layers"></param>
        /// <param name="charge_density_deriv"></param>
        /// <param name="chem_pot"></param>
        public override void Get_ChargeDensity_Deriv(ILayer[] layers, ref SpinResolved_Data charge_density_deriv, Band_Data chem_pot)
        {
            // convert the chemical potential into a quantum mechanical potential
            Band_Data dft_pot = chem_pot.DeepenThisCopy();

            Get_Potential(ref dft_pot, layers);

            DoubleHermitianMatrix    hamiltonian = Create_Hamiltonian(layers, dft_pot);
            DoubleHermitianEigDecomp eig_decomp  = new DoubleHermitianEigDecomp(hamiltonian);

            double min_eigval       = eig_decomp.EigenValues.Min();
            int    max_wavefunction = (from val in eig_decomp.EigenValues
                                       where val < no_kb_T * Physics_Base.kB * temperature
                                       select val).ToArray().Length;

            for (int i = 0; i < nx; i++)
            {
                for (int j = 0; j < ny; j++)
                {
                    double dens_val_deriv = 0.0;

                    // do not add anything to the density if on the edge of the domain
                    if (i == 0 || i == nx - 1 || j == 0 || j == ny - 1)
                    {
                        charge_density_deriv.Spin_Up.mat[i, j]   = 0.0;
                        charge_density_deriv.Spin_Down.mat[i, j] = 0.0;
                        continue;
                    }

                    for (int k = 0; k < max_wavefunction; k++)
                    {
                        // and integrate the density of states at this position for this eigenvector from the minimum energy to
                        // (by default) 50 * k_b * T above mu = 0
                        dens_val_deriv += DoubleComplex.Norm(eig_decomp.EigenVector(k)[i * ny + j]) * DoubleComplex.Norm(eig_decomp.EigenVector(k)[i * ny + j]) * Get_OneD_DoS_Deriv(eig_decomp.EigenValue(k), no_kb_T);
                    }

                    // just share the densities (there is no spin polarisation)
                    charge_density_deriv.Spin_Up.mat[i, j]   = 0.5 * dens_val_deriv;
                    charge_density_deriv.Spin_Down.mat[i, j] = 0.5 * dens_val_deriv;
                }
            }

            // and multiply the density derivative by e to get the charge density and by e to convert it to d/dphi (as increasing phi decreases the charge: dn/dphi*-e^2 )
            charge_density_deriv = unit_charge * unit_charge * charge_density_deriv;
        }
Exemplo n.º 7
0
        public override void Get_ChargeDensity(ILayer[] layers, ref SpinResolved_Data charge_density, Band_Data chem_pot)
        {
            // convert the chemical potential into a quantum mechanical potential
            Band_Data dft_pot = chem_pot.DeepenThisCopy();

            Get_Potential(ref dft_pot, layers);

            DoubleHermitianEigDecomp eig_decomp = Solve_Eigenvector_Problem(dft_pot, ref charge_density);

            int max_wavefunction = (from val in eig_decomp.EigenValues
                                    where val < no_kb_T * Physics_Base.kB * temperature
                                    select val).ToArray().Length;

            double[] dens_x = new double[nx];
            // and generate a density for the y-direction
            for (int i = 0; i < nx; i++)
            {
                for (int k = 0; k < max_wavefunction; k++)
                {
                    dens_x[i] += DoubleComplex.Norm(eig_decomp.EigenVector(k)[i]) * DoubleComplex.Norm(eig_decomp.EigenVector(k)[i]) * Get_OneD_DoS(energies[k], no_kb_T);
                }
            }

            // multiply the z-densities by the y-density
            for (int i = 0; i < nx; i++)
            {
                for (int j = 0; j < ny; j++)
                {
                    // do not add anything to the density if on the edge of the domain
                    if (i == 0 || i == nx - 1 || j == 0 || j == ny - 1)
                    {
                        charge_density.Spin_Up.mat[i, j]   *= 0.0;
                        charge_density.Spin_Down.mat[i, j] *= 0.0;
                    }

                    charge_density.Spin_Up.mat[i, j]   *= dens_x[i];
                    charge_density.Spin_Down.mat[i, j] *= dens_x[i];
                }
            }

            // and multiply the density by -e to get the charge density (as these are electrons)
            charge_density = unit_charge * charge_density;
        }
Exemplo n.º 8
0
        void Write_Out_Wavefunction_Densities(DoubleHermitianEigDecomp eig_decomp)
        {
            double min_eigval       = eig_decomp.EigenValues.Min();
            int    max_wavefunction = (from val in eig_decomp.EigenValues
                                       where val < no_kb_T * Physics_Base.kB * temperature
                                       select val).ToArray().Length;

            for (int k = 0; k < max_wavefunction; k++)
            {
                System.IO.StreamWriter sw  = new System.IO.StreamWriter("dens_wavefunction_" + k.ToString("00"));
                DoubleMatrix           tmp = new DoubleMatrix(nx, ny, 0.0);

                for (int i = 0; i < nx; i++)
                {
                    for (int j = 0; j < ny; j++)
                    {
                        // do not add anything to the density if on the edge of the domain
                        if (i == 0 || i == nx - 1 || j == 0 || j == ny - 1)
                        {
                            continue;
                        }

                        // and integrate the density of states at this position for this eigenvector from the minimum energy to
                        // (by default) 50 * k_b * T above mu = 0
                        tmp[i, j] = unit_charge * DoubleComplex.Norm(eig_decomp.EigenVector(k)[i * ny + j]) * DoubleComplex.Norm(eig_decomp.EigenVector(k)[i * ny + j]) * Get_OneD_DoS(eig_decomp.EigenValue(k), no_kb_T);
                    }
                }

                for (int i = 0; i < nx; i++)
                {
                    for (int j = 0; j < ny; j++)
                    {
                        sw.Write(tmp[i, j].ToString() + '\t');
                        if (j == ny - 1)
                        {
                            sw.WriteLine();
                        }
                    }
                }

                sw.Close();
            }
        }
Exemplo n.º 9
0
        public override void Get_ChargeDensity(ILayer[] layers, ref SpinResolved_Data charge_density, Band_Data chem_pot)
        {
            // interpolate the input charge density and chemical potential onto a reduced domain to simplify DFT solve
            SpinResolved_Data dft_dens = new SpinResolved_Data(nz);
            Band_Data         dft_pot  = new Band_Data(new DoubleVector(nz));

            Interpolate_DFT_Grid(ref dft_dens, ref dft_pot, charge_density, chem_pot);
            Get_Potential(ref dft_pot, layers);

            DoubleHermitianMatrix    hamiltonian = Create_Hamiltonian(layers, dft_pot);
            DoubleHermitianEigDecomp eig_decomp  = new DoubleHermitianEigDecomp(hamiltonian);

            double min_eigval = eig_decomp.EigenValues.Min();

            max_wavefunction = (from val in eig_decomp.EigenValues
                                where val < no_kb_T * Physics_Base.kB * temperature
                                select val).ToArray().Length;

            DoubleVector dens_up   = new DoubleVector(nz, 0.0);
            DoubleVector dens_down = new DoubleVector(nz, 0.0);

            for (int j = 0; j < nz; j++)
            {
                double dens_val = 0.0;
                for (int i = 0; i < max_wavefunction; i++)
                {
                    // and integrate the density of states at this position for this eigenvector from the minimum energy to
                    // (by default) 50 * k_b * T above mu = 0
                    //dens_val += dens_of_states.Integrate(min_eigval, no_kb_T * Physics_Base.kB * temperature);
                    dens_val += DoubleComplex.Norm(eig_decomp.EigenVector(i)[j]) * DoubleComplex.Norm(eig_decomp.EigenVector(i)[j]) * Get_TwoD_DoS(eig_decomp.EigenValue(i), no_kb_T);
                }

                // just share the densities (there is no spin polarisation)
                dens_up[j]   = 0.5 * dens_val;
                dens_down[j] = 0.5 * dens_val;
            }

            // and multiply the density by -e to get the charge density (as these are electrons)
            dft_dens = unit_charge * new SpinResolved_Data(new Band_Data(dens_up), new Band_Data(dens_down));

            Insert_DFT_Charge(ref charge_density, dft_dens);
        }
Exemplo n.º 10
0
        public Band_Data Get_KS_KE(ILayer[] layers, Band_Data chem_pot)
        {
            // convert the chemical potential into a quantum mechanical potential
            Band_Data dft_pot = chem_pot.DeepenThisCopy();

            Get_Potential(ref dft_pot, layers);

            // create temporary density for input into...
            DoubleMatrix      dens_up   = new DoubleMatrix(nx, ny, 0.0);
            DoubleMatrix      dens_down = new DoubleMatrix(nx, ny, 0.0);
            SpinResolved_Data dens      = new SpinResolved_Data(new Band_Data(dens_up), new Band_Data(dens_down));

            // calculate eigenvectors
            DoubleHermitianEigDecomp eig_decomp = Solve_Eigenvector_Problem(dft_pot, ref dens);

            int max_wavefunction = (from val in eig_decomp.EigenValues
                                    where val < no_kb_T * Physics_Base.kB * temperature
                                    select val).ToArray().Length;

            // generate kinetic energy data
            Band_Data ke           = new Band_Data(nx, ny, 0.0);
            Band_Data dens_tot     = dens.Spin_Summed_Data;
            double    ke_prefactor = -0.5 * Physics_Base.hbar * Physics_Base.hbar / mass;

            for (int i = 1; i < nx - 1; i++)
            {
                for (int j = 1; j < ny - 1; j++)
                {
                    for (int k = 0; k < max_wavefunction; k++)
                    {
                        double dy2psi = (dens_tot.mat[i, j + 1] + dens_tot.mat[i, j - 1] - 2.0 * dens_tot.mat[i, j]) * DoubleComplex.Norm(eig_decomp.EigenVector(k)[i]);
                        double dx2psi = DoubleComplex.Norm(eig_decomp.EigenVector(k)[i + 1] + eig_decomp.EigenVector(k)[i - 1] - 2.0 * eig_decomp.EigenVector(k)[i]) * dens_tot.mat[i, j];

                        double psi_div2psi = (dens_tot.mat[i, j] * DoubleComplex.Norm(eig_decomp.EigenVector(k)[i])) * (dx2psi + dy2psi);

                        ke.mat[i, j] += ke_prefactor * psi_div2psi * Get_OneD_DoS(eig_decomp.EigenValue(k), no_kb_T);
                    }
                }
            }

            return(ke);
        }
Exemplo n.º 11
0
        SpinResolved_Data Calculate_Density_Derivative(DoubleHermitianEigDecomp eig_decomp_old, DoubleHermitianEigDecomp eig_decomp_new, int wavefunction)
        {
            DoubleMatrix dens_up_deriv   = new DoubleMatrix(nx, ny, 0.0);
            DoubleMatrix dens_down_deriv = new DoubleMatrix(nx, ny, 0.0);

            for (int i = 0; i < nx; i++)
            {
                for (int j = 0; j < ny; j++)
                {
                    // do not add anything to the density if on the edge of the domain
                    if (i == 0 || i == nx - 1 || j == 0 || j == ny - 1)
                    {
                        continue;
                    }

                    // calculate the density of this spin configuration this hamiltonian
                    dens_up_deriv[i, j]   += g_1D * DoubleComplex.Norm(eig_decomp_old.EigenVector(wavefunction)[i * ny + j]) * DoubleComplex.Norm(eig_decomp_old.EigenVector(wavefunction)[i * ny + j]) * Interpolate_Fermi_Function_Derivative(eig_decomp_old, eig_decomp_new, wavefunction);
                    dens_down_deriv[i, j] += g_1D * DoubleComplex.Norm(eig_decomp_old.EigenVector(wavefunction)[i * ny + j + nx * ny]) * DoubleComplex.Norm(eig_decomp_old.EigenVector(wavefunction)[i * ny + j + nx * ny]) * Interpolate_Fermi_Function_Derivative(eig_decomp_old, eig_decomp_new, wavefunction);
                }
            }

            // and multiply the density by -e to get the charge density (as these are electrons)
            return(unit_charge * unit_charge * new SpinResolved_Data(new Band_Data(dens_up_deriv), new Band_Data(dens_down_deriv)));
        }
Exemplo n.º 12
0
        private DoubleComplexMatrix Calculate_Boundary_Conditions(Boundary boundary, double energy)
        {
            DoubleComplexMatrix bc_matrix = new DoubleComplexMatrix(2 * ny, 2 * ny);

            // generate Hamiltonians for the boundaries
            DoubleComplexMatrix tmp_slice;

            if (boundary == Boundary.left)
            {
                tmp_slice = Generate_Slice_Hamiltonian(-0.5 * (nx - 1) * dx);
            }
            else if (boundary == Boundary.right)
            {
                tmp_slice = Generate_Slice_Hamiltonian(0.5 * (nx - 1) * dx);
            }
            else
            {
                throw new NotImplementedException();
            }
            DoubleComplexMatrix tmp_hopping_trans = Generate_Hopping_Hamiltonian().Transpose();

            // create temporary matrix for top-left of BC matrix
            DoubleComplexMatrix tmp_matrix = Product(tmp_hopping_trans, (new DoubleComplexMatrix(energy * DoubleMatrix.Identity(ny)) - tmp_slice));

            // fill with transfer matrix
            for (int i = 0; i < ny; i++)
            {
                for (int j = 0; j < ny; j++)
                {
                    bc_matrix[i, j]      = tmp_matrix[i, j];
                    bc_matrix[i + ny, j] = tmp_hopping_trans[i, j];
                    bc_matrix[i, j + ny] = -1.0 * tmp_hopping_trans[i, j];
                }
            }

            // solve eigen-problem for transfer matrix
            DoubleComplexEigDecomp       eig_decomp = new DoubleComplexEigDecomp(bc_matrix);
            DoubleComplexEigDecompServer server     = new DoubleComplexEigDecompServer();

            eig_decomp = server.Factor(bc_matrix);

            // fill the eigenvalue matrix, excluding unphysical solutions which blow-up in the wire
            DoubleComplexMatrix eig_vals = new DoubleComplexMatrix(ny, ny);
            DoubleComplexMatrix eig_vecs = new DoubleComplexMatrix(ny, ny);
            int count = 0;

            for (int i = 0; i < 2 * ny; i++)
            {
                if (Add_BC_EigenSolution(i, eig_decomp, boundary))
                {
                    // invert eigenvalues if calculating for the right lead
                    if (boundary == Boundary.left)
                    {
                        eig_vals[count, count] = eig_decomp.EigenValue(i);
                    }
                    else if (boundary == Boundary.right)
                    {
                        eig_vals[count, count] = 1.0 / eig_decomp.EigenValue(i);
                    }
                    else
                    {
                        throw new NotImplementedException();
                    }

                    // insert corresponding (normalised) eigenvector into the matrix
                    double norm = 0.0;
                    for (int j = 0; j < ny; j++)
                    {
                        norm += DoubleComplex.Norm(eig_decomp.RightEigenVector(i)[j]) * DoubleComplex.Norm(eig_decomp.RightEigenVector(i)[j]);
                    }
                    double inv_norm = 1.0 / Math.Sqrt(norm);
                    for (int j = 0; j < ny; j++)
                    {
                        eig_vecs[j, count] = inv_norm * eig_decomp.RightEigenVector(i)[j];
                    }

                    count++;
                }
            }

            // calculate coefficient matrix A and output Green's function
            DoubleComplexMatrix coefficient_matrix = new DoubleComplexMatrix(ny, ny);

            coefficient_matrix = Product(Product(tmp_hopping_trans.Transpose(), eig_vecs), eig_vals);
            DoubleComplexLUFact lu_fact = new DoubleComplexLUFact(coefficient_matrix);

            return(new DoubleComplexMatrix(Product(eig_vecs, lu_fact.Inverse())));

            /*          // allocate for evanescent modes
             *        if (boundary == Boundary.left && DoubleComplex.Norm(eig_decomp.EigenValue(i)) < 1.0 - propagating_mode_error)
             *            eig_vals[i, i] = eig_decomp.EigenValue(i);
             *        else if (boundary == Boundary.right && DoubleComplex.Norm(eig_decomp.EigenValue(i)) > 1.0 + propagating_mode_error)
             *            eig_vals[i, i] = 1.0 / eig_decomp.EigenValue(i);
             *        // and for propagating modes
             *        else if (Math.Abs(DoubleComplex.Norm(eig_decomp.EigenValue(i)) - 1.0) < propagating_mode_error)
             *            eig_vals[i, i]
             *        else
             *            throw new InvalidArgumentException("Error - Cannot have eigenvalue of transfer matrix with value " + eig_decomp.EigenValue(i).ToString() + "!");
             *
             *    // fill the eigenvector matrix with only the top half of the eigenvector
             *    DoubleComplexMatrix eig_vecs = new DoubleComplexMatrix(ny, ny);
             *    DoubleComplexMatrix inv_eigvec = new DoubleComplexMatrix(ny, ny);
             *    for (int i = 0; i < ny; i++)
             *    {
             *        DoubleComplexVector tmpvec_right = eig_decomp.RightEigenVector(i);
             *        DoubleComplexVector tmpvec_left = eig_decomp.LeftEigenVector(i);
             *
             *        // normalise the top of half of the eigenvector
             *        double norm2_right = 0.0;
             *        double norm2_left = 0.0;
             *        for (int j = 0; j < ny; j++)
             *        {
             *            norm2_right += NMathFunctions.Abs(tmpvec_right[i]) * NMathFunctions.Abs(tmpvec_right[i]);
             *            norm2_left += NMathFunctions.Abs(tmpvec_left[i]) * NMathFunctions.Abs(tmpvec_left[i]);
             *        }
             *        double norm_left = Math.Sqrt(norm2_left);
             *        double norm_right = Math.Sqrt(norm2_right);
             *
             *        // and insert it into the matrix
             *        for (int j = 0; j < ny; j++)
             *        {
             *            eig_vecs[j, i] = tmpvec_right[j] / norm_right;
             *            inv_eigvec[i, j] = tmpvec_left[j] / norm_left;
             *        }
             *    }
             *
             *    // get the inverse of the eigenvector matrix
             * //           DoubleComplexMatrix tmp_eigvec = new DoubleComplexMatrix(eig_vecs);
             * //           DoubleComplexMatrix inv_eigvec = NMathFunctions.PseudoInverse(tmp_eigvec);
             *    //DoubleComplexLUFact eigvec_fact = new DoubleComplexLUFact(tmp_eigvec);
             *    //DoubleComplexMatrix inv_eigvec = eigvec_fact.Inverse();
             *
             *    // Calculate the on-site Greens function of the end of the wire and return it
             *    if (boundary == Boundary.left)
             *        return Product(Product(Product(eig_vecs, eig_vals), inv_eigvec), tmp_hopping_trans);
             *    else if (boundary == Boundary.right)
             *        return Product(Product(Product(eig_vecs, eig_vals), inv_eigvec), tmp_hopping);
             *    else
             *        throw new NotImplementedException();
             */
        }