Solving Second Order ODE in coordinate space - python

I need to numerically compute the eigenvalues and eigenfunctions of the radial Schrodinger Equation in the case of a 3D and 2D Coulomb potential. The differential equation contains both the first and second derivative of R(r).
The problem is that scipy.integrate.ode or scipy.integrate.odeint require "initial values" for the function and its first derivative - every example I've seen online uses a differential equation in t, so that specifying the initial conditions of the system is trivial and arbitrary.
Since I am considering an ODE in space, not time, I should note that "initial conditions" should be more accurately called "boundary conditions". For my case, however, the function R(r) must only be finite at the origin, but there is no specific value it must take at r=0. Furthermore the derivative of the radial wavefunction, R', is physically meaningless and so constraining (or especially specifying) its value at the origin is nonsensical. The only definite boundary conditions present in the system is that the function must exponentially decay to zero for very large r.
In this case I am considering, instead of making the linspace count up from zero to some large number, make it count backwards from a large value to zero, for which I can set my "initial condition at r -> infinity" such that R and R' are zero.
Anyone else have this issue, and what workaround did you find for it?
Thank you!

Related

How to integrate discrete function

I need to integrate a certain function that I have specified as discrete values for discrete arguments (I want to count the area under the graph I get).
I.e., from the earlier part of the code I have the literal:
args=[a1, a2, a3, a3]
valuses=[v1, v2, v3, v4]
where value v1 corresponds to a1, etc. If it's important, I have args set in advance with a specific discretization width, and I count values with a ready-made function.
I am attaching a figure.
And putting this function, which gave me a 'values' array, into integrate.quad() gives me an error:
IntegrationWarning: The maximum number of subdivisions (50) has been achieved. If increasing the limit yields no improvement it is advised to analyze
the integrand in order to determine the difficulties. If the position of a
local difficulty can be determined (singularity, discontinuity) one will
probably gain from splitting up the interval and calling the integrator on the subranges. Perhaps a special-purpose integrator should be used.
How can I integrate this? I'm mulling over the scipy documentation, but I can't seem to put it together. Because, after all, args themselves are already discretized by a finite number.
I am guessing that before passing the integral to quad you did some kind of interpolation on it. In general this is a misguided approach.
Integration and interpolation is very closely related. An integral requires you to compute the area under the curve and thus you must know the value of the function at any given point. Hence, starting from a set of data it is natural to want to interpolate it first. Yet the quad routine does not know that you started with a limited set of data, it just assumes that the function you gave it is "perfect" and it will do its best to compute the area under it! However the interpolated function is just a guess on what the values are between given points and thus integrating an interpolated function is a waste of time.
As MB-F said, in the discrete case you should simply sum up the points while multiplying them by the step size between them. You can do this the naïve way by pretending that the function is just rectangles. Or you can do what MB-F suggested which pretend that all the data points are connected with straight lines. Going one step further is pretending that the line connecting the data points is smooth (often true for physical systems) and use simpson integration implemented by scipy
Since you only have a discrete approximation of the function, integration reduces to summation.
As a simple approximation of an integral, try this:
midpoints = (values[:-1] + values[1:]) / 2
steps = np.diff(args)
area = np.sum(midpoints * steps)
(Assuming args and values are numpy arrays and the function is value = f(arg).)
That approach sums the areas of all trapezoids between adjacent data points (Wikipedia).

Solving nonlinear eigenvalue problem with python

I have an eigenvalue problem in the form
At the moment I'm using scipy.linalg.eig. The strategy I use is conceptually similar to the Campbel diagram used to identify the natural frequencies of a rotor whose stiffness and damping are function of the rotating velocity. This is:
vary the value of that I use to compute from 0 to a certain maximum value (or loop to infinity)
Compute the eigenvalues of for each value of
Now for each eigenvalue (eg. the lowest one) "intersect" the function with the bisector
Search for the intersection where the eigenvalue is equal to the value of used to compute the matrix. Store it and the associated eigenvector.
This however is slow, tedious and, most importantly, it is required to "follow" the eigenvalues when changes. When two eigenvalues "swap" order (the second becomes the third for example), you need to properly identify this to avoid errors.
Is there a better way of solving this?

Eigenvector ambiguity - How to enforce a specific sign convention

I am writing a program in Python that uses numpy.linalg.eigh to diagonalize a Hermitian matrix (a Hamiltonian). I diagonalize many such matrices and use the resultant eigenvector matrices for multiple unitary transformations of some other matrix. By "eigenvector matrix", I mean a matrix whose columns are the eigenvectors of the original matrix.
Unfortunately, I am hitting a potential problem because of the eigenvector sign ambiguity (i.e., eigenvectors are only defined up to a constant and normalization still does not fix the sign of an eigenvector). Specifically, the result I am calculating depends on the interference patterns produced by the successive unitary transformations. Thus, I anticipate that the sign ambiguity will become a problem.
My question:
What is the best way (or the industry standard) to enforce a particular sign convention for the eigenvectors?
I have thought of/come across the following:
Ensure the first coefficient of each eigenvector is positive. Problem: some of these coefficients are zero or within numerical error of zero.
Ensure the first coefficient of largest magnitude is positive. Problem: some of the eigenvectors have multiple coefficients with the same magnitude within numerical error. Numerical error then "randomly" determines which coefficient is "bigger."
Ensure the sum of the coefficients is positive. Problem: some coefficients are equal in magnitude but opposite in sign, leaving the sign still ambiguous/determined by numerical error. (I also see other problems with this approach).
Add a small number (such as 1E-16) to the eigenvector, ensure that the first coefficient is positive, then subtract the number. Problem: Maybe none important for me, but this makes me uneasy as I am not sure what problems it may cause.
(Inspired by Eigenshuffle and Sign correction in SVD and PCA) Pick a reference vector and ensure that the dot product of every eigenvector with this vector is positive. Problem: How to pick the vector? A random vector increases the likelihood that no eigenvectors are orthogonal to it (within numerical error), but there is no guarantee. Alternatively, one could choose a set of random vectors (all with positive coefficients) to increase the likelihood that the vector space is "spanned" well-enough.
I have tried to find what is the "standard" convention but I have a hard time finding anything particularly useful, particularly in Python. There is a solution for SVD (Sign correction in SVD and PCA), but I don't have any data vectors to compare to. There is Eigenshuffle (which is for Matlab and I am using Python), but my matrices are not usually successive small modifications of each other (though some are).
I am leaning toward solution 5 at it seems pretty intuitive; we are simply ensuring that all eigenvectors are in the same high-dimensional "quadrant". Also, having two or three random reference vectors with positive coefficients should cover almost all eigenvectors with very high probability, assuming the dimensionality of the system is not too big (my system has a dimensionality of 9).

How to decide between scipy.integrate.simps or numpy.trapz?

I have a set of points, of which when I plot I get the graph below. I would like to find the area under the graph, however I am not sure whether scipy.integrate.simps or numpy.trapz is more suitable.
Could someone advice me on the mathematical background between the two functions and thus the conclusion on which function is more accurate?
The trapezoidal rule is the simplest of numerical integration methods. In effect, it estimates the area under a curve by approximating the curve with straight line segments, which only requires two points for each segment. Simpson's rule uses quadratic curves to approximate the function segments instead, each of which requires three points, sampled from your function, to approximate a given segment.
So what is the error associated with using these numerical methods as approximations to an analytical integral?
The error associated with the trapezoidal rule, to leading order, is proportional to h^2[f'(a) - f'(b)]. h is the spacing between sampled points in your function; f'(a) and f'(b) are the first derivative of your function at the beginning and end of the sampling domain.
The error through Simpson's rule, on the other hand, is proportional to h^4[f'''(a) - f'''(b)]. f''' is the third-order derivative in your function.
h is typically small, so h^4 is typically much smaller than h^2!
TLDR: Simpson's rule typically gives far superior results for numerical integration, compared to the trapezoidal rule, with basically no additional computational cost.

What derivatives are used for splines in SciPy.interpolate.interp1d?

I'm trying to find out how the spline interpolation in scipy.interpolate.interp1d decides what the derivatives of the fitting/smoothing function should be. From the documentation, I understand that interp1d fits a spline if an int (or quadratic or cubic) is passed to the kind keyword.
But if I'm not providing any derivative information, how does it decide what the derivatives are? I tried to follow the function calls in the source code, but this only got me to the cryptic spleval function, which just seems to call FITPACK. And I wasn't really sure how to find information about FITPACK from their website...
Spline derivatives at the knot points are not explicitly prescribed, they are determined by continuity/smoothness conditions. I'll take the cubic case as an example. You give n x-values and n y-values. A cubic spline has 4*(n-1) coefficients, 4 on each of (n-1) intervals between the given x-values. These coefficients are determined from the following conditions:
The spline must be continuous at each interior knot: this is (n-2) equations, as there are (n-2) interior knots. We want both pieces to the left and right of a knot to have the same value at the knot.
The first derivative of the spline must be continuous at each interior knot: this is (n-2) equations.
The second derivative of the spline must be continuous at each interior knot: this is (n-2) equations.
The spline must match each of the given y-values: this is n equations.
The total so far is 4*n-6 equations for 4*n-4 unknowns. Two additional equations are needed; the most popular choice is to require the 3rd derivative to be continuous at the leftmost and rightmost interior knots (this is called the "not a knot" condition). Now we have a linear system of size 4*n-4, which can be solved for the coefficients.
The above should not be confused with Hermite interpolation, which is where one prescribes the values of derivatives as well as of the function itself. This is a less common task, and to my knowledge, SciPy does not have a built-in tool for it.

Categories