/*
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
#include
#include "common.h"
#include "gfft.h"
#include "debug.h"
#include "forcing.h"
#include "particles.h"
#include "ltide.h"
#ifdef COMPRESSIBLE
/***************************************************************/
/**
Compute the right hand side of the COMPRESSIBLE dynamical equation
@param dfldo: (output) right hand side of the dynamical equation
@param fldi: (input) current status of the flow
@param t: current time of the simulation
@param tremap: current remap time (only when shear is on)
@param dt: current timestep size
*/
/***************************************************************/
void timestep( struct Field dfldo,
struct Field fldi,
const double t,
const double tremap,
const double dt) {
// NB: in the compressible version, fldi.vx,vy, vz are actually the linear momenta in x,y,z!
int i;
double S;
double complex qc;
#ifdef WITH_SHEAR
#ifdef TIME_DEPENDANT_SHEAR
S = param.shear * cos(param.omega_shear * t); // This is the real shear: -dvy/dx
#else
S = param.shear;
#endif
#endif
/******************************************
** Momentum Advection ****************
*******************************************/
#ifdef _OPENMP
#pragma omp parallel for private(i) schedule(static)
#endif
for( i = 0 ; i < NTOTAL_COMPLEX ; i++) {
w1[i] = fldi.vx[i];
w2[i] = fldi.vy[i];
w3[i] = fldi.vz[i];
w4[i] = fldi.d[i];
}
gfft_c2r_t(w1);
gfft_c2r_t(w2);
gfft_c2r_t(w3);
gfft_c2r_t(w4);
// Compute density
check_positivity(wr4);
for( i = 0 ; i < 2*NTOTAL_COMPLEX ; i++) {
wr4[i] = 1.0 / wr4[i];
}
/////////////////////////////////////////////////////////////
/*
#ifdef _OPENMP
#pragma omp parallel for private(i,q0) schedule(static)
#endif
for( i = 0 ; i < 2*NTOTAL_COMPLEX ; i++) {
wr4[i] = ((double) NTOTAL) exp( wr4[i]/((double) NTOTAL) );
}
*/
////////////////////////////////////////////////////////////
// Here, we compute the Reynolds stress tensor p_ip_j/rho=v_iv_jrho
#ifdef _OPENMP
#pragma omp parallel for private(i) schedule(static)
#endif
for( i = 0 ; i < 2*NTOTAL_COMPLEX ; i++) {
wr5[i] = wr1[i] * wr1[i] * wr4[i] / ((double) NTOTAL);
wr6[i] = wr2[i] * wr2[i] * wr4[i] / ((double) NTOTAL);
wr7[i] = wr3[i] * wr3[i] * wr4[i] / ((double) NTOTAL);
wr8[i] = wr1[i] * wr2[i] * wr4[i] / ((double) NTOTAL);
wr9[i] = wr1[i] * wr3[i] * wr4[i] / ((double) NTOTAL);
wr10[i] = wr2[i] * wr3[i] * wr4[i] / ((double) NTOTAL);
}
gfft_r2c_t(wr5);
gfft_r2c_t(wr6);
gfft_r2c_t(wr7);
gfft_r2c_t(wr8);
gfft_r2c_t(wr9);
gfft_r2c_t(wr10);
#ifdef _OPENMP
#pragma omp parallel for private(i) schedule(static)
#endif
for( i = 0 ; i < NTOTAL_COMPLEX ; i++) {
dfldo.vx[i] = - I * mask[i] * (
kxt[i] * w5[i] + ky[i] * w8[i] + kz[i] * w9[i] );
dfldo.vy[i] = - I * mask[i] * (
kxt[i] * w8[i] + ky[i] * w6[i] + kz[i] * w10[i] );
dfldo.vz[i] = - I * mask[i] * (
kxt[i] * w9[i] + ky[i] * w10[i] + kz[i] * w7[i] );
}
/*********************************************
** Other terms will need the velocity field **
** Computation of the velocity field **
**********************************************/
#ifdef _OPENMP
#pragma omp parallel for private(i) schedule(static)
#endif
for( i = 0 ; i < 2*NTOTAL_COMPLEX ; i++) {
wr1[i] = wr1[i] * wr4[i];
wr2[i] = wr2[i] * wr4[i];
wr3[i] = wr3[i] * wr4[i];
}
// wr1-wr3 is the real velocity field
/*********************************************
** Viscous term ******************************
**********************************************/
// Fourier transform the velocity field
for( i = 0 ; i < 2*NTOTAL_COMPLEX ; i++) {
wr4[i] = wr1[i];
wr5[i] = wr2[i];
wr6[i] = wr3[i];
}
gfft_r2c_t(wr4);
gfft_r2c_t(wr5);
gfft_r2c_t(wr6);
// Add the viscous term to the linear momentum
#ifdef _OPENMP
#pragma omp parallel for private(i,qc) schedule(static)
#endif
for( i = 0 ; i < NTOTAL_COMPLEX ; i++) {
qc = 1.0 / 3.0 * (kxt[i] * w4[i] + ky[i] * w5[i] + kz[i] * w6[i]);
dfldo.vx[i] -= nu * mask[i] * (k2t[i] * w4[i] + kxt[i] * qc);
dfldo.vy[i] -= nu * mask[i] * (k2t[i] * w5[i] + ky[i] * qc);
dfldo.vz[i] -= nu * mask[i] * (k2t[i] * w6[i] + kz[i] * qc);
}
/*********************************************
**** MHD Terms (if needed) *****************
*********************************************/
#ifdef MHD
// We first build up the velocity field from the momentum
// do the induction equation
#ifdef _OPENMP
#pragma omp parallel for private(i) schedule(static)
#endif
for( i = 0 ; i < NTOTAL_COMPLEX ; i++) {
w4[i] = fldi.bx[i];
w5[i] = fldi.by[i];
w6[i] = fldi.bz[i];
}
// These fields should have no divergence.
// When shear is on, however, divergence is conserved up to the timeintegrator precision.
// Let's clean it.
projector(w4,w5,w6);
gfft_c2r_t(w4);
gfft_c2r_t(w5);
gfft_c2r_t(w6);
// (vx,vy,vz) is in w1-w3 and (bx,by,bz) is in (w4-w6). It is now time to compute the emfs in w7-w9...
#ifdef _OPENMP
#pragma omp parallel for private(i) schedule(static)
#endif
for( i = 0 ; i < 2*NTOTAL_COMPLEX ; i++) {
wr7[i] = (wr2[i] * wr6[i] - wr3[i] * wr5[i]) / ((double) NTOTAL);
wr8[i] = (wr3[i] * wr4[i] - wr1[i] * wr6[i]) / ((double) NTOTAL);
wr9[i] = (wr1[i] * wr5[i] - wr2[i] * wr4[i]) / ((double) NTOTAL);
}
// Compute the curl of the emf to add in the induction equation.
gfft_r2c_t(wr7);
gfft_r2c_t(wr8);
gfft_r2c_t(wr9);
#ifdef _OPENMP
#pragma omp parallel for private(i) schedule(static)
#endif
for( i = 0 ; i < NTOTAL_COMPLEX ; i++) {
dfldo.bx[i] = I * mask[i] * (ky[i] * w9[i] - kz[i] * w8[i]);
dfldo.by[i] = I * mask[i] * (kz[i] * w7[i] - kxt[i]* w9[i]);
dfldo.bz[i] = I * mask[i] * (kxt[i]* w8[i] - ky[i] * w7[i]);
}
// Let's do the Lorentz Force
// We already have (bx,by,bz) in w4-w6. No need to compute them again...
#ifdef _OPENMP
#pragma omp parallel for private(i) schedule(static)
#endif
for( i = 0 ; i < 2*NTOTAL_COMPLEX ; i++) {
wr1[i] = wr4[i] * wr4[i] / ((double) NTOTAL*NTOTAL);
wr2[i] = wr5[i] * wr5[i] / ((double) NTOTAL*NTOTAL);
wr3[i] = wr6[i] * wr6[i] / ((double) NTOTAL*NTOTAL);
wr7[i] = wr4[i] * wr5[i] / ((double) NTOTAL*NTOTAL);
wr8[i] = wr4[i] * wr6[i] / ((double) NTOTAL*NTOTAL);
wr9[i] = wr5[i] * wr6[i] / ((double) NTOTAL*NTOTAL);
}
gfft_r2c_t(wr1);
gfft_r2c_t(wr2);
gfft_r2c_t(wr3);
gfft_r2c_t(wr7);
gfft_r2c_t(wr8);
gfft_r2c_t(wr9);
// The 0.5 factor is here to take into account the magnetic pressure term -B^2/2 delta_ij
#ifdef _OPENMP
#pragma omp parallel for private(i,qc) schedule(static)
#endif
for( i = 0 ; i < NTOTAL_COMPLEX ; i++) {
qc = 0.5 * (w1[i] + w2[i] + w3[i]);
dfldo.vx[i] += I * mask[i] * (kxt[i] * (w1[i]-qc) + ky[i] * w7[i] + kz[i] * w8[i]);
dfldo.vy[i] += I * mask[i] * (kxt[i] * w7[i] + ky[i] * (w2[i]-qc) + kz[i] * w9[i]);
dfldo.vz[i] += I * mask[i] * (kxt[i] * w8[i] + ky[i] * w9[i] + kz[i] * (w3[i]-qc));
}
#endif
/*********************************************
**** Continuity equation *****************
*********************************************/
#ifdef _OPENMP
#pragma omp parallel for private(i) schedule(static)
#endif
for( i = 0 ; i < NTOTAL_COMPLEX ; i++) {
dfldo.d[i] = -I * mask[i] * (kxt[i] * fldi.vx[i] + ky[i] * fldi.vy[i] + kz[i] * fldi.vz[i]);
}
/************************************
** SOURCE TERMS ********************
************************************/
#ifdef _OPENMP
#pragma omp parallel for private(i) schedule(static)
#endif
for( i = 0 ; i < NTOTAL_COMPLEX ; i++) {
#ifdef WITH_ROTATION
dfldo.vx[i] += 2.0 * param.omega * fldi.vy[i];
dfldo.vy[i] -= 2.0 * param.omega * fldi.vx[i];
#endif
#ifdef WITH_SHEAR
dfldo.vy[i] += S * fldi.vx[i];
#ifdef MHD
dfldo.by[i] -= S * fldi.bx[i];
#endif
#endif
}
/**************************************
** Thermal Pressure *******************
***************************************/
// NB: magnetic pressure was already included in the lorentz force calculation
#ifdef _OPENMP
#pragma omp parallel for private(i) schedule(static)
#endif
for( i = 0 ; i < NTOTAL_COMPLEX ; i++) {
dfldo.vx[i] += -I * mask[i] * kxt[i] * param.cs * param.cs * fldi.d[i];
dfldo.vy[i] += -I * mask[i] * ky[i] * param.cs * param.cs * fldi.d[i];
dfldo.vz[i] += -I * mask[i] * kz[i] * param.cs * param.cs * fldi.d[i];
}
// Finished
return;
}
/***************************************************************/
/**
Implicit steps of the integrator (essentially linear diffusion terms)
This is an implicit model: fldi is modified by this routine
@param fldi: (input and output) current status of the flow
@param t: current time of the simulation
@param dt: current timestep size
*/
/***************************************************************/
void implicitstep(
struct Field fldi,
const double t,
const double dt ) {
double q0,lambda;
int exponent;
int i,j;
#ifdef _OPENMP
#pragma omp parallel for private(i,q0) schedule(static)
#endif
for( i = 0 ; i < NTOTAL_COMPLEX ; i++) {
#ifdef MHD
q0 = exp( - eta * dt* k2t[i] );
fldi.bx[i] = fldi.bx[i] * q0;
fldi.by[i] = fldi.by[i] * q0;
fldi.bz[i] = fldi.bz[i] * q0;
#endif
}
// Hyperviscosity (diffusive time=grid-scale sound crossing time at the cutoff scale)
/*
lambda=3;
exponent=4;
#ifdef _OPENMP
#pragma omp parallel for private(i,q0) schedule(static)
#endif
for( i = 0 ; i < NTOTAL_COMPLEX ; i++) {
q0 = exp( - dt* kmax*param.cs*pow(lambda*lambda*k2t[i]/(kmax*kmax),exponent) );
q0 = 1.0;
fldi.d[i] = fldi.d[i] * q0;
q0=1.0;
fldi.vx[i] = fldi.vx[i] * q0;
fldi.vy[i] = fldi.vy[i] * q0;
fldi.vz[i] = fldi.vz[i] * q0;
#ifdef MHD
fldi.bx[i] = fldi.bx[i] * q0;
fldi.by[i] = fldi.by[i] * q0;
fldi.bz[i] = fldi.bz[i] * q0;
#endif
}
*/
#ifdef FORCING
forcing(fldi, dt);
#endif
#ifdef WITH_PARTICLES
particle_implicit_step( fldi, t, dt);
#endif
return;
}
#endif