aboutsummaryrefslogtreecommitdiff
path: root/src/AHFinder_leg.F
blob: 654963b6e63c48b77c3b94bfb355d5e2ed85b856 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
/*@@
  @file      AHFinder_leg.F
  @date      April 1998
  @author    Miguel Alcubierre
  @desc 
             Associated Legendre functions.
  @enddesc 
@@*/

#include "cctk.h"

      CCTK_REAL function LEGEN(L,M,X)

!     This function computes the associated Legendre polynomial
!     P^M_L(X).  Here M and L are integers satisfying 0 <= M <= L,
!     and X lies in the range -1 <= X <= 1.
!
!     The general function is taken from Numerical Recipes.
!     However, I hardwired the first few polynomials for M=0,
!     and I added a section that tries to use as much information
!     as possible from previous calls to the routine to avoid
!     having to start from scratch every time.

      use AHFinder_dat

      implicit none

      integer L,M
      integer LOLD1,LOLD2,MOLD1,MOLD2
      integer I,LL

      CCTK_REAL X,Y
      CCTK_REAL PMM,PLL,PMMP1,SOMX2,FACT
      CCTK_REAL factor,aux
      CCTK_REAL zero,one,two
      CCTK_REAL XOLD1,XOLD2
      CCTK_REAL LEGOLD1,LEGOLD2

      save LOLD1,LOLD2,MOLD1,MOLD2,XOLD1,XOLD2
      save LEGOLD1,LEGOLD2


!     **************************
!     ***   DEFINE NUMBERS   ***
!     **************************

      zero = 0.0D0
      one  = 1.0D0
      two  = 2.0D0


!     *************************************************
!     ***   HARDWIRE FIRST FEW POLYNOMIALS FOR M=0  ***
!     *************************************************

!     Notice that it does not make sense to go much higher
!     than L=12 since the explicit expressions for the
!     polynomials have large terms with alternating signs,
!     so for high L we rapidly loose accuracy in the sum.
!     The method from Numerical Recipes that comes below
!     is slow, but does not have this problem.

      Y = X**2

!     Find polynomials for M=0.

      if ((M.eq.0).and.(L.le.12)) then

         if (L.eq.0) then
            LEGEN = one
            goto 10
         else if (L.eq.1) then
            LEGEN = X
            goto 10
         else if (L.eq.2) then
            LEGEN = 5.0D-1*(3.0D0*Y - one)
            goto 10
         else if (L.eq.3) then
            LEGEN = 5.0D-1*(5.0D0*Y - 3.0D0)*X
            goto 10
         else if (L.eq.4) then
            LEGEN = 1.25D-1*(35.0D0*Y**2 - 30.0D0*Y + 3.0D0)
            goto 10
         else if (L.eq.5) then
            LEGEN = 1.25D-1*(63.0D0*Y**2 - 70.0D0*Y + 15.0D0)*X
            goto 10
         else if (L.eq.6) then
            LEGEN = 6.25D-2*(231.0D0*Y**3 - 315.0D0*Y**2 + 105.0D0*Y
     .            - 5.0D0)
            goto 10
         else if (L.eq.7) then
            LEGEN = 6.25D-2*(429.0D0*Y**3 - 693.0D0*Y**2 + 315.0D0*Y
     .            - 35.0D0)*X
            goto 10
         else if (L.eq.8) then
            LEGEN = 7.8125D-3*(6435.0D0*Y**4 - 12012.0D0*Y**3
     .            + 6930.0D0*Y**2 - 1260.0D0*Y + 35.0D0)
            goto 10
         else if (L.eq.9) then
            LEGEN = 7.8125D-3*(12155.0D0*Y**4 - 25740.0D0*Y**3
     .            + 18018.0D0*Y**2 - 4620.0D0*Y + 315.0D0)*X
            goto 10
         else if (L.eq.10) then
            LEGEN = 3.90625D-3*(46189.0D0*Y**5 - 109395.0D0*Y**4
     .            + 90090.0D0*Y**3 - 30030.0D0*Y**2 + 3465.0D0*Y
     .            - 63.0D0)
            goto 10
         else if (L.eq.11) then
            LEGEN = 3.90625D-3*(88179.0D0*Y**5 - 230945.0D0*Y**4
     .            + 218790.0D0*Y**3  - 90090.0D0*Y**2 + 15015.0D0*Y
     .            - 693.0D0)*X
            goto 10
         else if (L.eq.12) then
            LEGEN = 9.76563D-4*(676039.0D0*Y**6 - 1939938.0D0*Y**5
     .            + 2078505.0D0*Y**4 - 1021020.0D0*Y**3
     .            + 225225.0D0*Y**2 - 18018.0D0*Y + 231.0D0)
            goto 10
         end if

      end if


!     ***********************************************
!     ***   USE RECURSIVE RELATIONS IF POSSIBLE   ***
!     ***********************************************

!     Here I try to optimize the calculation of the Legendre
!     polynomials (which is ordinarily very expensive) by using
!     the recurrence relations and information from the last
!     calls to the routine.

      if (firstleg) then

         LOLD1   = -2
         MOLD1   = -2
         XOLD1   = zero
         LEGOLD1 = zero

         firstleg = .false.

      else

!        See if we have the same values of M and X as the last call.

         if ((M.eq.MOLD1).and.(X.eq.XOLD1).and.(L.ne.M)) then

!           If L is the same as in the last call, then
!           LEGEN should also be the same. This will
!           almost certainly never happen.

            if (L.eq.LOLD1) then

               LEGEN = LEGOLD1
               goto 10

!           This is the most interesting case L = LOLD1+1.

            else if (L.eq.LOLD1+1) then

!              LOLD1 = MOLD1.

               if (LOLD1.eq.MOLD1) then

                  LEGEN = X*dble(2*M+1)*LEGOLD1
                  goto 10

!              LOLD1 = LOLD2 + 1.

               else if ((L.eq.LOLD2+2).and.(X.eq.XOLD2)) then

                  LEGEN = (X*dble(2*L-1)*LEGOLD1 - dble(L+M-1)*LEGOLD2)
     .                  / dble(L-M)
                  goto 10

               end if

            end if

         end if

      end if


!     *************************************
!     ***   NUMERICAL RECIPES ROUTINE   ***
!     *************************************

!     Compute P^M_M.

      PMM = one

      if (M.gt.0) then

         SOMX2 = sqrt((one-X)*(one+X))
         FACT = one

         do I=1,M
            PMM = - PMM*FACT*SOMX2
            FACT = FACT + two
         end do

      end if

      if (L.eq.M) then

         LEGEN = PMM

!     Compute P^M_M+1.

      else

         PMMP1 = X*dble(2*M+1)*PMM

         if (L.eq.M+1) then

            LEGEN = PMMP1

!        Compute P^M_L, L > M+1.

         else

            do LL=M+2,L
               PLL = (X*dble(2*LL-1)*PMMP1 - dble(LL+M-1)*PMM)
     .             / dble(LL-M)
               PMM = PMMP1
               PMMP1 = PLL
            end do

            LEGEN = PLL

          end if

      end if


!     ***************************
!     ***   SAVE OLD VALUES   ***
!     ***************************

 10   continue

      LOLD2   = LOLD1
      MOLD2   = MOLD1
      XOLD2   = XOLD1
      LEGOLD2 = LEGOLD1

      LOLD1   = L
      MOLD1   = M
      XOLD1   = X
      LEGOLD1 = LEGEN


!     **********************************************
!     ***   MULTIPLY WITH NORMALIZATION FACTOR   ***
!     **********************************************

!     For M=0 I use the normalization factor:
!
!     sqrt(2*L + 1)
!
!     For M non-zero I use the normalization factor:
!
!     sqrt( 2 (2*L + 1) (L-M)! / (L+M)! )
!
!     The extra factor of sqrt(2) is there because I
!     use a basis of sines and cosines and not the
!     standard complex exponential.
!
!     With this normalization, I ensure that my basis
!     functions f _lm are such that:
!
!     /
!     | f_lm f_l;mp sin(theta) dtheta dphi  =  4 pi delta_mm' delta_ll'
!     /
!
!     Notice the extra factor of 4 pi.  This is there because
!     for a sphere, I want the (0,0) coefficient to correspond
!     to the radius.

      factor = dble(2*L+1)

      if (M.ne.0) then
 
         aux = one
 
         do i=L-M+1,L+M
            aux = aux*dble(i)
         end do
 
         factor = two*factor/aux
 
      end if

      LEGEN = sqrt(factor)*LEGEN


!     ***************
!     ***   END   ***
!     ***************

      end function LEGEN