xref: /petsc/src/mat/impls/fft/fftw/fftw.c (revision d63f877fec171bed822722979cba2adffb37de2a)
1 
2 /*
3     Provides an interface to the FFTW package.
4     Testing examples can be found in ~src/mat/examples/tests
5 */
6 
7 #include <../src/mat/impls/fft/fft.h>   /*I "petscmat.h" I*/
8 EXTERN_C_BEGIN
9 #include <fftw3-mpi.h>
10 EXTERN_C_END
11 
12 typedef struct {
13   fftw_plan   p_forward,p_backward;
14   unsigned    p_flag; /* planner flags, FFTW_ESTIMATE,FFTW_MEASURE, FFTW_PATIENT, FFTW_EXHAUSTIVE */
15   PetscScalar *finarray,*foutarray,*binarray,*boutarray; /* keep track of arrays becaue fftw plan should be
16                                                             executed for the arrays with which the plan was created */
17 } Mat_FFTW;
18 
19 extern PetscErrorCode MatMult_SeqFFTW(Mat,Vec,Vec);
20 extern PetscErrorCode MatMultTranspose_SeqFFTW(Mat,Vec,Vec);
21 extern PetscErrorCode MatMult_MPIFFTW(Mat,Vec,Vec);
22 extern PetscErrorCode MatMultTranspose_MPIFFTW(Mat,Vec,Vec);
23 extern PetscErrorCode MatDestroy_FFTW(Mat);
24 extern PetscErrorCode VecDestroy_MPIFFTW(Vec);
25 extern PetscErrorCode MatGetVecs_FFTW(Mat,Vec*,Vec*);
26 
27 #undef __FUNCT__
28 #define __FUNCT__ "MatMult_SeqFFTW"
29 PetscErrorCode MatMult_SeqFFTW(Mat A,Vec x,Vec y)
30 {
31   PetscErrorCode ierr;
32   Mat_FFT        *fft  = (Mat_FFT*)A->data;
33   Mat_FFTW       *fftw = (Mat_FFTW*)fft->data;
34   PetscScalar    *x_array,*y_array;
35   PetscInt       ndim=fft->ndim,*dim=fft->dim;
36 
37   PetscFunctionBegin;
38 #if !defined(PETSC_USE_COMPLEX)
39   SETERRQ(PETSC_COMM_WORLD,PETSC_ERR_SUP,"not support for real numbers");
40 #endif
41   ierr = VecGetArray(x,&x_array);CHKERRQ(ierr);
42   ierr = VecGetArray(y,&y_array);CHKERRQ(ierr);
43   if (!fftw->p_forward){ /* create a plan, then excute it */
44     switch (ndim){
45     case 1:
46       fftw->p_forward = fftw_plan_dft_1d(dim[0],(fftw_complex*)x_array,(fftw_complex*)y_array,FFTW_FORWARD,fftw->p_flag);
47       break;
48     case 2:
49       fftw->p_forward = fftw_plan_dft_2d(dim[0],dim[1],(fftw_complex*)x_array,(fftw_complex*)y_array,FFTW_FORWARD,fftw->p_flag);
50       break;
51     case 3:
52       fftw->p_forward = fftw_plan_dft_3d(dim[0],dim[1],dim[2],(fftw_complex*)x_array,(fftw_complex*)y_array,FFTW_FORWARD,fftw->p_flag);
53       break;
54     default:
55       fftw->p_forward = fftw_plan_dft(ndim,dim,(fftw_complex*)x_array,(fftw_complex*)y_array,FFTW_FORWARD,fftw->p_flag);
56       break;
57     }
58     fftw->finarray  = x_array;
59     fftw->foutarray = y_array;
60     /* Warning: if (fftw->p_flag!==FFTW_ESTIMATE) The data in the in/out arrays is overwritten!
61                 planning should be done before x is initialized! See FFTW manual sec2.1 or sec4 */
62     fftw_execute(fftw->p_forward);
63   } else { /* use existing plan */
64     if (fftw->finarray != x_array || fftw->foutarray != y_array){ /* use existing plan on new arrays */
65       fftw_execute_dft(fftw->p_forward,(fftw_complex*)x_array,(fftw_complex*)y_array);
66     } else {
67       fftw_execute(fftw->p_forward);
68     }
69   }
70   ierr = VecRestoreArray(y,&y_array);CHKERRQ(ierr);
71   ierr = VecRestoreArray(x,&x_array);CHKERRQ(ierr);
72   PetscFunctionReturn(0);
73 }
74 
75 #undef __FUNCT__
76 #define __FUNCT__ "MatMultTranspose_SeqFFTW"
77 PetscErrorCode MatMultTranspose_SeqFFTW(Mat A,Vec x,Vec y)
78 {
79   PetscErrorCode ierr;
80   Mat_FFT        *fft = (Mat_FFT*)A->data;
81   Mat_FFTW       *fftw = (Mat_FFTW*)fft->data;
82   PetscScalar    *x_array,*y_array;
83   PetscInt       ndim=fft->ndim,*dim=fft->dim;
84 
85   PetscFunctionBegin;
86 #if !defined(PETSC_USE_COMPLEX)
87   SETERRQ(PETSC_COMM_WORLD,PETSC_ERR_SUP,"not support for real numbers");
88 #endif
89   ierr = VecGetArray(x,&x_array);CHKERRQ(ierr);
90   ierr = VecGetArray(y,&y_array);CHKERRQ(ierr);
91   if (!fftw->p_backward){ /* create a plan, then excute it */
92     switch (ndim){
93     case 1:
94       fftw->p_backward = fftw_plan_dft_1d(dim[0],(fftw_complex*)x_array,(fftw_complex*)y_array,FFTW_BACKWARD,fftw->p_flag);
95       break;
96     case 2:
97       fftw->p_backward = fftw_plan_dft_2d(dim[0],dim[1],(fftw_complex*)x_array,(fftw_complex*)y_array,FFTW_BACKWARD,fftw->p_flag);
98       break;
99     case 3:
100       fftw->p_backward = fftw_plan_dft_3d(dim[0],dim[1],dim[2],(fftw_complex*)x_array,(fftw_complex*)y_array,FFTW_BACKWARD,fftw->p_flag);
101       break;
102     default:
103       fftw->p_backward = fftw_plan_dft(ndim,dim,(fftw_complex*)x_array,(fftw_complex*)y_array,FFTW_BACKWARD,fftw->p_flag);
104       break;
105     }
106     fftw->binarray  = x_array;
107     fftw->boutarray = y_array;
108     fftw_execute(fftw->p_backward);CHKERRQ(ierr);
109   } else { /* use existing plan */
110     if (fftw->binarray != x_array || fftw->boutarray != y_array){ /* use existing plan on new arrays */
111       fftw_execute_dft(fftw->p_backward,(fftw_complex*)x_array,(fftw_complex*)y_array);
112     } else {
113       fftw_execute(fftw->p_backward);CHKERRQ(ierr);
114     }
115   }
116   ierr = VecRestoreArray(y,&y_array);CHKERRQ(ierr);
117   ierr = VecRestoreArray(x,&x_array);CHKERRQ(ierr);
118   PetscFunctionReturn(0);
119 }
120 
121 #undef __FUNCT__
122 #define __FUNCT__ "MatMult_MPIFFTW"
123 PetscErrorCode MatMult_MPIFFTW(Mat A,Vec x,Vec y)
124 {
125   PetscErrorCode ierr;
126   Mat_FFT        *fft  = (Mat_FFT*)A->data;
127   Mat_FFTW       *fftw = (Mat_FFTW*)fft->data;
128   PetscScalar    *x_array,*y_array;
129   PetscInt       ndim=fft->ndim,*dim=fft->dim;
130   MPI_Comm       comm=((PetscObject)A)->comm;
131 
132   PetscFunctionBegin;
133 #if !defined(PETSC_USE_COMPLEX)
134   SETERRQ(PETSC_COMM_WORLD,PETSC_ERR_SUP,"not support for real numbers");
135 #endif
136   ierr = VecGetArray(x,&x_array);CHKERRQ(ierr);
137   ierr = VecGetArray(y,&y_array);CHKERRQ(ierr);
138   if (!fftw->p_forward){ /* create a plan, then excute it */
139     switch (ndim){
140     case 1:
141       fftw->p_forward = fftw_mpi_plan_dft_1d(dim[0],(fftw_complex*)x_array,(fftw_complex*)y_array,comm,FFTW_FORWARD,fftw->p_flag);
142       break;
143     case 2:
144       fftw->p_forward = fftw_mpi_plan_dft_2d(dim[0],dim[1],(fftw_complex*)x_array,(fftw_complex*)y_array,comm,FFTW_FORWARD,fftw->p_flag);
145       break;
146     case 3:
147       fftw->p_forward = fftw_mpi_plan_dft_3d(dim[0],dim[1],dim[2],(fftw_complex*)x_array,(fftw_complex*)y_array,comm,FFTW_FORWARD,fftw->p_flag);
148       break;
149     default:
150       fftw->p_forward = fftw_mpi_plan_dft(ndim,(const ptrdiff_t*)dim,(fftw_complex*)x_array,(fftw_complex*)y_array,comm,FFTW_FORWARD,fftw->p_flag);
151       break;
152     }
153     fftw->finarray  = x_array;
154     fftw->foutarray = y_array;
155     /* Warning: if (fftw->p_flag!==FFTW_ESTIMATE) The data in the in/out arrays is overwritten!
156                 planning should be done before x is initialized! See FFTW manual sec2.1 or sec4 */
157     fftw_execute(fftw->p_forward);
158   } else { /* use existing plan */
159     if (fftw->finarray != x_array || fftw->foutarray != y_array){ /* use existing plan on new arrays */
160       fftw_execute_dft(fftw->p_forward,(fftw_complex*)x_array,(fftw_complex*)y_array);
161     } else {
162       fftw_execute(fftw->p_forward);
163     }
164   }
165   ierr = VecRestoreArray(y,&y_array);CHKERRQ(ierr);
166   ierr = VecRestoreArray(x,&x_array);CHKERRQ(ierr);
167   PetscFunctionReturn(0);
168 }
169 
170 #undef __FUNCT__
171 #define __FUNCT__ "MatMultTranspose_MPIFFTW"
172 PetscErrorCode MatMultTranspose_MPIFFTW(Mat A,Vec x,Vec y)
173 {
174   PetscErrorCode ierr;
175   Mat_FFT        *fft  = (Mat_FFT*)A->data;
176   Mat_FFTW       *fftw = (Mat_FFTW*)fft->data;
177   PetscScalar    *x_array,*y_array;
178   PetscInt       ndim=fft->ndim,*dim=fft->dim;
179   MPI_Comm       comm=((PetscObject)A)->comm;
180 
181   PetscFunctionBegin;
182 #if !defined(PETSC_USE_COMPLEX)
183   SETERRQ(PETSC_COMM_WORLD,PETSC_ERR_SUP,"not support for real numbers");
184 #endif
185   ierr = VecGetArray(x,&x_array);CHKERRQ(ierr);
186   ierr = VecGetArray(y,&y_array);CHKERRQ(ierr);
187   if (!fftw->p_backward){ /* create a plan, then excute it */
188     switch (ndim){
189     case 1:
190       fftw->p_backward = fftw_mpi_plan_dft_1d(dim[0],(fftw_complex*)x_array,(fftw_complex*)y_array,comm,FFTW_BACKWARD,fftw->p_flag);
191       break;
192     case 2:
193       fftw->p_backward = fftw_mpi_plan_dft_2d(dim[0],dim[1],(fftw_complex*)x_array,(fftw_complex*)y_array,comm,FFTW_BACKWARD,fftw->p_flag);
194       break;
195     case 3:
196       fftw->p_backward = fftw_mpi_plan_dft_3d(dim[0],dim[1],dim[2],(fftw_complex*)x_array,(fftw_complex*)y_array,comm,FFTW_BACKWARD,fftw->p_flag);
197       break;
198     default:
199       fftw->p_backward = fftw_mpi_plan_dft(ndim,(const ptrdiff_t*)dim,(fftw_complex*)x_array,(fftw_complex*)y_array,comm,FFTW_BACKWARD,fftw->p_flag);
200       break;
201     }
202     fftw->binarray  = x_array;
203     fftw->boutarray = y_array;
204     fftw_execute(fftw->p_backward);CHKERRQ(ierr);
205   } else { /* use existing plan */
206     if (fftw->binarray != x_array || fftw->boutarray != y_array){ /* use existing plan on new arrays */
207       fftw_execute_dft(fftw->p_backward,(fftw_complex*)x_array,(fftw_complex*)y_array);
208     } else {
209       fftw_execute(fftw->p_backward);CHKERRQ(ierr);
210     }
211   }
212   ierr = VecRestoreArray(y,&y_array);CHKERRQ(ierr);
213   ierr = VecRestoreArray(x,&x_array);CHKERRQ(ierr);
214   PetscFunctionReturn(0);
215 }
216 
217 #undef __FUNCT__
218 #define __FUNCT__ "MatDestroy_FFTW"
219 PetscErrorCode MatDestroy_FFTW(Mat A)
220 {
221   Mat_FFT        *fft = (Mat_FFT*)A->data;
222   Mat_FFTW       *fftw = (Mat_FFTW*)fft->data;
223   PetscErrorCode ierr;
224 
225   PetscFunctionBegin;
226 #if !defined(PETSC_USE_COMPLEX)
227   SETERRQ(PETSC_COMM_WORLD,PETSC_ERR_SUP,"not support for real numbers");
228 #endif
229   fftw_destroy_plan(fftw->p_forward);
230   fftw_destroy_plan(fftw->p_backward);
231   ierr = PetscFree(fft->data);CHKERRQ(ierr);
232   PetscFunctionReturn(0);
233 }
234 
235 #include <../src/vec/vec/impls/mpi/pvecimpl.h>   /*I  "petscvec.h"   I*/
236 #undef __FUNCT__
237 #define __FUNCT__ "VecDestroy_MPIFFTW"
238 PetscErrorCode VecDestroy_MPIFFTW(Vec v)
239 {
240   PetscErrorCode  ierr;
241   PetscScalar     *array;
242 
243   PetscFunctionBegin;
244 #if !defined(PETSC_USE_COMPLEX)
245   SETERRQ(PETSC_COMM_WORLD,PETSC_ERR_SUP,"not support for real numbers");
246 #endif
247   ierr = VecGetArray(v,&array);CHKERRQ(ierr);
248   fftw_free((fftw_complex*)array);CHKERRQ(ierr);
249   ierr = VecRestoreArray(v,&array);CHKERRQ(ierr);
250   ierr = VecDestroy_MPI(v);CHKERRQ(ierr);
251   PetscFunctionReturn(0);
252 }
253 
254 #undef __FUNCT__
255 #define __FUNCT__ "MatGetVecs_FFTW"
256 /*
257    MatGetVecs_FFTW - Get vector(s) compatible with the matrix, i.e. with the
258      parallel layout determined by FFTW
259 
260    Collective on Mat
261 
262    Input Parameter:
263 .  mat - the matrix
264 
265    Output Parameter:
266 +   fin - (optional) input vector of forward FFTW
267 -   fout - (optional) output vector of forward FFTW
268 
269   Level: advanced
270 
271 .seealso: MatCreateFFTW()
272 */
273 PetscErrorCode  MatGetVecs_FFTW(Mat A,Vec *fin,Vec *fout)
274 {
275   PetscErrorCode ierr;
276   PetscMPIInt    size,rank;
277   MPI_Comm       comm=((PetscObject)A)->comm;
278   Mat_FFT        *fft = (Mat_FFT*)A->data;
279   PetscInt       N=fft->N;
280 
281   PetscFunctionBegin;
282 #if !defined(PETSC_USE_COMPLEX)
283   SETERRQ(PETSC_COMM_WORLD,PETSC_ERR_SUP,"not support for real numbers");
284 #endif
285   PetscValidHeaderSpecific(A,MAT_CLASSID,1);
286   PetscValidType(A,1);
287 
288   ierr = MPI_Comm_size(comm, &size);CHKERRQ(ierr);
289   ierr = MPI_Comm_rank(comm, &rank);CHKERRQ(ierr);
290   if (size == 1){ /* sequential case */
291     if (fin) {ierr = VecCreateSeq(PETSC_COMM_SELF,N,fin);CHKERRQ(ierr);}
292     if (fout){ierr = VecCreateSeq(PETSC_COMM_SELF,N,fout);CHKERRQ(ierr);}
293   } else {        /* mpi case */
294     ptrdiff_t      alloc_local,local_n0,local_0_start;
295     ptrdiff_t      local_n1,local_1_end;
296     PetscInt       ndim=fft->ndim,*dim=fft->dim,n=fft->n;
297     fftw_complex   *data_fin,*data_fout;
298 
299     switch (ndim){
300     case 1:
301       /* Get local size */
302 
303       alloc_local = fftw_mpi_local_size_1d(dim[0],comm,FFTW_FORWARD,FFTW_ESTIMATE,&local_n0,&local_0_start,&local_n1,&local_1_end);
304       if (fin) {
305         data_fin  = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*alloc_local);
306         ierr = VecCreateMPIWithArray(comm,local_n0,N,(const PetscScalar*)data_fin,fin);CHKERRQ(ierr);
307         (*fin)->ops->destroy   = VecDestroy_MPIFFTW;
308       }
309       if (fout) {
310         data_fout = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*alloc_local);
311         ierr = VecCreateMPIWithArray(comm,local_n1,N,(const PetscScalar*)data_fout,fout);CHKERRQ(ierr);
312         (*fout)->ops->destroy   = VecDestroy_MPIFFTW;
313       }
314       break;
315     case 2:
316       /* Get local size */
317       alloc_local = fftw_mpi_local_size_2d(dim[0],dim[1],comm,&local_n0,&local_0_start);
318       if (fin) {
319         data_fin  = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*alloc_local);
320         ierr = VecCreateMPIWithArray(comm,n,N,(const PetscScalar*)data_fin,fin);CHKERRQ(ierr);
321         (*fin)->ops->destroy   = VecDestroy_MPIFFTW;
322       }
323       if (fout) {
324         data_fout = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*alloc_local);
325         ierr = VecCreateMPIWithArray(comm,n,N,(const PetscScalar*)data_fout,fout);CHKERRQ(ierr);
326         (*fout)->ops->destroy   = VecDestroy_MPIFFTW;
327       }
328       break;
329     case 3:
330       /* Get local size */
331       alloc_local = fftw_mpi_local_size_3d(dim[0],dim[1],dim[2],comm,&local_n0,&local_0_start);
332       if (fin) {
333         data_fin  = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*alloc_local);
334         ierr = VecCreateMPIWithArray(comm,n,N,(const PetscScalar*)data_fin,fin);CHKERRQ(ierr);
335         (*fin)->ops->destroy   = VecDestroy_MPIFFTW;
336       }
337       if (fout) {
338         data_fout = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*alloc_local);
339         ierr = VecCreateMPIWithArray(comm,n,N,(const PetscScalar*)data_fout,fout);CHKERRQ(ierr);
340         (*fout)->ops->destroy   = VecDestroy_MPIFFTW;
341       }
342       break;
343     default:
344       /* Get local size */
345       alloc_local = fftw_mpi_local_size(ndim,(const ptrdiff_t*)dim,comm,&local_n0,&local_0_start);
346       if (fin) {
347         data_fin  = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*alloc_local);
348         ierr = VecCreateMPIWithArray(comm,n,N,(const PetscScalar*)data_fin,fin);CHKERRQ(ierr);
349         (*fin)->ops->destroy   = VecDestroy_MPIFFTW;
350       }
351       if (fout) {
352         data_fout = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*alloc_local);
353         ierr = VecCreateMPIWithArray(comm,n,N,(const PetscScalar*)data_fout,fout);CHKERRQ(ierr);
354         (*fout)->ops->destroy   = VecDestroy_MPIFFTW;
355       }
356       break;
357     }
358   }
359   if (fin){
360     ierr = PetscLayoutReference(A->cmap,&(*fin)->map);CHKERRQ(ierr);
361   }
362   if (fout){
363     ierr = PetscLayoutReference(A->rmap,&(*fout)->map);CHKERRQ(ierr);
364   }
365   PetscFunctionReturn(0);
366 }
367 
368 EXTERN_C_BEGIN
369 #undef __FUNCT__
370 #define __FUNCT__ "MatCreate_FFTW"
371 /*
372       MatCreate_FFTW - Creates a matrix object that provides FFT
373   via the external package FFTW
374 
375   Options Database Keys:
376 + -mat_fftw_plannerflags - set FFTW planner flags
377 
378    Level: intermediate
379 
380 */
381 PetscErrorCode MatCreate_FFTW(Mat A)
382 {
383   PetscErrorCode ierr;
384   MPI_Comm       comm=((PetscObject)A)->comm;
385   Mat_FFT        *fft=(Mat_FFT*)A->data;
386   Mat_FFTW       *fftw;
387   PetscInt       n=fft->n,N=fft->N,ndim=fft->ndim,*dim = fft->dim;
388   const char     *p_flags[]={"FFTW_ESTIMATE","FFTW_MEASURE","FFTW_PATIENT","FFTW_EXHAUSTIVE"};
389   PetscBool      flg;
390   PetscInt       p_flag,partial_dim=1,ctr;
391   PetscMPIInt    size;
392 
393   PetscFunctionBegin;
394 #if !defined(PETSC_USE_COMPLEX)
395   SETERRQ(comm,PETSC_ERR_SUP,"not support for real numbers");
396 #endif
397 
398   ierr = MPI_Comm_size(comm, &size);CHKERRQ(ierr);
399   for(ctr=1;ctr<ndim;ctr++){
400           partial_dim*=dim[ctr];
401       }
402   if (size == 1) {
403     ierr = MatSetSizes(A,N,N,N,N);CHKERRQ(ierr);
404     n = N;
405   } else {
406     ptrdiff_t alloc_local,local_n0,local_0_start,local_n1,local_1_end;
407     switch (ndim){
408     case 1:
409       alloc_local = fftw_mpi_local_size_1d(dim[0],comm,FFTW_FORWARD,FFTW_ESTIMATE,&local_n0,&local_0_start,&local_n1,&local_1_end);
410       n = (PetscInt)local_n0;
411       ierr = MatSetSizes(A,n,n,N,N);CHKERRQ(ierr);
412 
413       break;
414     case 2:
415       alloc_local = fftw_mpi_local_size_2d(dim[0],dim[1],comm,&local_n0,&local_0_start);
416       /*
417        PetscMPIInt    rank;
418        PetscSynchronizedPrintf(comm,"[%d] MatCreateSeqFFTW: local_n0, local_0_start %d %d, N %d,dim %d, %d\n",rank,(PetscInt)local_n0*dim[1],(PetscInt)local_0_start,m,dim[0],dim[1]);
419        PetscSynchronizedFlush(comm);
420        */
421       n = (PetscInt)local_n0*dim[1];
422       ierr = MatSetSizes(A,n,n,N,N);CHKERRQ(ierr);
423       break;
424     case 3:
425       alloc_local = fftw_mpi_local_size_3d(dim[0],dim[1],dim[2],comm,&local_n0,&local_0_start);
426       n = (PetscInt)local_n0*dim[1]*dim[2];
427       ierr = MatSetSizes(A,n,n,N,N);CHKERRQ(ierr);
428       break;
429     default:
430       alloc_local = fftw_mpi_local_size(ndim,(const ptrdiff_t*)dim,comm,&local_n0,&local_0_start);
431       n = (PetscInt)local_n0*partial_dim;
432       ierr = MatSetSizes(A,n,n,N,N);CHKERRQ(ierr);
433       break;
434     }
435   }
436   ierr = PetscObjectChangeTypeName((PetscObject)A,MATFFTW);CHKERRQ(ierr);
437 
438   ierr = PetscNewLog(A,Mat_FFTW,&fftw);CHKERRQ(ierr);
439   fft->data = (void*)fftw;
440 
441   fft->n           = n;
442   fftw->p_forward  = 0;
443   fftw->p_backward = 0;
444   fftw->p_flag     = FFTW_ESTIMATE;
445 
446   if (size == 1){
447     A->ops->mult          = MatMult_SeqFFTW;
448     A->ops->multtranspose = MatMultTranspose_SeqFFTW;
449   } else {
450     A->ops->mult          = MatMult_MPIFFTW;
451     A->ops->multtranspose = MatMultTranspose_MPIFFTW;
452   }
453   fft->matdestroy          = MatDestroy_FFTW;
454   A->ops->getvecs       = MatGetVecs_FFTW;
455   A->assembled          = PETSC_TRUE;
456 
457   /* get runtime options */
458   ierr = PetscOptionsBegin(((PetscObject)A)->comm,((PetscObject)A)->prefix,"FFTW Options","Mat");CHKERRQ(ierr);
459     ierr = PetscOptionsEList("-mat_fftw_plannerflags","Planner Flags","None",p_flags,4,p_flags[0],&p_flag,&flg);CHKERRQ(ierr);
460     if (flg) {fftw->p_flag = (unsigned)p_flag;}
461   PetscOptionsEnd();
462   PetscFunctionReturn(0);
463 }
464 EXTERN_C_END
465