DCT сложных массивов с FFTW в Фортране: как указать на массив мнимой части?
Я пишу псевдоспектральный код CFD на Фортране, который, по сути, является шаговым шагом по уравнениям Навье-Стокса в плоском слое. В моем случае это действительно трехмерный код, но проблему можно хорошо понять в 2d, поэтому я буду придерживаться этого случая. Геометрически мой 2-мерный плоский слой ограничен y=0
а также y=1
и является периодическим вдоль другого направления x
, Не вдаваясь слишком много в сорняки, эффективная дискретизация состоит в том, чтобы разложить поля (например, скорость) на полиномы Чебышева вдоль y
и моды Фурье вдоль x
, Чебышевские многочлены по существу являются замаскированными косинусами на искаженной сетке. Уравнения Навье-Стокса имеют простую форму в спектральном пространстве, за исключением нелинейного члена. Таким образом, большинство вычислений выполняется в спектральном пространстве со случайными отклонениями в физическом пространстве для вычисления нелинейного члена: необходимо преобразовать двумерный массив комплексных коэффициентов Чебышева-Фурье в соответствующее двумерное поле (то есть массив вещественных значений на сетке). Без других ограничений это преобразование относительно легко реализовать. Например, начиная со сложных спектральных коэффициентов - назовем их c_in(NX, NY/2+1)
- можно взять комплекс к реальному, дискретному преобразованию Фурье по x
для каждого значения y
получить двумерный массив вещественных чебышевских коэффициентов. Затем можно выполнить дискретное косинусное преобразование (FFTW_REDFT
в FFTW) вместе y
для всех x
и, вуаля, наконец-то получается настоящее поле, r_out(NX,NY)
,
Корень всех проблем в том, что по некоторым причинам мне нужно сначала вычислить DCT. Это проблема, потому что косинусные преобразования реализуются только для реальных данных в FFTW. Различные ограничения приводят к тому, что я не хочу разбивать свой сложный массив спектральных коэффициентов на два реальных массива для действительной и мнимой частей. Учитывая эти ограничения на структуру данных, возникает вопрос: как эффективно заставить FFTW вычислять несколько DCT вдоль первого индекса массива комплексных чисел.
Пока что мое решение состоит в использовании plan_many_r2r
расширенный интерфейс для определения преобразования, которое перепрыгивает через мнимые значения: я установил idist
2. В результате, если я использую этот план с указателем ptr2real_in
связано с реальной частью c_in(1,1)
вычисляется косинус-преобразование всех вещественных частей. Затем я повторяю выполнение плана с указателем ptr2imag_in
связано с мнимой частью c_in(1,1)
, После этого вычисление комплексного и реального ДПФ по второму измерению становится простым.
Так что суть этого подхода заключается в определении ptr2imag_in
, который на самом деле является адресом памяти c_in(1,1)
смещен на размер в памяти C_double
, Ниже приведен минимальный пример, который работает, но выглядит для меня неуклюже. В частности, я определяю указатель на мнимую часть сложного массива таким образом
cptr = C_loc(c_in(1,1))
Call C_F_Pointer(cptr, ptr2imag_in, [2*NX, (NY/2+1)])
cptr = C_loc(ptr2imag_in(2,1))
Call C_F_Pointer(cptr, ptr2imag_in, [2*NX, (NY/2+1)])
Мне кажется, что все, что мне нужно сделать, это сдвинуться cptr
на 8 байт. Как я мог это сделать? Сбой следующего кода:
cptr = C_loc(c_in(1,1))
cptr = cptr + 8
Call C_F_Pointer(cptr, ptr2imag_in, [2*NX, (NY/2+1)])
Ниже приведен полный минимальный пример взятия DCT, сопровождаемого комплексным и реальным DFT:
Program monkeying_with_dct
Use, Intrinsic :: ISO_C_BINDING
Implicit None
include 'fftw3.f03'
Integer, Parameter :: dp = C_Double
Complex(C_double), Pointer :: c_in (:,:)
Complex(C_double), Pointer :: c_out(:,:)
Real(C_Double), Pointer :: r_out(:,:)
Real(C_Double), Pointer :: ptr2real_in (:,:)
Real(C_Double), Pointer :: ptr2real_out(:,:)
Real(C_Double), Pointer :: ptr2imag_in (:,:)
Real(C_Double), Pointer :: ptr2imag_out(:,:)
Type(C_ptr) :: cptr
Type(C_ptr) :: plan_IDCT
Type(C_ptr) :: plan_C2R
Type(C_ptr) :: pdum
Integer, Parameter :: NX = 512
Integer, Parameter :: NY = 1024
print *,'... allocating memory ...'
pdum = fftw_alloc_complex(int((NY/2+1)*NX, C_size_T))
Call C_F_Pointer(pdum, c_in , [NX, NY/2+1])
pdum = fftw_alloc_complex(int((NY/2+1)*NX, C_size_T))
Call C_F_Pointer(pdum, c_out, [NX, NY/2+1])
pdum = fftw_alloc_real(int(NY*NX, C_size_T))
Call C_F_Pointer(pdum, r_out, [NX, NY])
print *,'... initializing data ...'
c_in = Cmplx(0._dp, 0._dp, Kind=dp)
c_in(2,3) = Cmplx(1._dp, 0.5_dp, Kind=dp)
print *, '... defining a pointer to the real part of input complex data ...'
cptr = C_loc(c_in(1,1))
Call C_F_Pointer(cptr, ptr2real_in, [2*NX, (NY/2+1)])
print *, '... defining a pointer to the imag part of input complex data ...'
cptr = C_loc(c_in(1,1))
Call C_F_Pointer(cptr, ptr2imag_in, [2*NX, (NY/2+1)])
cptr = C_loc(ptr2imag_in(2,1))
Call C_F_Pointer(cptr, ptr2imag_in, [2*NX, (NY/2+1)])
print *, '... defining a pointer to the real part of transformed complex data ...'
cptr = C_loc(c_out(1,1))
Call C_F_Pointer(cptr, ptr2real_out, [2*NX, (NY/2+1)])
print *, '... defining a pointer to the imag part of transformed complex data ...'
cptr = C_loc(c_out(1,1))
Call C_F_Pointer(cptr, ptr2imag_out, [2*NX, (NY/2+1)])
cptr = C_loc(ptr2imag_out(2,1))
Call C_F_Pointer(cptr, ptr2imag_out, [2*NX, (NY/2+1)])
print*, '... planning IDCT ...'
plan_IDCT = fftw_plan_many_r2r(1, [NX], (NY/2+1), &
ptr2real_in, [2*NX], 2, 2*NX, &
ptr2real_out, [2*NX], 2, 2*NX, &
[FFTW_REDFT01] , FFTW_MEASURE)
print*, '... planning C2R DFT ...'
plan_C2R = fftw_plan_many_dft_c2r(1, [NY], NX, &
c_out, [NY/2+1], NX, 1, &
r_out, [NY], NX, 1, &
FFTW_MEASURE)
print*, '... DCT of the real part ...'
Call fftw_execute_r2r(plan_IDCT, ptr2real_in, ptr2real_out)
print*, '... DCT of the imaginary part ...'
Call fftw_execute_r2r(plan_IDCT, ptr2imag_in, ptr2imag_out)
print*, '... DFT Complex to real ...'
Call fftw_execute_dft_c2r(plan_C2R, c_out,r_out)
End Program monkeying_with_dct
2 ответа
Можно уже transfer()
указатель на целое число, сделать сдвиг и transfer()
назад, если это то, что вы хотите.
cptr = transfer( transfer(cptr, 1_c_intptr_t) + 8_c_intptr_t , cptr)
или можно просто вызвать маленький C, который делает арифметику указателей более управляемым способом. Но я не уверен, что это действительно то, что вам нужно.
В Fortran 2008 можно просто использовать %im
синтаксис, но поддержка компилятора пока не очень хорошая, gfortran его вообще не поддерживает..
Вы можете попробовать что-то в смысле следующего пробного 1-dim примера (я не уверен, насколько эффективен скомпилированный код...).
program mapctor
implicit none
integer, parameter :: n = 10
integer :: i
complex :: cx(n) ! the couplex array
real :: rx(2,n) ! mapped to real
EQUIVALENCE(cx(1), rx(1,1)) ! some old fortran stuff !
! fill in some test values
do i=1,n
cx(i) = cmplx(i,-i)
enddo
write(6,*)"REAL PART:"
call xfft(rx(1,:),n)
write(6,*)"IMAG PART:"
call xfft(rx(2,:),n)
end program mapctor
subroutine xfft(x,n) ! mock-up fft routine, or whatever
implicit none
real, intent(in) :: x(n)
integer, intent(in) :: n
write(6,'(f12.6)') x
end subroutine xfft