@@ -76,7 +76,7 @@ private:
7676 EmpCylSL::AxiDiskPtr model;
7777 double xmin, xmax, dx, ascl;
7878 int lmax, numr;
79- bool logr, xscl;
79+ bool logr, xscl, prenorm= true ;
8080
8181 static const std::string cachefile;
8282
@@ -94,42 +94,126 @@ private:
9494 exp (0.5 *(lgamma (1.0 +l-m) - lgamma (1.0 +l+m)));
9595 }
9696
97- // ! Ylm evaluation
98- // CAUTION: this will fail at l>150 owing to double precision issues.
99- double Ylm (int l, int m, double cosx)
97+
98+ // ! Standard recursion
99+ // ! CAUTION: this will fail at l>150 owing to double precision issues.
100+ double Ylm_standard (int l, int m, double cosx)
100101 {
101102 int M = abs (m);
102103 double plm = plgndr (l, M, cosx);
103104 if (std::isnan (plm) or std::isinf (plm))
104- std::cout << " Failure in pldndr at l=" << l
105+ std::cout << " Failure in plgndr at l=" << l
105106 << " m=" << m << " cosx=" << cosx << std::endl;
106107 return Nlm (l, M) * plm * pow (-1.0 , M);
107108 }
108109
109110 // ! Partial derivative of Ylm in theta
110- double Zlm (int l, int m, double cosx)
111+ // ! Standard version
112+ double Zlm_standard (int l, int m, double cosx)
111113 {
112114 if (l==0 or fabs (cosx)>=1.0 ) return 0.0 ;
113115
114116 int M = abs (m);
115117 double dplm = dplgndr (l, M, cosx);
116118 if (std::isnan (dplm) or std::isinf (dplm))
117- std::cout << " Failure in pldndr at l=" << l
119+ std::cout << " Failure in dplgndr at l=" << l
118120 << " m=" << m << " cosx=" << cosx << std::endl;
119121
120122 return -Nlm (l, M) * dplm * pow (-1.0 , M) * sqrt (1.0 - cosx*cosx);
121123 }
122124
125+
126+ // ! Prenormalized Ylm evaluation
127+ double Ylm_prenorm (int L, int m, double x)
128+ {
129+ int M = abs (m);
130+
131+ // Initial value precompile
132+ constexpr double ylm00 = 0.5 /sqrt (M_PI );
133+
134+ // Assign initial value
135+ double pmm = ylm00;
136+
137+ // Recurrence to get l=m=M
138+ if (M>0 ) {
139+ double somx2 = std::sqrt ( (1.0 - x)*(1.0 + x) );
140+ for (int l=1 ; l<=M; l++) {
141+ pmm = -std::sqrt (1.0 + 0.5 /l)*somx2*pmm;
142+ }
143+ }
144+
145+ // Even or odd M?
146+ double facM = 1.0 ;
147+ if (M & 0x1 ) facM = -1.0 ;
148+
149+ // We are done if L equals M
150+ if (L == M) {
151+ return pmm * facM;
152+ }
153+ else {
154+ // L=M+1
155+ double pmmp1 = std::sqrt (1.0 + 2.0 *(M+1 ))*x*pmm, pll = 0.0 ;
156+ if (L == M+1 ) {
157+ return pmmp1 * facM;
158+ }
159+ // L>M+1 recursion
160+ else {
161+ for (int l=M+2 ; l<=L; l++) {
162+ // Recursion constants
163+ double pfac = (2.0 *l + 1.0 )/(2.0 *l - 3.0 )/(l*l - M*M);
164+ double alpha = std::sqrt ( pfac * (4 *(l-1 )*(l-1 ) - 1 ) );
165+ double beta = std::sqrt ( pfac * ((l-1 )*(l-1 ) - M*M ) );
166+ // Compute next term
167+ pll = alpha*x*pmmp1 - beta*pmm;
168+ pmm = pmmp1;
169+ pmmp1 = pll;
170+ }
171+ return pll * facM;
172+ }
173+ }
174+ }
175+
176+ // ! Derivative of Ylm prenormalized
177+ double Zlm_prenorm (int L, int m, double x)
178+ {
179+ int M = abs (m);
180+ double Ylmp = Ylm_prenorm (L+1 , M, x), Ylm = Ylm_prenorm (L, M, x);
181+ double fac = -1.0 /std::sqrt (1.0 - x*x);
182+ double fac2 = 2.0 *L + 1.0 , fac3 = 2.0 *L + 3.0 ;
183+ return fac*( Ylm*x*(1.0 + L) - Ylmp*std::sqrt ( fac2/fac3*(fac2 + L*L - M*M) ) );
184+ }
185+
186+ // ! Ylm evaluation
187+ double Ylm (int l, int m, double cosx)
188+ {
189+ if (prenorm)
190+ return Ylm_prenorm (l, m, cosx);
191+ else
192+ return Ylm_standard (l, m, cosx);
193+ }
194+
195+ // ! Ylm derivative
196+ double Zlm (int l, int m, double cosx)
197+ {
198+ if (prenorm)
199+ return Zlm_prenorm (l, m, cosx);
200+ else
201+ return Zlm_standard (l, m, cosx);
202+ }
203+
204+ // ! Coordinate scaling: scaled to physical
123205 double x_to_r (double x)
124206 {
125207 return ascl * x/(1.0 - x);
126208 }
127209
210+ // ! Coordinate scaling: physical to scaled
128211 double r_to_x (double r)
129212 {
130213 return r/(r + ascl);
131214 }
132215
216+ // Coordinate scaling: Jacobian
133217 double dr_to_dx (double x)
134218 {
135219 return ascl/(1.0 - x)/(1.0 - x);
@@ -149,6 +233,9 @@ public:
149233 // ! Evaluation where the return tuple is potential, dPhi/dR, dPhi/dz, dPhi/dphi
150234 std::tuple<double , double , double , double > operator ()(double R, double z, double phi=0 );
151235
236+ // ! Set prenorm (true) standard (false) evaluation
237+ void setPrenorm (bool b) { prenorm = b; }
238+
152239};
153240
154241#endif
0 commit comments