Как сделать численное интегрирование с волновой функцией квантового гармонического осциллятора? - PullRequest
6 голосов
/ 01 марта 2009

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

phi n (x) = N n H n (x) exp (-x 2 / 2)
, где H n (x) равно полином Эрмита

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

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

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

Ответы [ 6 ]

8 голосов
/ 01 марта 2009

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

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

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

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

Самый сложный вопрос когда-либо о SO? Вряд ли:)

4 голосов
/ 23 августа 2009

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

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

Приближение WKB ?

0 голосов
/ 11 сентября 2017

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

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

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

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

0 голосов
/ 08 августа 2015

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

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. Но это вызывается каждый раз, когда вам нужно вычислить интеграцию, и это может занять некоторое время, но я не уверен, как долго он будет использоваться в вашей программе.

Я предлагаю NO.2 выбор, который я предложил.

0 голосов
/ 15 августа 2009

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

// 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);
        }

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

...