From c728826a1df5c5a6206ce221d1641fe8d263a31c Mon Sep 17 00:00:00 2001
From: Antoine Hoffmann <antoine.hoffmann@epfl.ch>
Date: Fri, 16 Feb 2024 16:07:35 +0100
Subject: [PATCH] add routines for  taylor and pade approx of kernel

---
 src/numerics_mod.F90 | 201 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 184 insertions(+), 17 deletions(-)

diff --git a/src/numerics_mod.F90 b/src/numerics_mod.F90
index 62f2421..357f092 100644
--- a/src/numerics_mod.F90
+++ b/src/numerics_mod.F90
@@ -77,31 +77,80 @@ SUBROUTINE evaluate_kernels
   USE grid,    ONLY : local_na, local_nj,ngj, local_nkx, local_nky, local_nz, ngz, jarray, kp2array,&
                       nzgrid
   USE species, ONLY : sigma2_tau_o2
+  USE model,    ONLY : KN_MODEL, ORDER, ORDER_NUM, ORDER_DEN
   IMPLICIT NONE
   INTEGER    :: j_int, ia, eo, ikx, iky, iz, ij
   REAL(xp)   :: j_xp, y_, factj, sigma_i
   sigma_i = 1._xp ! trivial singe sigma_a = sqrt(m_a/m_i)
 
-DO ia  = 1,local_na
-  DO eo  = 1,nzgrid
-    DO ikx = 1,local_nkx
-      DO iky = 1,local_nky
-        DO iz  = 1,local_nz + ngz
-          DO ij = 1,local_nj + ngj
-            j_int = jarray(ij)
-            j_xp  = REAL(j_int,xp)
-            y_    =  sigma2_tau_o2(ia) * kp2array(iky,ikx,iz,eo)
-            IF(j_int .LT. 0) THEN !ghosts values
-              kernel(ia,ij,iky,ikx,iz,eo) = 0._xp
-            ELSE
-              factj = REAL(GAMMA(j_xp+1._xp),xp)
-              kernel(ia,ij,iky,ikx,iz,eo) = y_**j_int*EXP(-y_)/factj
-            ENDIF
+  SELECT CASE (KN_MODEL)
+  CASE('taylor') ! developped with Leonhard Driever
+    ! Kernels based on the ORDER_NUM order Taylor series of J0
+    WRITE (*,*) 'Kernel approximation uses Taylor series with ', ORDER, ' powers of k'
+    DO ia  = 1,local_na
+      DO eo  = 1,nzgrid
+        DO ikx = 1,local_nkx
+          DO iky = 1,local_nky
+            DO iz  = 1,local_nz + ngz
+              DO ij = 1,local_nj + ngj
+                y_    =  sigma2_tau_o2(ia) * kp2array(iky,ikx,iz,eo)
+                j_int = jarray(ij)
+                IF (j_int > ORDER .OR. j_int < 0) THEN
+                  kernel(ia,ij,ikx,iky,iz,eo) = 0._xp
+                ELSE
+                  kernel(ia,ij,ikx,iky,iz,eo) = taylor_kernel_n(ORDER, j_int, y_)
+                ENDIF
+              ENDDO
+            ENDDO
           ENDDO
         ENDDO
       ENDDO
     ENDDO
-  ENDDO
+  CASE ('pade')
+    ! Kernels based on the ORDER_NUM / ORDER_DEN Pade approximation of the kernels
+    WRITE (*,*) 'Kernel approximation uses ', ORDER_NUM ,'/', ORDER_DEN, ' Pade approximation'
+    DO ia  = 1,local_na
+      DO eo  = 1,nzgrid
+        DO ikx = 1,local_nkx
+          DO iky = 1,local_nky
+            DO iz  = 1,local_nz + ngz
+              DO ij = 1,local_nj + ngj
+                y_    =  sigma2_tau_o2(ia) * kp2array(iky,ikx,iz,eo)
+                j_int = jarray(ij)
+                IF (j_int > ORDER_NUM .OR. j_int < 0) THEN
+                  kernel(ia,ij,ikx,iky,iz,eo) = 0._xp
+                ELSE
+                  kernel(ia,ij,ikx,iky,iz,eo) = pade_kernel_n(j_int, y_,ORDER_NUM,ORDER_DEN)
+                ENDIF
+              ENDDO
+            ENDDO
+          ENDDO
+        ENDDO
+      ENDDO
+    ENDDO
+  CASE DEFAULT
+    DO ia  = 1,local_na
+      DO eo  = 1,nzgrid
+        DO ikx = 1,local_nkx
+          DO iky = 1,local_nky
+            DO iz  = 1,local_nz + ngz
+              DO ij = 1,local_nj + ngj
+                j_int = jarray(ij)
+                j_xp  = REAL(j_int,xp)
+                y_    =  sigma2_tau_o2(ia) * kp2array(iky,ikx,iz,eo)
+                IF(j_int .LT. 0) THEN !ghosts values
+                  kernel(ia,ij,iky,ikx,iz,eo) = 0._xp
+                ELSE
+                  factj = REAL(GAMMA(j_xp+1._xp),xp)
+                  kernel(ia,ij,iky,ikx,iz,eo) = y_**j_int*EXP(-y_)/factj
+                ENDIF
+              ENDDO
+            ENDDO
+          ENDDO
+        ENDDO
+      ENDDO
+    ENDDO
+  END SELECT
 
   ! !! Correction term for the evaluation of the heat flux
   ! HF_phi_correction_operator(:,:,:) = &
@@ -117,7 +166,6 @@ DO ia  = 1,local_na
   !       -     (j_xp+1.0_xp) * Kernel(ia,ij+1,:,:,:,1) &
   !       -              j_xp * Kernel(ia,ij-1,:,:,:,1))
   ! ENDDO
-ENDDO
 END SUBROUTINE evaluate_kernels
 !******************************************************************************!
 
@@ -324,4 +372,123 @@ SUBROUTINE compute_lin_coeff
   ENDDO
 END SUBROUTINE compute_lin_coeff
 
+
+!******************************************************************************!
+!!!!!!! Auxilliary kernel functions/subroutines (developped with Leonhard Driever)
+!******************************************************************************!
+REAL(xp) FUNCTION taylor_kernel_n(order, n, y)
+	IMPLICIT NONE
+	INTEGER, INTENT(IN) :: order
+	INTEGER, INTENT(IN) :: n
+	REAL(xp), INTENT(IN) :: y
+	REAL(xp) :: sum_variable
+	INTEGER  ::  m
+	REAL(xp) :: m_dp, n_dp
+
+	n_dp = REAL(n, xp)
+	sum_variable = 0._xp
+	
+	DO m = n, order
+	   m_dp = REAL(m, xp)
+	   sum_variable = sum_variable + (-1._xp)**(m - n) * y**m / (GAMMA(n_dp + 1._xp) * GAMMA(m_dp - n_dp + 1._xp)) ! Denominator of m C n
+    END DO
+	taylor_kernel_n = sum_variable
+END FUNCTION taylor_kernel_n
+
+
+REAL(xp) FUNCTION pade_kernel_n(n, y, N_NUM, N_DEN)
+	IMPLICIT NONE
+	INTEGER, INTENT(IN) :: n, N_NUM, N_DEN
+	REAL(xp), INTENT(IN) :: y
+	REAL(xp) :: pade_numerator_coeffs(N_NUM + 1), pade_denominator_coeffs(N_NUM + 1)
+	REAL(xp) :: numerator_sum
+	REAL(xp) :: denominator_sum
+	INTEGER  :: m
+	! If N_NUM == 0, then the approximation should be the same as the Taylor approx. of N_NUM:
+	IF (N_NUM == 0) THEN
+	    pade_kernel_n = taylor_kernel_n(N_NUM, n, y)
+	ELSE
+    CALL find_pade_coefficients(pade_numerator_coeffs, pade_denominator_coeffs, n, N_NUM, N_DEN)
+    numerator_sum = 0
+    denominator_sum = 0
+    DO m = 0, N_NUM
+      numerator_sum = numerator_sum + pade_numerator_coeffs(m + 1) * y ** m
+    END DO
+    DO m = 0, N_NUM
+      denominator_sum = denominator_sum + pade_denominator_coeffs(m + 1) * y ** m
+    END DO
+    pade_kernel_n = numerator_sum / denominator_sum
+	END IF
+END FUNCTION pade_kernel_n
+
+
+SUBROUTINE find_pade_coefficients(pade_num_coeffs, pade_denom_coeffs, n, N_NUM, N_DEN)
+	IMPLICIT NONE
+#ifdef SINGLE_PRECISION
+  EXTERNAL :: SGESV ! Use DGESV rather than SGESV for double precision
+#else
+	EXTERNAL :: DGESV ! Use DGESV rather than SGESV for double precision
+#endif
+	INTEGER, INTENT (IN) ::  n, N_NUM, N_DEN ! index of the considered Kernel
+	REAL(xp), INTENT(OUT) :: pade_num_coeffs(N_NUM + 1), pade_denom_coeffs(N_NUM + 1) ! OUT rather than INOUT to make sure no information is retained from previous Kernel computations
+	
+	REAL(xp) :: taylor_kernel_coeffs(N_NUM + N_NUM + 1), denom_matrix(N_NUM, N_NUM)
+	INTEGER  :: m, j
+	REAL(xp) :: m_dp, n_dp
+	INTEGER  :: return_code  ! for DGESV
+	REAL(xp) :: pivot(N_NUM) ! for DGESV
+
+	n_dp = REAL(n, xp)
+	
+	! First find the kernel Taylor expansion coefficients
+	DO m = 0, N_NUM + N_NUM ! m here counts the order of the derivatives
+	   m_dp = REAL(m, xp)
+
+	   IF (m < n) THEN
+	      taylor_kernel_coeffs(m + 1) = 0
+	   ELSE   
+	      taylor_kernel_coeffs(m + 1) = (-1)**(n + m) / (GAMMA(n_dp + 1._xp) * GAMMA(m_dp - n_dp + 1._xp))
+	   END IF
+	END DO
+
+	! Next construct the denominator solving matrix
+	DO m = 1, N_NUM
+	   DO j = 1, N_NUM
+	      IF (N_NUM + m - j < 0) THEN
+		denom_matrix(m, j) = 0
+	      ELSE
+	      	denom_matrix(m, j) = taylor_kernel_coeffs(N_NUM + m - j + 1)
+	      END IF
+	   END DO
+	END DO
+
+	! Then solve for the denominator coefficients, setting the first one to 1
+	!!!! SOLVER NOT YET IMPLEMENTED!!!!
+	pade_denom_coeffs(1) = 1
+	pade_denom_coeffs(2:) = - taylor_kernel_coeffs(N_NUM + 2 : N_NUM + N_NUM + 1) ! First acts as RHS vector for equation, is then transformed to solution by DGESV
+#ifdef SINGLE_PRECISION
+  CALL SGESV(N_NUM, 1, denom_matrix, N_NUM, pivot, pade_denom_coeffs(2:), N_NUM, return_code) ! LAPACK solver for matrix equation. Note that denom_matrix is now no longer as expected
+#else
+  CALL DGESV(N_NUM, 1, denom_matrix, N_NUM, pivot, pade_denom_coeffs(2:), N_NUM, return_code) ! LAPACK solver for matrix equation. Note that denom_matrix is now no longer as expected
+#endif
+
+
+	! Print an error message in case there was a problem with the solver
+	IF (return_code /= 0) THEN
+	   WRITE (*,*) 'An error occurred in the solving for the Pade denominator coefficients. The error code is: ', return_code
+	END IF
+
+	! Finally compute the numerator coefficients
+	DO m = 1, N_NUM + 1
+	   pade_num_coeffs(m) = 0 ! As the array is not automatically filled with zeros
+	   DO j = 1, m	
+      	      !num_matrix(m, j) = taylor_kernel_coeffs(m - j + 1)
+	      pade_num_coeffs(m) = pade_num_coeffs(m) + pade_denom_coeffs(j) * taylor_kernel_coeffs(m - j + 1)
+	   END DO
+	END DO
+
+END SUBROUTINE find_pade_coefficients
+!******************************************************************************!
+
+
 END MODULE numerics
-- 
GitLab