void ConstantDamping::rhs(const double t, const double dt_lin, const solution * SolIn, solution * SolOut,doubVec *linOpFluct) { // Exponential growth for (int i=0; i<Dimxy(); ++i) { *( SolOut->pLin(i) ) = *( SolIn->pLin(i) )*dampFac/2; } for (int i=0; i<num_MFs(); ++i) { *( SolOut->pMF(i) ) = *( SolIn->pMF(i) )*dampFac/2; } // linear part is zero for (int i=0; i<Dimxy(); ++i) { linOpFluct[i].setConstant(dampFac/2); } }
void ConstantDamping::linearOPs_Init(double t0, doubVec *linOpFluct, doubVec *linOpMF){ for (int i=0; i<Dimxy(); ++i) { linOpFluct[i].setConstant(dampFac/2); } for (int i=0; i<num_MFs(); ++i) { linOpMF[i].setConstant(dampFac/2); } }
void ConstantDamping::linearOPs_Init(double t0, doubVec **linOpFluct, doubVec *linOpMF){ // Fluctuations for (int i=0; i<Dimxy(); ++i) { for (int j=0; j<num_Lin(); ++j) { linOpFluct[i][j].setZero(); } } // Mean fields for (int i=0; i<num_MFs(); ++i) { linOpMF[i].setZero(); } }
////////////////////////////////////////////////////////// // Remapping // Remapping for the shearing box // Using the Lithwick method of continuously remapping wavenumbers when they become too large void ConstantDamping::ShearingBox_Remap(double qt, solution *sol){ int nx = N(0)-1; double kxt; for (int i=0; i<Dimxy(); ++i) { kxt = K->kx[i].imag() + qt*K->ky[i].imag(); // If kx has grown too large if (kxt > nx/2*K->kxLfac) { // Put kx back in correct range K->kx[i] = K->kx[i] - dcmplx(0,nx*K->kxLfac); // zero out solution for (int Vn=0; Vn<num_Lin(); ++Vn) { (sol->pLin(i, Vn))->setZero(); } } } }
// Modified const of SolIn so that it can be divergence cleaned! void ConstantDamping::rhs(const double t, const double dt_lin, solution * SolIn, solution * SolOut,doubVec **linOpFluct) { for (int i=0; i<Dimxy(); ++i) { // Full Loop containing all the main work equation /////////////////////////////////////// ///// MAIN EQUATIONS assign_laplacians_(i, t, 1); for (int Vn=0; Vn<num_Lin(); ++Vn) { *( SolOut->pLin(i,Vn) ) = -(*( SolIn->pLin(i,Vn) ))*dampFac; } //////////////////////////////////////// ////// LINEAR PART // Need to re-evaluate laplacian, since different time. kxtmp_=kxtmp_ + q_*dt_lin*kytmp_; linOpFluct[i][0].setZero(); linOpFluct[i][1].setZero(); linOpFluct[i][2].setZero(); linOpFluct[i][3].setZero(); // linOpFluct[i][0] = nu_*((-kxtmp_*kxtmp_-kytmp_*kytmp_)+ K->kz2); // linOpFluct[i][1] = linOpFluct[i][0]; // linOpFluct[i][2] = (eta_/nu_)*linOpFluct[i][0];// Saves some calculation // linOpFluct[i][3] = linOpFluct[i][2]; //////////////////////////////////////// } for (int i=0; i<num_MFs(); ++i) { (*( SolOut->pMF(i) )).setZero();; } }
////////////////////////////////////////////////////////// // DRIVING NOISE void ConstantDamping::DrivingNoise(double t, double dt, solution *sol) { // NZ() is now dealiased NZ!!! // So - drive all of NZ and Nyquist frequency is not included // Adds noise onto linear part of solution for (int i=0; i<Dimxy(); ++i) { if (K->ky_index[i] != 0){ // ky=0 dealt with seperately. kx=ky=0 missed automatically /////////////////////////// // Noise multipliers assign_laplacians_(i,t,0); // Assign laplacian's - no inverses // ky=0 so not many if statements double noise_multfac = dt*totalN2_*mult_noise_fac_; // f_noise is included in normal distribution lapFtmp_ = noise_multfac*lap2tmp_/lapFtmp_; // lapFtmp_ is no longer lapF! lap2tmp_ = (-noise_multfac*lap2tmp_).sqrt(); lapFtmp_ = lapFtmp_.sqrt(); double *multU_pnt = lapFtmp_.data(); double *multZeta_pnt = lap2tmp_.data(); ///////////////////////////// // ky != 0 modes are completely random // U dcmplx* ePoint = (*(sol->pLin(i,0) )).data(); for (int jj=0; jj<NZ(); ++jj) { ePoint[jj] += multU_pnt[jj]*dcmplx(ndist_(mt_), ndist_(mt_)); } // zeta ePoint = (*(sol->pLin(i,1) )).data(); for (int jj=0; jj<NZ(); ++jj) { ePoint[jj] += multZeta_pnt[jj]*dcmplx(ndist_(mt_), ndist_(mt_)); } // b ePoint = (*(sol->pLin(i,2) )).data(); for (int jj=0; jj<NZ(); ++jj) { ePoint[jj] += multU_pnt[jj]*dcmplx(ndist_(mt_), ndist_(mt_)); } // eta ePoint = (*(sol->pLin(i,3) )).data(); for (int jj=0; jj<NZ(); ++jj) { ePoint[jj] += multZeta_pnt[jj]*dcmplx(ndist_(mt_), ndist_(mt_)); } } // Don't drive kx=ky=0 modes (mean fields) or ky=kz=0 modes (non-invertible).These are both taken care of in laplacian // Still need to make sure that ky=0 kx!=0 kz!=0 modes are symmetric. In particular ky=0, kx=a, kz=b must be the complex conjugate to kx=-a, kz=-b // To do this, have to communicate between processors! } if (!dont_drive_ky0_modes_){ // Deal with ky=0 part - a real PIA!! // Iterators for vectors K->i_tosend = K->match_kx_tosend.begin(); K->i_loc = K->match_kx_local.begin(); K->i_from = K->match_kx_from.begin(); K->i_fp = K->match_kx_from_p.begin(); K->i_sp = K->match_kx_tosend_p.begin(); int nz = NZ()-1; // For noise #ifdef USE_MPI_FLAG ////////////////////////////////////////////////// // Sending - create noise here and broadcast while (K->i_sp < K->match_kx_tosend_p.end()) { int i = *(K->i_tosend); // Index in k array if (K->ky_index[i]!=0) // Be paranoid mpi_.print1("Warning: Something wrong in noise data swapping!!"); /////////////////////////// // Noise multipliers assign_laplacians_(i,t,0); // Assign laplacian's - no inverses double noise_multfac = dt*totalN2_*mult_noise_fac_; // f_noise is included in normal distribution lap2tmp_(0) = 0.0; // Don't drive ky=kz=0 lapFtmp_ = noise_multfac*lap2tmp_/lapFtmp_; // lapFtmp_ is no longer lapF! lap2tmp_ = (-noise_multfac*lap2tmp_).sqrt(); lapFtmp_ = lapFtmp_.sqrt(); double *multU_pnt = lapFtmp_.data(); double *multZeta_pnt = lap2tmp_.data(); ///////////////////////////// // Make some noise - add to current k value dcmplx* noise_buff_dat_ = noise_buff_.data(); dcmplx* ePoint = (*(sol->pLin(i,0) )).data(); for (int jj=1; jj<NZ(); ++jj){ noise_buff_dat_[jj-1]=multU_pnt[jj]*dcmplx(ndist_(mt_), ndist_(mt_)); ePoint[jj] += noise_buff_dat_[jj-1]; } // zeta noise_buff_dat_ = noise_buff_.data()+nz; ePoint = (*(sol->pLin(i,1) )).data(); for (int jj=1; jj<NZ(); ++jj){ noise_buff_dat_[jj-1]=multZeta_pnt[jj]*dcmplx(ndist_(mt_), ndist_(mt_)); ePoint[jj] += noise_buff_dat_[jj-1]; } // b noise_buff_dat_ = noise_buff_.data()+2*nz; ePoint = (*(sol->pLin(i,2) )).data(); for (int jj=1; jj<NZ(); ++jj){ noise_buff_dat_[jj-1]=multU_pnt[jj]*dcmplx(ndist_(mt_), ndist_(mt_)); ePoint[jj] += noise_buff_dat_[jj-1]; } // eta noise_buff_dat_ = noise_buff_.data()+3*nz; ePoint = (*(sol->pLin(i,3) )).data(); for (int jj=1; jj<NZ(); ++jj){ noise_buff_dat_[jj-1]=multZeta_pnt[jj]*dcmplx(ndist_(mt_), ndist_(mt_)); ePoint[jj] += noise_buff_dat_[jj-1]; } // Send data mpi_.Send_dcmplx(noise_buff_.data(), num_Lin()*nz, *(K->i_sp), *(K->i_tosend)); // cout << "(kx,ky)=(" << K->kx[i] <<"," << K->ky[i] << ")" << endl; // cout << noise_buff_.segment(0, nz).transpose() << endl<<endl; // Update iterator ++(K->i_tosend); ++(K->i_sp); } //////////////////////////////////////////// // Recieving while (K->i_fp < K->match_kx_from_p.end()) { int i = *(K->i_loc); // Receive data from matching call mpi_.Recv_dcmplx(noise_buff_.data(), num_Lin()*nz, *(K->i_fp), *(K->i_from)); // Flip noise around for (int nV=0; nV<num_Lin(); ++nV) { noise_buff_.segment(nV*nz, nz).reverseInPlace(); } // cout << "(kx,ky)=(" << K->kx[i] <<"," << K->ky[i] << ")" << endl; // cout << noise_buff_.segment(0, nz).transpose().conjugate() <<endl<<endl; // Add to solution for (int nV=0; nV<num_Lin(); ++nV) { (*(sol->pLin(i,nV) )).segment(1,nz) += noise_buff_.segment(nV*nz, nz).conjugate(); } // Update iterators ++(K->i_fp); ++(K->i_loc); ++(K->i_from); } // mpi_.Barrier(); // This is probably not necessary but don't see why it would cause harm //////////////////////////////////////////////////////////////////// #else // Need a separate "no-mpi" case, since MPI_Send functions not defined //////////////////////////////////////////////////////////////////// while (K->i_tosend < K->match_kx_tosend.end()) { /////////////////////////// // Noise multipliers int i = *(K->i_tosend); // index assign_laplacians_(i,t,0); // Assign laplacian's - no inverses double noise_multfac = dt*totalN2_*mult_noise_fac_; // f_noise is included in normal distribution lap2tmp_(0) = 0.0; // Don't drive ky=kz=0 lap2tmp_(NZ()/2)=0.0; // Or Nyquist lapFtmp_ = noise_multfac*lap2tmp_/lapFtmp_; // lapFtmp_ is no longer lapF! lap2tmp_ = (-noise_multfac*lap2tmp_).sqrt(); lapFtmp_ = lapFtmp_.sqrt(); double *multU_pnt = lapFtmp_.data(); double *multZeta_pnt = lap2tmp_.data(); ///////////////////////////// // Make some noise - add to current k value dcmplx* noise_buff_dat_ = noise_buff_.data(); dcmplx* ePoint = (*(sol->pLin(i,0) )).data(); for (int jj=1; jj<NZ(); ++jj){ noise_buff_dat_[jj-1]=multU_pnt[jj]*dcmplx(ndist_(mt_), ndist_(mt_)); ePoint[jj] += noise_buff_dat_[jj-1]; } // zeta noise_buff_dat_ = noise_buff_.data()+nz; ePoint = (*(sol->pLin(i,1) )).data(); for (int jj=1; jj<NZ(); ++jj){ noise_buff_dat_[jj-1]=multZeta_pnt[jj]*dcmplx(ndist_(mt_), ndist_(mt_)); ePoint[jj] += noise_buff_dat_[jj-1]; } // b noise_buff_dat_ = noise_buff_.data()+2*nz; ePoint = (*(sol->pLin(i,2) )).data(); for (int jj=1; jj<NZ(); ++jj){ noise_buff_dat_[jj-1]=multU_pnt[jj]*dcmplx(ndist_(mt_), ndist_(mt_)); ePoint[jj] += noise_buff_dat_[jj-1]; } // eta noise_buff_dat_ = noise_buff_.data()+3*nz; ePoint = (*(sol->pLin(i,3) )).data(); for (int jj=1; jj<NZ(); ++jj){ noise_buff_dat_[jj-1]=multZeta_pnt[jj]*dcmplx(ndist_(mt_), ndist_(mt_)); ePoint[jj] += noise_buff_dat_[jj-1]; } // Find matching K value and use the same noise i = *(K->i_loc); // Flip noise around for (int nV=0; nV<num_Lin(); ++nV) { noise_buff_.segment(nV*nz, nz).reverseInPlace(); } // Add to solution for (int nV=0; nV<num_Lin(); ++nV) { (*(sol->pLin(i,nV) )).segment(1,nz) += noise_buff_.segment(nV*nz, nz).conjugate(); } // Update iterators ++(K->i_tosend); ++(K->i_loc); } #endif //////////////////////////////////////////////////////////////////// } }
////////////////////////////////////////////////////////// // ENERGY/MOMENTUM ETC. void ConstantDamping::Calc_Energy_AM_Diss(TimeVariables* tv, double t, const solution *sol) { // Energy, angular momentum and dissipation of the solution MFin and Cin // TimeVariables class stores info and data about energy, AM etc. // t is time // OUTPUT: energy[1] and [2] contain U and B mean field energies (energy[1]=0 for this) // energy[3] and [4] contain u and b fluctuating energies. // Energy double energy_u=0, energy_b=0; // Angular momentum double AM_u = 0, AM_b = 0; // Dissipation double diss_u=0,diss_b=0; // MPI buffers - this method passes around some unecessary data but will be very minimal const int num_to_mpi = 6; int mult_fac;// Factor to account for only doing half fft sum. ///////////////////////////////////// // ALL OF THIS IS PARALLELIZED for (int i=0; i<Dimxy(); ++i){ // Form Laplacians using time-dependent kx assign_laplacians_(i, 0*t, 1); mult_fac = 2; if (kytmp_== 0.0 ) mult_fac = 1; // Only count ky=0 mode once lap2tmp_ = lapFtmp_*ilap2tmp_; // lap2tmp_ just a convenient storage if (tv->energy_save_Q()){ ////////////////////////////////////// // ENERGY // Use Qkl_tmp_ for Mkl to save memory lap2tmp_ = lapFtmp_*ilap2tmp_; // lap2tmp_ just a convenient storage // (NB: ilap2tmp_ is negative but want abs, just use -ilap2) energy_u += mult_fac*( lap2tmp_*(( *(sol->pLin(i,0)) ).abs2()) - ilap2tmp_*(( *(sol->pLin(i,1)) ).abs2()) ).sum(); energy_b += mult_fac*( lap2tmp_*(( *(sol->pLin(i,2)) ).abs2()) - ilap2tmp_*(( *(sol->pLin(i,3)) ).abs2()) ).sum(); ////////////////////////////////////// } if (tv->AngMom_save_Q()){ ////////////////////////////////////// // Angular momentum uy_ = ( (-kyctmp_*kxctmp_)*(*sol->pLin(i, 0)) + K->kz*(*sol->pLin(i, 1)) )*ilap2tmp_; by_ = ( (-kyctmp_*kxctmp_)*(*sol->pLin(i, 2)) + K->kz*(*sol->pLin(i, 3)) )*ilap2tmp_; AM_u += ( (*sol->pLin(i, 0))*uy_.conjugate() ).real().sum(); AM_b += ( (*sol->pLin(i, 2))*by_.conjugate() ).real().sum(); // ////////////////////////////////////// } if (tv->dissip_save_Q()){ ////////////////////////////////////// // DISSIPATION // ////////////////////////////////////// } } double mpi_send_buff[num_to_mpi] = {energy_u,energy_b,AM_u,AM_b,diss_u,diss_b}; double mpi_receive_buff[num_to_mpi]; // Put the everything on processor 0 mpi_.SumReduce_doub(mpi_send_buff,mpi_receive_buff,num_to_mpi); // mpi_.SumReduce_IP_doub(&energy_u_f,1); // Is this working? // Currently, TimeVariables is set to save on root process, may want to generalize this at some point (SumReduce_doub is also) if (mpi_.my_n_v() == 0) { //////////////////////////////////////////// ///// All this is only on processor 0 ///// double divfac=1.0/totalN2_; energy_u = mpi_receive_buff[0]*divfac; energy_b = mpi_receive_buff[1]*divfac; AM_u = mpi_receive_buff[2]*divfac; AM_b = mpi_receive_buff[3]*divfac; diss_u = mpi_receive_buff[4]*divfac; diss_b = mpi_receive_buff[5]*divfac; /////////////////////////////////////// /// MEAN FIELDS ////// // Only need to calculate on one processor double energy_MU=0, energy_MB=0; double AM_MU =0, AM_MB=0; double diss_MU =0, diss_MB =0; energy_MB = ( (*(sol->pMF(0))).abs2().sum() )/nz2_; AM_MB = 0; AM_MU = 0; diss_MB = 0; diss_MU = 0; /////////////////////////////////////// ////// OUTPUT ////// // Energy double* en_point = tv->current_energy(); en_point[0] = energy_MU/2; en_point[1] = energy_MB/2; en_point[2] = energy_u/2; en_point[3] = energy_b/2; // Angular momentum double* AM_point = tv->current_AM(); AM_point[0] = AM_MU; AM_point[1] = AM_MB; AM_point[2] = AM_u; AM_point[3] = AM_b; // Energy double* diss_point = tv->current_diss(); diss_point[0] = diss_MU; diss_point[1] = diss_MB; diss_point[2] = diss_u; diss_point[3] = diss_b; ///// All this is only on processor 0 ///// //////////////////////////////////////////// } // Save the data tv->Save_Data(t); }