Как выполнить численное интегрирование с волновой функцией квантового гармонического осциллятора?

Как выполнить численное интегрирование (какой численный метод и какие приемы использовать) для одномерного интегрирования в бесконечном диапазоне, когда одна или несколько функций в подынтегральном выражении являются волновыми функциями 1d квантового гармонического осциллятора . Среди прочего, я хочу вычислить матричные элементы некоторой функции в базисе гармонического осциллятора:

фи п (х) = N п Н п (х) ехр (-x 2 /2) ,
где Н п (х) полином Эрмита

V m, n = \ int _ {- бесконечность} ^ {бесконечность} phi m (x) V (x) phi n (x) dx

Также в случае, когда есть квантовые гармонические волновые функции с разной шириной.

Проблема в том, что волновые функции phi n (x) имеют колебательное поведение, что является проблемой для больших n , а алгоритм, подобный адаптивной квадратуре Гаусса-Кронрода из GSL (Научная библиотека GNU), требует много времени для вычисления и имеет большие ошибки.

Ответов (6)

Решение

Неполный ответ, так как на данный момент у меня мало времени; если другие не могут завершить картину, я могу предоставить более подробную информацию позже.

  1. По возможности применяйте ортогональность волновых функций. Это должно значительно сократить объем вычислений.

  2. Делайте все, что можете, аналитически. Поднимите константы, разделите интегралы по частям, что угодно. Изолируйте интересующий регион; большинство волновых функций ограничены полосой пропускания, и уменьшение области интереса значительно сэкономит работу.

  3. Что касается самой квадратуры, вы, вероятно, захотите разделить волновые функции на три части и интегрировать каждую по отдельности: колебательный бит в центре плюс экспоненциально затухающие хвосты с обеих сторон. Если волновая функция нечетная, вам повезет, и хвосты будут компенсировать друг друга, а это означает, что вам нужно беспокоиться только о центре. Для четных волновых функций вам нужно только интегрировать одну и удвоить ее (ура для симметрии!). В противном случае проинтегрируйте хвосты, используя квадратурное правило Гаусса-Лагерра высокого порядка. Возможно, вам придется вычислить правила самостоятельно; Я не знаю, содержат ли таблицы хорошие правила Гаусса-Лагерра, поскольку они используются не слишком часто. Вы, вероятно, также захотите проверить поведение ошибки при увеличении количества узлов в правиле; прошло много времени с тех пор, как я использовал правила Гаусса-Лагерра, и я не Не помню, демонстрируют ли они феномен Рунге. Интегрируйте центральную часть любым способом, который вам нравится; Конечно, Гаусс-Кронрод - хороший выбор, но есть также квадратура Фейера (которая иногда лучше масштабируется для большого числа узлов, что может лучше работать с осциллирующим подынтегральным выражением) и даже правило трапеции (которое демонстрирует потрясающую точность при определенных колебательных функциях). ). Выберите один и попробуйте; если результаты плохие, попробуйте другой метод. Выберите один и попробуйте; если результаты плохие, попробуйте другой метод. Выберите один и попробуйте; если результаты плохие, попробуйте другой метод.

Самый сложный вопрос о SO? Едва ли :)

Я не собираюсь сейчас объяснять или уточнять что-либо из этого. Этот код написан как есть и, вероятно, неверен. Я даже не уверен, что это именно тот код, который я искал, я просто помню, что много лет назад у меня возникла эта проблема, и при поиске в моих архивах я нашел это. Вам нужно будет самостоятельно построить график вывода, здесь есть некоторые инструкции. Я скажу, что интеграция в бесконечном диапазоне - это проблема, к которой я обращался, и при выполнении кода в ней указывается ошибка округления на «бесконечности» (что численно просто означает большой).

// compile g++ base.cc -lm
#include <iostream>
#include <cstdlib>
#include <fstream>
#include <math.h>

using namespace std;

int main ()
        {
        double xmax,dfx,dx,x,hbar,k,dE,E,E_0,m,psi_0,psi_1,psi_2;
        double w,num;
        int n,temp,parity,order;
        double last;
        double propogator(double E,int parity);
        double eigen(double E,int parity);
         double f(double x, double psi, double dpsi);
        double g(double x, double psi, double dpsi);
        double rk4(double x, double psi, double dpsi, double E);

        ofstream datas ("test.dat");

        E_0= 1.602189*pow(10.0,-19.0);// ev joules conversion
        dE=E_0*.001;
//w^2=k/m                 v=1/2 k x^2             V=??? = E_0/xmax   x^2      k-->
//w=sqrt( (2*E_0)/(m*xmax) );
//E=(0+.5)*hbar*w;

        cout << "Enter what energy level your looking for, as an (0,1,2...) INTEGER: ";
        cin >> order;

        E=0;
        for (n=0; n<=order; n++)
                {
                parity=0;
//if its even parity is 1 (true)
                temp=n;
                if ( (n%2)==0 ) {parity=1; }
                cout << "Energy " << n << " has these parameters: ";
                E=eigen(E,parity);
                if (n==order)
                        {
                        propogator(E,parity);
                        cout <<" The postive values of the wave function were written to sho.dat \n";
                        cout <<" In order to plot the data should be reflected about the y-axis \n";
                        cout <<"  evenly for even energy levels and oddly for odd energy levels\n";
                        }
                E=E+dE;
                }
        }

double propogator(double E,int parity)
        {
        ofstream datas ("sho.dat") ;

        double hbar =1.054*pow(10.0,-34.0);
        double m =9.109534*pow(10.0,-31.0);
        double E_0= 1.602189*pow(10.0,-19.0);
        double dx =pow(10.0,-10);
        double xmax= 100*pow(10.0,-10.0)+dx;
        double dE=E_0*.001;
        double last=1;
        double x=dx;
        double psi_2=0.0;
        double psi_0=0.0;
        double psi_1=1.0;
//      cout <<parity << " parity passsed \n";
        psi_0=0.0;
        psi_1=1.0;
        if (parity==1)
                {
                psi_0=1.0;
                psi_1=m*(1.0/(hbar*hbar))* dx*dx*(0-E)+1 ;
                }

        do
                {
                datas << x << "\t" << psi_0 << "\n";
                psi_2=(2.0*m*(dx/hbar)*(dx/hbar)*(E_0*(x/xmax)*(x/xmax)-E)+2.0)*psi_1-psi_0;
//cout << psi_1 << "=psi_1\n";
                psi_0=psi_1;
                psi_1=psi_2;
                x=x+dx;
                } while ( x<= xmax);
//I return 666 as a dummy value sometimes to check the function has run
        return 666;
        }


   double eigen(double E,int parity)
        {
        double hbar =1.054*pow(10.0,-34.0);
        double m =9.109534*pow(10.0,-31.0);
        double E_0= 1.602189*pow(10.0,-19.0);
        double dx =pow(10.0,-10);
        double xmax= 100*pow(10.0,-10.0)+dx;
        double dE=E_0*.001;
        double last=1;
        double x=dx;
        double psi_2=0.0;
        double psi_0=0.0;
        double psi_1=1.0;
        do
                {
                psi_0=0.0;
                psi_1=1.0;

                if (parity==1)
                        {double psi_0=1.0; double psi_1=m*(1.0/(hbar*hbar))* dx*dx*(0-E)+1 ;}
                x=dx;
                do
                        {
                        psi_2=(2.0*m*(dx/hbar)*(dx/hbar)*(E_0*(x/xmax)*(x/xmax)-E)+2.0)*psi_1-psi_0;
                        psi_0=psi_1;
                        psi_1=psi_2;
                        x=x+dx;
                        } while ( x<= xmax);


                if ( sqrt(psi_2*psi_2)<=1.0*pow(10.0,-3.0))
                        {
                        cout << E << " is an eigen energy and " << psi_2 << " is psi of 'infinity'  \n";
                        return E;
                        }
                else
                        {
                        if ( (last >0.0 && psi_2<0.0) ||( psi_2>0.0 && last<0.0) )
                                {
                                E=E-dE;
                                dE=dE/10.0;
                                }
                        }
                last=psi_2;
                E=E+dE;
                } while (E<=E_0);
        }

Если этот код кажется правильным, неправильным, интересным или у вас есть конкретные вопросы, задайте их, и я отвечу на них.

Я учусь по специальности физика, и я тоже столкнулся с проблемой. Сейчас я все время думаю над этим вопросом и получаю свой ответ. Думаю, это поможет вам решить этот вопрос.

1. В gsl есть функции, которые помогут вам интегрировать колебательную функцию - qawo и qawf. Может быть, вы можете установить значение a . И интеграцию можно разделить на две части, [0, a ] и [ a , pos_infinity]. В первом интервале вы можете использовать любую функцию интеграции gsl, которую хотите, а во втором интервале вы можете использовать qawo или qawf.

2. Или вы можете интегрировать функцию до верхнего предела b , который интегрирован в [0, b ]. Таким образом, интеграцию можно рассчитать с помощью метода легенд Гаусса, который предоставляется в gsl. Хотя может быть какая-то разница между реальным значением и вычисленным значением, но если вы правильно установите b , разницей можно пренебречь. Пока разница меньше требуемой точности. И этот метод, использующий функцию gsl, вызывается только один раз и может использоваться много раз, потому что возвращаемое значение - это точка и соответствующий ей вес, а интеграция - это только сумма f (xi) * wi, для более подробной информации вы можете поискать gauss legendre квадратура в Википедии. Операция умножения и сложения выполняется намного быстрее, чем интеграция.

3. Также есть функция, которая может вычислить интеграцию бесконечной области - qagi, вы можете поискать ее в gsl-руководстве пользователя. Но это вызывается каждый раз, когда вам нужно рассчитать интеграцию, и это может занять некоторое время, но я не уверен, как долго он будет использоваться в вашей программе.

Предлагаю вариант №2, который я предложил.

Если вы собираетесь работать с функциями гармонического осциллятора меньше n = 100, вы можете попробовать:

http://www.mymathlib.com/quadrature/gauss_hermite.html

Программа вычисляет интеграл с помощью квадратуры Гаусса-Эрмита со 100 нулями и весами (нули H_100). Как только вы перейдете к Hermite_100, интегралы будут не такими точными.

Используя этот метод интеграции, я написал программу, которая рассчитывает именно то, что вы хотите вычислить, и она работает довольно хорошо. Кроме того, может быть способ выйти за пределы n = 100, используя асимптотику нулей полиномиального Эрмита, но я не рассматривал это.

ВКБ приближение?

Я бы порекомендовал еще несколько вещей:

  1. Попробуйте преобразовать функцию в конечную область, чтобы сделать интеграцию более управляемой.
  2. По возможности используйте симметрию - разбейте ее на сумму двух интегралов от отрицательной бесконечности до нуля и от нуля до бесконечности и посмотрите, является ли функция симметричной или антисимметричной. Это могло бы упростить ваши вычисления.
  3. Взгляните на квадратуру Гаусса-Лагерра и посмотрите, может ли она вам помочь.