Copyright 2022 Matthew Digman

Produced under NASA LISA Preparatory Science Grant 80NSSC19K0320

==========License=================
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with with program; see the file COPYING. If not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
MA  02111-1307  USA

=============Overview=============
This module implements the IMRPhenomD in a pure python code, compiled with the numba just in time compiler.
Currently the only non-standard dependencies are numba itself, and the pip-installable module 'interpolation',
which provides numba-compatible functions for cubic spline interpolation.

=======Structure and Usage========
The structure of the code is for the most part closely related to the C code, and in particular the module provides nearly identical function interfaces 
for the standard interfaces in IMRPhenomD.py. The demo_IMRPhenomD.py provides a simple demo of the speed and results

============Accuracy==============
The module implements the analytic first and second derivatives necessary to compute t(f) and t'(f), rather than computing them numerically 
as is done in the C code. Using the analytic derivatives increases the code complexity somewhat, but is both walltime faster and produces more numerically accurate results. 
The improvement in numerical accuracy is particularly significant for t'(f). 

============Speed==================
Partly because of the improvements in the derivatives, and partly because of other optimizations,
in testing PyIMRPhenomD is in most cases considerably faster than the C implementation.
For example, for the test_case in demo_IMRPhenomD.py,
PyIMRPhenomD version runs in approximately 0.22ms on one core of an an AMD Ryzen Threadripper 3970X, 
whereas the C implementation runs in 1.8ms. 
Therefore, for this test case the python version is almost 9 times faster. 
The speed advantage of the python version is typically larger the less points are evaluated
suggesting that the C version is being slowed by overheads not present in the python version.
However, the python version is typically still ~5 times faster even for much larger grids.

Here are some speed results for different NF with a fixed frequency range for the source in demo_IMRPhenomD.py 
on a single core of an AMD Ryzen Threadripper 3970X:

NF       PyIMRPhenomD    C IMRPhenomD  Python Speedup
10          0.000008s       0.000078s           10.4x
100         0.000012s       0.000096s            8.4x
1002        0.000028s       0.000248s            8.7x
10024       0.000217s       0.001845s            8.5x
100240      0.002396s       0.017934s            7.5x
1002400     0.032153s       0.187773s            6.0x
10024000    0.371990s       1.688623s            4.4x
100240000   3.955083s      18.259744s            4.6x

The Python version is always much faster than the C version.
Though both versions become overhead limited at very small NF, the C version overhead is an order of magnitude worse.
The exception to the speedup is in compile time. The numba just-in-time compiler takes approximately 15s
to compile the code, which much occur at run time. However, this is a one time startup overhead, 
and for typical MCMC search applications the overhead will typically quickly become insignificant


===========Feedback================
For comments, questions, or feedback, please feel free to either contact me through github 
or contact me direclty at matthew.digman [at] msu.montana.edu