/*
This file is part of the Snoopy code.
Snoopy code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Snoopy code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Snoopy code. If not, see .
*/
#include "snoopy.h"
#include
#include "common.h"
#include "error.h"
#include "gfft.h"
#include "debug.h"
/**************************************************/
/* This code contains several routines to enforce symmetries
in Fourier space. Since some of these symmetries can be seen
as boundary conditions in real space, boundary_c (found in boundary.c)
calls several of these routines.
/**************************************************/
/*****************************************/
/** Symmetrize the complex space according
** to the symetries of the real tranform
** @param wi Field to be symmetrized
*/
/*******************************************/
void
symmetrize_complex (double complex wi[])
{
// Symmetrize an array
int i, j, k, index;
int idx2d, idx2dconj;
double complex *zplane;
double complex q0;
DEBUG_START_FUNC;
#ifdef MPI_SUPPORT
// Allocate zplane
if (rank == 0)
{
// 2 cases: if zplane fits in one of the temporary array, we're fine, and we can use this array
// Otherwise, we have to allocate an array for this specific task
if (NZ_COMPLEX >= NPROC)
zplane = w2;
else
{
zplane =
(double complex *) fftw_malloc (sizeof (double complex) *
NX_COMPLEX * NY_COMPLEX);
if (zplane == NULL)
ERROR_HANDLER (ERROR_CRITICAL, "No memory for zplane allocation");
}
}
#endif
#ifndef WITH_2D
// put kz=0 plane in w1
k = 0;
index = 0;
for (i = 0; i < NX_COMPLEX / NPROC; i++)
{
for (j = 0; j < NY_COMPLEX; j++)
{
w1[index] = wi[IDX3D];
index++;
}
}
#else
// put ky=0 line in w1
j = 0;
k = 0;
index = 0;
for (i = 0; i < NX_COMPLEX / NPROC; i++)
{
w1[index] = wi[IDX3D];
index++;
}
#endif
#ifdef MPI_SUPPORT
// construct the full kz=0 plane in zplane on rank=0 process
MPI_Gather (w1, 2 * NX_COMPLEX * NY_COMPLEX / NPROC, MPI_DOUBLE,
zplane, 2 * NX_COMPLEX * NY_COMPLEX / NPROC, MPI_DOUBLE,
0, MPI_COMM_WORLD);
#else
zplane = w1;
#endif
#ifndef WITH_2D
// Now let's do the nasty symmetrization bit
if (rank == 0)
{
// kz=0, ky!=0
for (i = 0; i < NX_COMPLEX; i++)
{
for (j = 1; j < NY_COMPLEX / 2; j++)
{
idx2d = j + NY_COMPLEX * i;
if (i > 0)
idx2dconj = (NY_COMPLEX - j) + NY_COMPLEX * (NX_COMPLEX - i); // where the complex conjugate should be
else
idx2dconj = (NY_COMPLEX - j); // special case when i=0
q0 = 0.5 * (zplane[idx2d] + conj (zplane[idx2dconj]));
zplane[idx2d] = q0;
zplane[idx2dconj] = conj (q0);
}
}
// kz=0, ky=0
for (i = 1; i < NX_COMPLEX / 2; i++)
{
idx2d = NY_COMPLEX * i; // That's actually IDX3D
idx2dconj = NY_COMPLEX * (NX_COMPLEX - i); // where the complex conjugate should be
q0 = 0.5 * (zplane[idx2d] + conj (zplane[idx2dconj]));
zplane[idx2d] = q0;
zplane[idx2dconj] = conj (q0);
}
}
#else
if (rank == 0)
{
// ky=0
for (i = 1; i < NX_COMPLEX / 2; i++)
{
idx2d = NY_COMPLEX * i; // That's actually IDX3D
idx2dconj = NY_COMPLEX * (NX_COMPLEX - i); // where the complex conjugate should be
q0 = 0.5 * (zplane[idx2d] + conj (zplane[idx2dconj]));
zplane[idx2d] = q0;
zplane[idx2dconj] = conj (q0);
}
}
#endif
// Wait until the symmetrization is finished.
#ifdef MPI_SUPPORT
MPI_Barrier (MPI_COMM_WORLD);
//Send it back
MPI_Scatter (zplane, 2 * NX_COMPLEX * NY_COMPLEX / NPROC, MPI_DOUBLE,
w1, 2 * NX_COMPLEX * NY_COMPLEX / NPROC, MPI_DOUBLE,
0, MPI_COMM_WORLD);
#endif
// No need to translate that back when no MPI is available since zplane=w1
// Put it back in the array
#ifndef WITH_2D
k = 0;
index = 0;
for (i = 0; i < NX_COMPLEX / NPROC; i++)
{
for (j = 0; j < NY_COMPLEX; j++)
{
wi[IDX3D] = w1[index];
index++;
}
}
#else
j = 0;
k = 0;
index = 0;
for (i = 0; i < NX_COMPLEX / NPROC; i++)
{
wi[IDX3D] = w1[index];
index++;
}
#endif
// that's all folks...
#ifdef MPI_SUPPORT
if (rank == 0)
{
if (NZ_COMPLEX < NPROC)
fftw_free (zplane);
}
#endif
DEBUG_END_FUNC;
return;
}
/*****************************************
/** Enforce Symmetries of field fld
** useful if numerical noise produces spurious
** growth of mean velocity field due to
** some linear source terms.
** Might be useful if N^2<0 or kappa^2<0
** @param fld field needed to be symmetrized
*/
/******************************************/
void
enforce_complex_symm (struct Field fldi)
{
DEBUG_START_FUNC;
// Enforce symmetries of complex plane
symmetrize_complex (fldi.vx);
symmetrize_complex (fldi.vy);
symmetrize_complex (fldi.vz);
#ifdef BOUSSINESQ
symmetrize_complex (fldi.th);
#endif
#ifdef MHD
symmetrize_complex (fldi.bx);
symmetrize_complex (fldi.by);
symmetrize_complex (fldi.bz);
#endif
// Remove mean field (noise is generated by the FFTs performed by nonlinear terms)
if (rank == 0)
{
fldi.vx[0] = 0.0;
fldi.vy[0] = 0.0;
fldi.vz[0] = 0.0;
#ifdef BOUSSINESQ
fldi.th[0] = 0.0;
#endif
}
DEBUG_END_FUNC;
return;
}
/*****************************************/
/** Symmetrize the complex space assuming
** wi is a sine in the z direction
/*******************************************/
void
symm_sin_z (double complex wi[])
{
int i, j, k, itarget, jtarget;
double complex q0, q1;
#ifdef MPI_SUPPORT
double complex *zplane;
int index;
DEBUG_START_FUNC;
if (rank == 0)
{
zplane =
(double complex *) fftw_malloc (sizeof (double complex) * NX_COMPLEX *
NY_COMPLEX);
if (zplane == NULL)
ERROR_HANDLER (ERROR_CRITICAL, "No memory for zplane allocation");
}
// Loop on all kzs
for (k = 0; k < NZ_COMPLEX; k++)
{
//copy the k plane into w1
index = 0;
for (i = 0; i < NX_COMPLEX / NPROC; i++)
{
for (j = 0; j < NY_COMPLEX; j++)
{
w1[index] = wi[IDX3D];
index++;
}
}
// construct the full kz=k plane in zplane on rank=0 process
MPI_Gather (w1, 2 * NX_COMPLEX * NY_COMPLEX / NPROC, MPI_DOUBLE,
zplane, 2 * NX_COMPLEX * NY_COMPLEX / NPROC, MPI_DOUBLE,
0, MPI_COMM_WORLD);
// loop in rank=0 to symmetrize the thing...
if (rank == 0)
{
for (i = 0; i <= NX_COMPLEX / 2; i++)
{
if (i != 0)
itarget = NX_COMPLEX - i;
else
itarget = i;
for (j = 0; j < NY_COMPLEX; j++)
{
if (j != 0)
jtarget = NY_COMPLEX - j;
else
jtarget = j;
q0 = zplane[j + i * NY_COMPLEX];
q1 = conj (zplane[jtarget + itarget * NY_COMPLEX]);
q0 = 0.5 * (q0 - q1);
zplane[j + i * NY_COMPLEX] = q0;
zplane[jtarget + itarget * NY_COMPLEX] = -conj (q0);
}
}
}
// everyone wait here
MPI_Barrier (MPI_COMM_WORLD);
//Send it back
MPI_Scatter (zplane, 2 * NX_COMPLEX * NY_COMPLEX / NPROC, MPI_DOUBLE,
w1, 2 * NX_COMPLEX * NY_COMPLEX / NPROC, MPI_DOUBLE,
0, MPI_COMM_WORLD);
// put it back
index = 0;
for (i = 0; i < NX_COMPLEX / NPROC; i++)
{
for (j = 0; j < NY_COMPLEX; j++)
{
wi[IDX3D] = w1[index];
index++;
}
}
// end of k-loop
}
if (rank == 0)
{
free (zplane);
}
#else
for (i = 0; i <= NX_COMPLEX / 2; i++)
{
if (i != 0)
itarget = NX_COMPLEX - i;
else
itarget = i;
//MPI_Printf("i=%d, itarget=%d\n",i,itarget);
for (j = 0; j < NY_COMPLEX; j++)
{
if (j != 0)
jtarget = NY_COMPLEX - j;
else
jtarget = j;
for (k = 0; k < NZ_COMPLEX; k++)
{
//MPI_Printf("kx1=%g, kx2=%g, ky1=%g, ky2=%g, kz1=%g, kz2=%g\n",kx[ k + j * NZ_COMPLEX + i * NZ_COMPLEX * NY_COMPLEX],kx[ k + j * NZ_COMPLEX + itarget * NZ_COMPLEX * NY_COMPLEX],ky[ k + j * NZ_COMPLEX + i * NZ_COMPLEX * NY_COMPLEX],ky[ k + j * NZ_COMPLEX + itarget * NZ_COMPLEX * NY_COMPLEX],kz[ k + j * NZ_COMPLEX + i * NZ_COMPLEX * NY_COMPLEX],kz[ k + j * NZ_COMPLEX + itarget * NZ_COMPLEX * NY_COMPLEX]);
q0 = wi[k + j * NZ_COMPLEX + i * NZ_COMPLEX * NY_COMPLEX];
q1 =
conj (wi
[k + jtarget * NZ_COMPLEX +
itarget * NZ_COMPLEX * NY_COMPLEX]);
q0 = 0.5 * (q0 - q1);
wi[k + j * NZ_COMPLEX + i * NZ_COMPLEX * NY_COMPLEX] = q0;
wi[k + jtarget * NZ_COMPLEX +
itarget * NZ_COMPLEX * NY_COMPLEX] = -conj (q0);
}
}
}
#endif
DEBUG_END_FUNC;
return;
}
/*****************************************/
/** Symmetrize the complex space assuming
** wi is a cosine in the z direction
/*******************************************/
void
symm_cos_z (double complex wi[])
{
int i, j, k, itarget, jtarget;
double complex q0, q1;
#ifdef MPI_SUPPORT
double complex *zplane;
int index;
DEBUG_START_FUNC;
if (rank == 0)
{
zplane =
(double complex *) fftw_malloc (sizeof (double complex) * NX_COMPLEX *
NY_COMPLEX);
if (zplane == NULL)
ERROR_HANDLER (ERROR_CRITICAL, "No memory for zplane allocation");
}
// Loop on all kzs
for (k = 0; k < NZ_COMPLEX; k++)
{
//copy the k plane into w1
index = 0;
for (i = 0; i < NX_COMPLEX / NPROC; i++)
{
for (j = 0; j < NY_COMPLEX; j++)
{
w1[index] = wi[IDX3D];
index++;
}
}
// construct the full kz=k plane in zplane on rank=0 process
MPI_Gather (w1, 2 * NX_COMPLEX * NY_COMPLEX / NPROC, MPI_DOUBLE,
zplane, 2 * NX_COMPLEX * NY_COMPLEX / NPROC, MPI_DOUBLE,
0, MPI_COMM_WORLD);
// loop in rank=0 to symmetrize the thing...
if (rank == 0)
{
for (i = 0; i <= NX_COMPLEX / 2; i++)
{
if (i != 0)
itarget = NX_COMPLEX - i;
else
itarget = i;
for (j = 0; j < NY_COMPLEX; j++)
{
if (j != 0)
jtarget = NY_COMPLEX - j;
else
jtarget = j;
q0 = zplane[j + i * NY_COMPLEX];
q1 = conj (zplane[jtarget + itarget * NY_COMPLEX]);
q0 = 0.5 * (q0 + q1);
zplane[j + i * NY_COMPLEX] = q0;
zplane[jtarget + itarget * NY_COMPLEX] = conj (q0);
}
}
}
// everyone wait here
MPI_Barrier (MPI_COMM_WORLD);
//Send it back
MPI_Scatter (zplane, 2 * NX_COMPLEX * NY_COMPLEX / NPROC, MPI_DOUBLE,
w1, 2 * NX_COMPLEX * NY_COMPLEX / NPROC, MPI_DOUBLE,
0, MPI_COMM_WORLD);
// put it back
index = 0;
for (i = 0; i < NX_COMPLEX / NPROC; i++)
{
for (j = 0; j < NY_COMPLEX; j++)
{
wi[IDX3D] = w1[index];
index++;
}
}
// end of k-loop
}
if (rank == 0)
{
free (zplane);
}
#else
for (i = 0; i <= NX_COMPLEX / 2; i++)
{
if (i != 0)
itarget = NX_COMPLEX - i;
else
itarget = i;
//MPI_Printf("i=%d, itarget=%d\n",i,itarget);
for (j = 0; j < NY_COMPLEX; j++)
{
if (j != 0)
jtarget = NY_COMPLEX - j;
else
jtarget = j;
for (k = 0; k < NZ_COMPLEX; k++)
{
//MPI_Printf("kx1=%g, kx2=%g, ky1=%g, ky2=%g, kz1=%g, kz2=%g\n",kx[ k + j * NZ_COMPLEX + i * NZ_COMPLEX * NY_COMPLEX],kx[ k + j * NZ_COMPLEX + itarget * NZ_COMPLEX * NY_COMPLEX],ky[ k + j * NZ_COMPLEX + i * NZ_COMPLEX * NY_COMPLEX],ky[ k + j * NZ_COMPLEX + itarget * NZ_COMPLEX * NY_COMPLEX],kz[ k + j * NZ_COMPLEX + i * NZ_COMPLEX * NY_COMPLEX],kz[ k + j * NZ_COMPLEX + itarget * NZ_COMPLEX * NY_COMPLEX]);
q0 = wi[k + j * NZ_COMPLEX + i * NZ_COMPLEX * NY_COMPLEX];
q1 =
conj (wi
[k + jtarget * NZ_COMPLEX +
itarget * NZ_COMPLEX * NY_COMPLEX]);
q0 = 0.5 * (q0 + q1);
wi[k + j * NZ_COMPLEX + i * NZ_COMPLEX * NY_COMPLEX] = q0;
wi[k + jtarget * NZ_COMPLEX +
itarget * NZ_COMPLEX * NY_COMPLEX] = conj (q0);
}
}
}
#endif
DEBUG_END_FUNC;
return;
}
/*****************************************/
/** Symmetrize the complex space assuming
** wi is a cosine in the x direction
/*******************************************/
void
symm_cos_x (double complex wi[])
{
int i, j, k, itarget;
double complex q0, q1;
DEBUG_START_FUNC;
#ifdef MPI_SUPPORT
transpose_complex_XY (wi, wi);
for (j = 0; j < NY_COMPLEX / NPROC; j++)
{
for (i = 0; i < NX_COMPLEX / 2; i++)
{
if (i != 0)
itarget = NX_COMPLEX - i;
else
itarget = i;
for (k = 0; k < NZ_COMPLEX; k++)
{
q0 = wi[k + i * NZ_COMPLEX + j * NZ_COMPLEX * NX_COMPLEX];
q1 = wi[k + itarget * NZ_COMPLEX + j * NZ_COMPLEX * NX_COMPLEX];
q0 = 0.5 * (q0 + q1);
wi[k + i * NZ_COMPLEX + j * NZ_COMPLEX * NX_COMPLEX] = q0;
wi[k + itarget * NZ_COMPLEX + j * NZ_COMPLEX * NX_COMPLEX] = q0;
}
}
}
transpose_complex_YX (wi, wi);
#else
for (i = 0; i < NX_COMPLEX / 2; i++)
{
if (i != 0)
itarget = NX_COMPLEX - i;
else
itarget = i;
//MPI_Printf("i=%d, itarget=%d\n",i,itarget);
for (j = 0; j < NY_COMPLEX; j++)
{
for (k = 0; k < NZ_COMPLEX; k++)
{
//MPI_Printf("kx1=%g, kx2=%g, ky1=%g, ky2=%g, kz1=%g, kz2=%g\n",kx[ k + j * NZ_COMPLEX + i * NZ_COMPLEX * NY_COMPLEX],kx[ k + j * NZ_COMPLEX + itarget * NZ_COMPLEX * NY_COMPLEX],ky[ k + j * NZ_COMPLEX + i * NZ_COMPLEX * NY_COMPLEX],ky[ k + j * NZ_COMPLEX + itarget * NZ_COMPLEX * NY_COMPLEX],kz[ k + j * NZ_COMPLEX + i * NZ_COMPLEX * NY_COMPLEX],kz[ k + j * NZ_COMPLEX + itarget * NZ_COMPLEX * NY_COMPLEX]);
q0 = wi[k + j * NZ_COMPLEX + i * NZ_COMPLEX * NY_COMPLEX];
q1 = wi[k + j * NZ_COMPLEX + itarget * NZ_COMPLEX * NY_COMPLEX];
q0 = 0.5 * (q0 + q1);
wi[k + j * NZ_COMPLEX + i * NZ_COMPLEX * NY_COMPLEX] = q0;
wi[k + j * NZ_COMPLEX + itarget * NZ_COMPLEX * NY_COMPLEX] = q0;
}
}
}
#endif
DEBUG_END_FUNC;
return;
}
/*****************************************/
/** Symmetrize the complex space assuming
** wi is a cosine in the x direction
/*******************************************/
void
symm_sin_x (double complex wi[])
{
int i, j, k, itarget;
double complex q0, q1;
DEBUG_START_FUNC;
#ifdef MPI_SUPPORT
transpose_complex_XY (wi, wi);
for (j = 0; j < NY_COMPLEX / NPROC; j++)
{
for (i = 0; i < NX_COMPLEX / 2; i++)
{
if (i != 0)
itarget = NX_COMPLEX - i;
else
itarget = i;
for (k = 0; k < NZ_COMPLEX; k++)
{
q0 = wi[k + i * NZ_COMPLEX + j * NZ_COMPLEX * NX_COMPLEX];
q1 = wi[k + itarget * NZ_COMPLEX + j * NZ_COMPLEX * NX_COMPLEX];
q0 = 0.5 * (q0 + q1);
wi[k + i * NZ_COMPLEX + j * NZ_COMPLEX * NX_COMPLEX] = q0;
wi[k + itarget * NZ_COMPLEX + j * NZ_COMPLEX * NX_COMPLEX] = q0;
}
}
}
transpose_complex_YX (wi, wi);
#else
for (i = 0; i < NX_COMPLEX / 2; i++)
{
if (i != 0)
itarget = NX_COMPLEX - i;
else
itarget = i;
//MPI_Printf("i=%d, itarget=%d\n",i,itarget);
for (j = 0; j < NY_COMPLEX; j++)
{
for (k = 0; k < NZ_COMPLEX; k++)
{
//MPI_Printf("kx1=%g, kx2=%g, ky1=%g, ky2=%g, kz1=%g, kz2=%g\n",kx[ k + j * NZ_COMPLEX + i * NZ_COMPLEX * NY_COMPLEX],kx[ k + j * NZ_COMPLEX + itarget * NZ_COMPLEX * NY_COMPLEX],ky[ k + j * NZ_COMPLEX + i * NZ_COMPLEX * NY_COMPLEX],ky[ k + j * NZ_COMPLEX + itarget * NZ_COMPLEX * NY_COMPLEX],kz[ k + j * NZ_COMPLEX + i * NZ_COMPLEX * NY_COMPLEX],kz[ k + j * NZ_COMPLEX + itarget * NZ_COMPLEX * NY_COMPLEX]);
q0 = wi[k + j * NZ_COMPLEX + i * NZ_COMPLEX * NY_COMPLEX];
q1 = wi[k + j * NZ_COMPLEX + itarget * NZ_COMPLEX * NY_COMPLEX];
q0 = 0.5 * (q0 - q1);
wi[k + j * NZ_COMPLEX + i * NZ_COMPLEX * NY_COMPLEX] = q0;
wi[k + j * NZ_COMPLEX + itarget * NZ_COMPLEX * NY_COMPLEX] =
-q0;
}
}
}
#endif
DEBUG_END_FUNC;
return;
}
/*****************************************/
/** Symmetrize the complex space assuming
** we have walls in the radial direction
** is equivalent to a plane Couette flow (but
** no spectral accuracy...)
*/
/*******************************************/
void
symmetrize_walls_x (struct Field fldi)
{
DEBUG_START_FUNC;
symm_sin_x (fldi.vx);
symm_cos_x (fldi.vy);
symm_cos_x (fldi.vz);
#ifdef MHD
symm_cos_x (fldi.bx);
symm_sin_x (fldi.by);
symm_sin_x (fldi.bz);
#endif
#ifdef BOUSSINESQ
symm_sin_x (fldi.th);
#endif
enforce_complex_symm (fldi);
DEBUG_END_FUNC;
return;
}
/*****************************************/
/** Symmetrize the complex space assuming
** we have walls in the vertical direction.
*/
/*******************************************/
void
symmetrize_walls_z (struct Field fldi)
{
DEBUG_START_FUNC;
symm_cos_z (fldi.vx);
symm_cos_z (fldi.vy);
symm_sin_z (fldi.vz);
#ifdef MHD
symm_sin_z (fldi.bx);
symm_sin_z (fldi.by);
symm_cos_z (fldi.bz);
#endif
#ifdef BOUSSINESQ
symm_sin_z (fldi.th);
#endif
enforce_complex_symm (fldi);
DEBUG_END_FUNC;
return;
}