Nstrophy/src/navier-stokes.c

1381 lines
41 KiB
C
Raw Normal View History

2023-05-10 19:33:29 -04:00
/*
Copyright 2017-2023 Ian Jauslin
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
2023-04-26 11:27:03 -04:00
#include "constants.cpp"
2022-05-27 16:09:17 -04:00
#include "io.h"
2023-04-25 16:38:25 -04:00
#include "navier-stokes.h"
2023-06-13 18:45:19 -04:00
#include "complex_tools.h"
2018-01-11 22:48:14 +00:00
#include <math.h>
#include <stdint.h>
2022-05-18 09:57:06 +02:00
#include <stdlib.h>
2023-04-12 19:05:01 -04:00
#include <string.h>
2018-01-11 22:48:14 +00:00
2022-05-18 09:57:06 +02:00
// compute solution as a function of time
int uk(
int K1,
int K2,
int N1,
int N2,
double final_time,
2022-05-18 09:57:06 +02:00
double nu,
double delta,
2022-05-25 11:12:02 -04:00
double L,
2023-05-15 20:29:06 -04:00
double adaptive_tolerance,
double adaptive_factor,
2023-06-13 18:45:19 -04:00
double max_delta,
2023-06-13 23:56:35 -04:00
unsigned int adaptive_norm,
2022-05-26 15:05:30 -04:00
_Complex double* u0,
_Complex double* g,
2023-04-05 20:33:38 -04:00
bool irreversible,
2023-04-26 11:27:03 -04:00
unsigned int algorithm,
double print_freq,
2023-05-15 20:29:06 -04:00
double starting_time,
2022-05-27 16:09:17 -04:00
unsigned int nthreads,
FILE* savefile
2022-05-18 09:57:06 +02:00
){
_Complex double* u;
_Complex double* tmp1;
_Complex double* tmp2;
_Complex double* tmp3;
2023-05-15 20:29:06 -04:00
_Complex double* tmp4;
_Complex double* tmp5;
_Complex double* tmp6;
_Complex double* tmp7;
double time;
2022-05-18 09:57:06 +02:00
fft_vect fft1;
fft_vect fft2;
fft_vect ifft;
int kx,ky;
2023-05-15 20:29:06 -04:00
ns_init_tmps(&u, &tmp1, &tmp2, &tmp3, &tmp4, &tmp5, &tmp6, &tmp7, &fft1, &fft2, &ifft, K1, K2, N1, N2, nthreads, algorithm);
2022-05-26 15:05:30 -04:00
// copy initial condition
copy_u(u, u0, K1, K2);
2022-05-18 09:57:06 +02:00
2022-05-19 17:51:45 +02:00
// print column headers
printf("# 1:i 2:t ");
unsigned int i=3;
2022-05-19 17:51:45 +02:00
for(kx=-K1;kx<=K1;kx++){
for (ky=-K2;ky<=K2;ky++){
printf(" %6u:(%4d,%4d)r ",i,kx,ky);
i++;
printf(" %6u:(%4d,%4d)i ",i,kx,ky);
i++;
2022-05-19 17:51:45 +02:00
}
}
// period
// add 0.1 to ensure proper rounding
uint64_t n=(uint64_t)((starting_time-fmod(starting_time, print_freq))/print_freq+0.1);
// store step (useful for adaptive step methods
double step=delta;
double next_step=step;
2022-05-18 09:57:06 +02:00
// iterate
time=starting_time;
while(final_time<0 || time<final_time){
2023-04-26 11:27:03 -04:00
if(algorithm==ALGORITHM_RK2){
ns_step_rk2(u, K1, K2, N1, N2, nu, step, L, g, fft1, fft2, ifft, tmp1, tmp2, irreversible);
2023-05-15 20:29:06 -04:00
} else if (algorithm==ALGORITHM_RK4) {
ns_step_rk4(u, K1, K2, N1, N2, nu, step, L, g, fft1, fft2, ifft, tmp1, tmp2, tmp3, irreversible);
2023-05-15 20:29:06 -04:00
} else if (algorithm==ALGORITHM_RKF45) {
ns_step_rkf45(u, adaptive_tolerance, adaptive_factor, max_delta, adaptive_norm, K1, K2, N1, N2, nu, &step, &next_step, L, g, fft1, fft2, ifft, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, irreversible, true);
2023-05-17 17:41:00 -04:00
} else if (algorithm==ALGORITHM_RKDP54) {
// only compute k1 on the first step
2023-06-13 23:56:35 -04:00
ns_step_rkdp54(u, adaptive_tolerance, adaptive_factor, max_delta, adaptive_norm, K1, K2, N1, N2, nu, &step, &next_step, L, g, fft1, fft2, ifft, &tmp1, &tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, irreversible, time==starting_time);
2023-05-17 16:59:15 -04:00
} else if (algorithm==ALGORITHM_RKBS32) {
// only compute k1 on the first step
ns_step_rkbs32(u, adaptive_tolerance, adaptive_factor, max_delta, adaptive_norm, K1, K2, N1, N2, nu, &step, &next_step, L, g, fft1, fft2, ifft, &tmp1, tmp2, tmp3, &tmp4, tmp5, irreversible, time==starting_time);
2023-05-15 20:29:06 -04:00
} else {
fprintf(stderr,"bug: unknown algorithm: %u, contact ian.jauslin@rutgers,edu\n",algorithm);
2023-04-26 11:27:03 -04:00
}
2023-05-15 20:29:06 -04:00
time+=step;
step=next_step;
2022-05-18 09:57:06 +02:00
if(time>(n+1)*print_freq){
n++;
fprintf(stderr,"% .8e ",time);
printf("% .15e ",time);
2022-05-18 09:57:06 +02:00
for(kx=-K1;kx<=K1;kx++){
for (ky=-K2;ky<=K2;ky++){
if (kx*kx+ky*ky<=1){
2023-04-11 18:45:45 -04:00
fprintf(stderr,"% .8e % .8e ",__real__ getval_sym(u,kx,ky,K2), __imag__ getval_sym(u, kx,ky,K2));
2022-05-18 09:57:06 +02:00
}
2023-04-11 18:45:45 -04:00
printf("% .15e % .15e ",__real__ getval_sym(u, kx,ky,K2), __imag__ getval_sym(u, kx,ky,K2));
2022-05-18 09:57:06 +02:00
}
}
fprintf(stderr,"\n");
printf("\n");
}
}
2023-06-14 14:19:27 -04:00
// TODO: update handling of savefile
2022-05-27 16:09:17 -04:00
// save final entry to savefile
write_vec_bin(u, K1, K2, savefile);
2022-05-27 16:09:17 -04:00
2023-05-15 20:29:06 -04:00
ns_free_tmps(u, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, fft1, fft2, ifft, algorithm);
2022-05-18 09:57:06 +02:00
return(0);
}
// compute enstrophy, alpha as a function of time
int enstrophy(
2022-05-18 09:57:06 +02:00
int K1,
int K2,
int N1,
int N2,
double final_time,
2022-05-18 09:57:06 +02:00
double nu,
double delta,
2022-05-25 11:12:02 -04:00
double L,
2023-05-15 20:29:06 -04:00
double adaptive_tolerance,
double adaptive_factor,
2023-06-13 18:45:19 -04:00
double max_delta,
2023-06-13 23:56:35 -04:00
unsigned int adaptive_norm,
2022-05-26 15:05:30 -04:00
_Complex double* u0,
_Complex double* g,
2023-04-05 20:33:38 -04:00
bool irreversible,
2023-04-26 11:27:03 -04:00
unsigned int algorithm,
double print_freq,
2023-05-15 20:29:06 -04:00
double starting_time,
2022-05-27 16:09:17 -04:00
unsigned int nthreads,
2023-04-12 19:05:01 -04:00
FILE* savefile,
2023-06-14 14:19:27 -04:00
FILE* utfile,
2023-04-12 19:05:01 -04:00
// for interrupt recovery
char* cmd_string,
char* params_string,
2023-06-14 14:28:03 -04:00
char* savefile_string,
char* utfile_string
2022-05-18 09:57:06 +02:00
){
_Complex double* u;
_Complex double* tmp1;
_Complex double* tmp2;
_Complex double* tmp3;
2023-05-15 20:29:06 -04:00
_Complex double* tmp4;
_Complex double* tmp5;
_Complex double* tmp6;
_Complex double* tmp7;
double time;
2023-05-08 15:04:00 -04:00
double alpha, enstrophy;
double prevtime;
2023-05-08 15:04:00 -04:00
double avg_a,avg_en,avg_en_x_a;
2023-04-12 14:37:02 -04:00
// index
2022-05-18 09:57:06 +02:00
fft_vect fft1;
fft_vect fft2;
fft_vect ifft;
2023-05-15 20:29:06 -04:00
ns_init_tmps(&u, &tmp1, &tmp2, &tmp3, &tmp4, &tmp5, &tmp6, &tmp7, &fft1, &fft2, &ifft, K1, K2, N1, N2, nthreads, algorithm);
2022-05-26 15:05:30 -04:00
// copy initial condition
copy_u(u, u0, K1, K2);
2022-05-18 09:57:06 +02:00
// store step (useful for adaptive step methods
double step=delta;
double next_step=step;
2022-05-18 09:57:06 +02:00
// init running average
avg_a=0;
avg_en=0;
2023-05-08 15:04:00 -04:00
avg_en_x_a=0;
prevtime=starting_time;
2022-05-18 09:57:06 +02:00
// period
// add 0.1 to ensure proper rounding
uint64_t n=(uint64_t)((starting_time-fmod(starting_time, print_freq))/print_freq+0.1);
2023-04-12 14:37:02 -04:00
2022-05-18 09:57:06 +02:00
// iterate
time=starting_time;
while(final_time<0 || time<final_time){
2023-04-26 11:27:03 -04:00
if(algorithm==ALGORITHM_RK2){
ns_step_rk2(u, K1, K2, N1, N2, nu, step, L, g, fft1, fft2, ifft, tmp1, tmp2, irreversible);
2023-05-15 20:29:06 -04:00
} else if (algorithm==ALGORITHM_RK4) {
ns_step_rk4(u, K1, K2, N1, N2, nu, step, L, g, fft1, fft2, ifft, tmp1, tmp2, tmp3, irreversible);
2023-05-15 20:29:06 -04:00
} else if (algorithm==ALGORITHM_RKF45) {
ns_step_rkf45(u, adaptive_tolerance, adaptive_factor, max_delta, adaptive_norm, K1, K2, N1, N2, nu, &step, &next_step, L, g, fft1, fft2, ifft, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, irreversible, true);
2023-05-17 17:41:00 -04:00
} else if (algorithm==ALGORITHM_RKDP54) {
// only compute k1 on the first step
2023-06-13 23:56:35 -04:00
ns_step_rkdp54(u, adaptive_tolerance, adaptive_factor, max_delta, adaptive_norm, K1, K2, N1, N2, nu, &step, &next_step, L, g, fft1, fft2, ifft, &tmp1, &tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, irreversible, time==starting_time);
2023-05-17 16:59:15 -04:00
} else if (algorithm==ALGORITHM_RKBS32) {
// only compute k1 on the first step
ns_step_rkbs32(u, adaptive_tolerance, adaptive_factor, max_delta, adaptive_norm, K1, K2, N1, N2, nu, &step, &next_step, L, g, fft1, fft2, ifft, &tmp1, tmp2, tmp3, &tmp4, tmp5, irreversible, time==starting_time);
2023-05-15 20:29:06 -04:00
} else {
fprintf(stderr,"bug: unknown algorithm: %u, contact ian.jauslin@rutgers,edu\n",algorithm);
2023-04-26 11:27:03 -04:00
}
2023-04-05 20:33:38 -04:00
time+=step;
2023-05-15 20:29:06 -04:00
alpha=compute_alpha(u, K1, K2, g, L);
enstrophy=compute_enstrophy(u, K1, K2, L);
2023-04-12 14:37:02 -04:00
// add to running averages (estimate the total duration of interval as print_freq, will be adjusted later)
avg_a+=alpha*(step/print_freq);
avg_en+=enstrophy*(step/print_freq);
avg_en_x_a+=enstrophy*alpha*(step/print_freq);
if(time>(n+1)*print_freq){
n++;
// adjust duration of interval to its actual value
avg_a*=print_freq/(time-prevtime);
avg_en*=print_freq/(time-prevtime);
avg_en_x_a*=print_freq/(time-prevtime);
2023-04-12 14:37:02 -04:00
2023-05-15 20:29:06 -04:00
// print to stderr so user can follow along
2023-05-17 16:47:15 -04:00
if(algorithm>ALGORITHM_ADAPTIVE_THRESHOLD){
fprintf(stderr,"% .8e % .8e % .8e % .8e % .8e % .8e % .8e % .8e\n",time, avg_a, avg_en, avg_en_x_a, alpha, enstrophy, alpha*enstrophy, step);
2023-05-17 18:01:46 -04:00
printf("% .15e % .15e % .15e % .15e % .15e % .15e % .15e % .15e\n",time, avg_a, avg_en, avg_en_x_a, alpha, enstrophy, alpha*enstrophy, step);
2023-05-15 20:29:06 -04:00
} else {
2023-05-16 00:07:38 -04:00
fprintf(stderr,"% .8e % .8e % .8e % .8e % .8e % .8e % .8e\n",time, avg_a, avg_en, avg_en_x_a, alpha, enstrophy, alpha*enstrophy);
2023-05-17 18:01:46 -04:00
printf("% .15e % .15e % .15e % .15e % .15e % .15e % .15e\n",time, avg_a, avg_en, avg_en_x_a, alpha, enstrophy, alpha*enstrophy);
2023-05-15 20:29:06 -04:00
}
// print to stdout
2022-05-18 09:57:06 +02:00
}
2023-04-12 15:23:35 -04:00
// reset averages
avg_a=0;
avg_en=0;
avg_en_x_a=0;
prevtime=time;
// get ready for next step
step=next_step;
2023-04-12 15:23:35 -04:00
// catch abort signal
if (g_abort){
// print u to stderr if no savefile
if (savefile==NULL){
savefile=stderr;
}
break;
}
2022-05-18 09:57:06 +02:00
}
if(savefile!=NULL){
fprintf(savefile,"# Continue computation with\n");
// command to resume
fprintf(savefile,"#! ");
fprintf(savefile, cmd_string);
// params
// allocate buffer for params
if(params_string!=NULL) {
char* params=calloc(sizeof(char), strlen(params_string)+1);
strcpy(params, params_string);
remove_entry(params, "starting_time");
remove_entry(params, "init");
2023-05-15 20:29:06 -04:00
if(algorithm>ALGORITHM_ADAPTIVE_THRESHOLD){
remove_entry(params, "delta");
}
fprintf(savefile," -p \"%s;init=file:%s", params, savefile_string);
free(params);
2023-05-15 20:29:06 -04:00
// write delta if adaptive, and not writing binary
if(algorithm>ALGORITHM_ADAPTIVE_THRESHOLD && (savefile==stderr || savefile==stdout)){
fprintf(savefile,";delta=%.15e", step);
2023-05-15 20:29:06 -04:00
}
// write starting_time if not writing binary
if(savefile==stderr || savefile==stdout){
fprintf(savefile,";starting_time=%.15e", time);
2023-05-15 20:29:06 -04:00
}
fprintf(savefile,"\"");
}
2023-06-14 14:28:03 -04:00
// utfile
if(utfile!=NULL){
fprintf(savefile," -u \"%s\"", utfile_string);
}
// threads
if(nthreads!=1){
fprintf(savefile," -t %d", nthreads);
}
fprintf(savefile," enstrophy\n");
// save final u to savefile
if(savefile==stderr || savefile==stdout){
write_vec(u, K1, K2, savefile);
} else {
write_vec_bin(u, K1, K2, savefile);
// last binary entry: starting time
fwrite(&time, sizeof(double), 1, savefile);
2023-05-15 20:29:06 -04:00
// extra binary data for adaptive algorithm
if(algorithm>ALGORITHM_ADAPTIVE_THRESHOLD){
fwrite(&step, sizeof(double), 1, savefile);
2023-05-15 20:29:06 -04:00
}
}
2023-04-14 15:01:52 -04:00
}
2023-04-12 15:23:35 -04:00
2023-06-14 14:19:27 -04:00
// save final u to utfile in txt format
if(utfile!=NULL){
write_vec(u, K1, K2, utfile);
}
2023-05-15 20:29:06 -04:00
ns_free_tmps(u, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, fft1, fft2, ifft, algorithm);
2022-05-18 09:57:06 +02:00
return(0);
}
2022-05-18 22:22:42 +02:00
// compute solution as a function of time, but do not print anything (useful for debugging)
int quiet(
int K1,
int K2,
int N1,
int N2,
double final_time,
2022-05-18 22:22:42 +02:00
double nu,
double delta,
2022-05-25 11:12:02 -04:00
double L,
2023-05-15 20:29:06 -04:00
double adaptive_tolerance,
double adaptive_factor,
2023-06-13 23:56:35 -04:00
double max_delta,
unsigned int adaptive_norm,
double starting_time,
2022-05-26 15:05:30 -04:00
_Complex double* u0,
_Complex double* g,
2023-04-05 20:33:38 -04:00
bool irreversible,
2023-04-26 11:27:03 -04:00
unsigned int algorithm,
2022-05-27 16:09:17 -04:00
unsigned int nthreads,
FILE* savefile
2022-05-18 22:22:42 +02:00
){
_Complex double* u;
_Complex double* tmp1;
_Complex double* tmp2;
_Complex double* tmp3;
2023-05-15 20:29:06 -04:00
_Complex double* tmp4;
_Complex double* tmp5;
_Complex double* tmp6;
_Complex double* tmp7;
double time;
2022-05-18 22:22:42 +02:00
fft_vect fft1;
fft_vect fft2;
fft_vect ifft;
2023-05-15 20:29:06 -04:00
ns_init_tmps(&u, &tmp1, &tmp2, &tmp3, &tmp4, &tmp5, &tmp6, &tmp7, &fft1, &fft2, &ifft, K1, K2, N1, N2, nthreads, algorithm);
2022-05-26 15:05:30 -04:00
// copy initial condition
copy_u(u, u0, K1, K2);
2022-05-18 22:22:42 +02:00
// store delta (useful for adaptive step algorithms)
double step=delta;
double next_step=step;
2022-05-18 22:22:42 +02:00
// iterate
time=starting_time;
while(final_time<0 || time<final_time){
2023-04-26 11:27:03 -04:00
if(algorithm==ALGORITHM_RK2){
ns_step_rk2(u, K1, K2, N1, N2, nu, step, L, g, fft1, fft2, ifft, tmp1, tmp2, irreversible);
2023-05-15 20:29:06 -04:00
} else if (algorithm==ALGORITHM_RK4) {
ns_step_rk4(u, K1, K2, N1, N2, nu, step, L, g, fft1, fft2, ifft, tmp1, tmp2, tmp3, irreversible);
2023-05-15 20:29:06 -04:00
} else if (algorithm==ALGORITHM_RKF45) {
ns_step_rkf45(u, adaptive_tolerance, adaptive_factor, max_delta, adaptive_norm, K1, K2, N1, N2, nu, &step, &next_step, L, g, fft1, fft2, ifft, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, irreversible, true);
2023-05-17 17:41:00 -04:00
} else if (algorithm==ALGORITHM_RKDP54) {
// only compute k1 on the first step
2023-06-13 23:56:35 -04:00
ns_step_rkdp54(u, adaptive_tolerance, adaptive_factor, max_delta, adaptive_norm, K1, K2, N1, N2, nu, &step, &next_step, L, g, fft1, fft2, ifft, &tmp1, &tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, irreversible, time==starting_time);
2023-05-17 16:59:15 -04:00
} else if (algorithm==ALGORITHM_RKBS32) {
// only compute k1 on the first step
ns_step_rkbs32(u, adaptive_tolerance, adaptive_factor, max_delta, adaptive_norm, K1, K2, N1, N2, nu, &step, &next_step, L, g, fft1, fft2, ifft, &tmp1, tmp2, tmp3, &tmp4, tmp5, irreversible, time==starting_time);
2023-05-15 20:29:06 -04:00
} else {
fprintf(stderr,"bug: unknown algorithm: %u, contact ian.jauslin@rutgers,edu\n",algorithm);
2023-04-26 11:27:03 -04:00
}
time+=step;
step=next_step;
2022-05-18 22:22:42 +02:00
}
2023-06-14 14:19:27 -04:00
// TODO: update handling of savefile
2022-05-27 16:09:17 -04:00
// save final entry to savefile
write_vec(u, K1, K2, savefile);
2022-05-27 16:09:17 -04:00
2023-05-15 20:29:06 -04:00
ns_free_tmps(u, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, fft1, fft2, ifft, algorithm);
2022-05-18 22:22:42 +02:00
return(0);
}
2022-05-18 09:57:06 +02:00
// initialize vectors for computation
2023-05-15 20:29:06 -04:00
int ns_init_tmps(
2022-05-18 09:57:06 +02:00
_Complex double ** u,
_Complex double ** tmp1,
_Complex double ** tmp2,
_Complex double ** tmp3,
2023-05-15 20:29:06 -04:00
_Complex double ** tmp4,
_Complex double ** tmp5,
_Complex double ** tmp6,
_Complex double ** tmp7,
2022-05-18 09:57:06 +02:00
fft_vect* fft1,
fft_vect* fft2,
fft_vect* ifft,
int K1,
int K2,
int N1,
2022-05-18 23:52:01 +02:00
int N2,
2023-05-15 20:29:06 -04:00
unsigned int nthreads,
unsigned int algorithm
2022-05-18 09:57:06 +02:00
){
// velocity field
2023-04-25 17:51:14 -04:00
*u=calloc(sizeof(_Complex double),K1*(2*K2+1)+K2);
2022-05-18 09:57:06 +02:00
// allocate tmp vectors for computation
2023-05-15 20:29:06 -04:00
if(algorithm==ALGORITHM_RK2){
*tmp1=calloc(sizeof(_Complex double),K1*(2*K2+1)+K2);
*tmp2=calloc(sizeof(_Complex double),K1*(2*K2+1)+K2);
} else if (algorithm==ALGORITHM_RK4){
*tmp1=calloc(sizeof(_Complex double),K1*(2*K2+1)+K2);
*tmp2=calloc(sizeof(_Complex double),K1*(2*K2+1)+K2);
*tmp3=calloc(sizeof(_Complex double),K1*(2*K2+1)+K2);
2023-05-17 17:41:00 -04:00
} else if (algorithm==ALGORITHM_RKF45 || algorithm==ALGORITHM_RKDP54){
2023-05-15 20:29:06 -04:00
*tmp1=calloc(sizeof(_Complex double),K1*(2*K2+1)+K2);
*tmp2=calloc(sizeof(_Complex double),K1*(2*K2+1)+K2);
*tmp3=calloc(sizeof(_Complex double),K1*(2*K2+1)+K2);
*tmp4=calloc(sizeof(_Complex double),K1*(2*K2+1)+K2);
*tmp5=calloc(sizeof(_Complex double),K1*(2*K2+1)+K2);
*tmp6=calloc(sizeof(_Complex double),K1*(2*K2+1)+K2);
*tmp7=calloc(sizeof(_Complex double),K1*(2*K2+1)+K2);
2023-05-17 16:59:15 -04:00
} else if (algorithm==ALGORITHM_RKBS32){
2023-05-17 16:47:15 -04:00
*tmp1=calloc(sizeof(_Complex double),K1*(2*K2+1)+K2);
*tmp2=calloc(sizeof(_Complex double),K1*(2*K2+1)+K2);
*tmp3=calloc(sizeof(_Complex double),K1*(2*K2+1)+K2);
*tmp4=calloc(sizeof(_Complex double),K1*(2*K2+1)+K2);
*tmp5=calloc(sizeof(_Complex double),K1*(2*K2+1)+K2);
2023-05-15 20:29:06 -04:00
} else {
fprintf(stderr,"bug: unknown algorithm: %u, contact ian.jauslin@rutgers,edu\n",algorithm);
};
2022-05-18 09:57:06 +02:00
2022-05-18 23:52:01 +02:00
// init threads
fftw_init_threads();
fftw_plan_with_nthreads(nthreads);
2022-05-18 09:57:06 +02:00
// prepare vectors for fft
fft1->fft=fftw_malloc(sizeof(fftw_complex)*N1*N2);
fft1->fft_plan=fftw_plan_dft_2d(N1,N2, fft1->fft, fft1->fft, FFTW_FORWARD, FFTW_MEASURE);
fft2->fft=fftw_malloc(sizeof(fftw_complex)*N1*N2);
fft2->fft_plan=fftw_plan_dft_2d(N1,N2, fft2->fft, fft2->fft, FFTW_FORWARD, FFTW_MEASURE);
ifft->fft=fftw_malloc(sizeof(fftw_complex)*N1*N2);
ifft->fft_plan=fftw_plan_dft_2d(N1,N2, ifft->fft, ifft->fft, FFTW_BACKWARD, FFTW_MEASURE);
return 0;
}
// release vectors
int ns_free_tmps(
_Complex double* u,
_Complex double* tmp1,
_Complex double* tmp2,
_Complex double* tmp3,
2023-05-15 20:29:06 -04:00
_Complex double* tmp4,
_Complex double* tmp5,
_Complex double* tmp6,
_Complex double* tmp7,
2022-05-18 09:57:06 +02:00
fft_vect fft1,
fft_vect fft2,
2023-05-15 20:29:06 -04:00
fft_vect ifft,
unsigned int algorithm
2022-05-18 09:57:06 +02:00
){
// free memory
fftw_destroy_plan(fft1.fft_plan);
fftw_destroy_plan(fft2.fft_plan);
fftw_destroy_plan(ifft.fft_plan);
fftw_free(fft1.fft);
fftw_free(fft2.fft);
fftw_free(ifft.fft);
2022-05-18 23:52:01 +02:00
fftw_cleanup_threads();
2022-05-18 09:57:06 +02:00
2023-05-15 20:29:06 -04:00
if(algorithm==ALGORITHM_RK2){
free(tmp1);
free(tmp2);
} else if (algorithm==ALGORITHM_RK4){
free(tmp1);
free(tmp2);
free(tmp3);
2023-05-17 17:41:00 -04:00
} else if (algorithm==ALGORITHM_RKF45 || algorithm==ALGORITHM_RKDP54){
2023-05-15 20:29:06 -04:00
free(tmp1);
free(tmp2);
free(tmp3);
free(tmp4);
free(tmp5);
free(tmp6);
free(tmp7);
2023-05-17 16:59:15 -04:00
} else if (algorithm==ALGORITHM_RKBS32){
2023-05-17 16:47:15 -04:00
free(tmp1);
free(tmp2);
free(tmp3);
free(tmp4);
free(tmp5);
2023-05-15 20:29:06 -04:00
} else {
fprintf(stderr,"bug: unknown algorithm: %u, contact ian.jauslin@rutgers,edu\n",algorithm);
};
2022-05-18 09:57:06 +02:00
free(u);
return 0;
}
2022-05-26 15:05:30 -04:00
// copy u0 to u
int copy_u(
2022-05-18 09:57:06 +02:00
_Complex double* u,
2022-05-26 15:05:30 -04:00
_Complex double* u0,
2022-05-18 09:57:06 +02:00
int K1,
int K2
){
2022-05-26 15:05:30 -04:00
int i;
2022-05-18 09:57:06 +02:00
2023-04-25 17:51:14 -04:00
for(i=0;i<K1*(2*K2+1)+K2;i++){
2022-05-26 15:05:30 -04:00
u[i]=u0[i];
2022-05-18 09:57:06 +02:00
}
return 0;
}
2023-04-05 20:33:38 -04:00
// next time step
2023-04-26 11:13:50 -04:00
// RK 4 algorithm
int ns_step_rk4(
2022-05-18 09:57:06 +02:00
_Complex double* u,
int K1,
int K2,
int N1,
int N2,
double nu,
double delta,
2022-05-25 11:12:02 -04:00
double L,
_Complex double* g,
2022-05-18 09:57:06 +02:00
fft_vect fft1,
fft_vect fft2,
fft_vect ifft,
_Complex double* tmp1,
_Complex double* tmp2,
2023-04-05 20:33:38 -04:00
_Complex double* tmp3,
bool irreversible
2022-05-18 09:57:06 +02:00
){
2018-01-11 22:48:14 +00:00
int kx,ky;
// k1
2023-04-05 20:33:38 -04:00
ns_rhs(tmp1, u, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
2018-01-11 22:48:14 +00:00
// add to output
2023-04-11 18:45:45 -04:00
for(kx=0;kx<=K1;kx++){
2023-04-25 17:51:14 -04:00
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
2023-04-11 18:45:45 -04:00
tmp3[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+delta/6*tmp1[klookup_sym(kx,ky,K2)];
2018-01-11 22:48:14 +00:00
}
}
// u+h*k1/2
2023-04-11 18:45:45 -04:00
for(kx=0;kx<=K1;kx++){
2023-04-25 17:51:14 -04:00
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
2023-04-11 18:45:45 -04:00
tmp2[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+delta/2*tmp1[klookup_sym(kx,ky,K2)];
2018-01-11 22:48:14 +00:00
}
}
// k2
2023-04-05 20:33:38 -04:00
ns_rhs(tmp1, tmp2, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
2018-01-11 22:48:14 +00:00
// add to output
2023-04-11 18:45:45 -04:00
for(kx=0;kx<=K1;kx++){
2023-04-25 17:51:14 -04:00
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
2023-04-11 18:45:45 -04:00
tmp3[klookup_sym(kx,ky,K2)]+=delta/3*tmp1[klookup_sym(kx,ky,K2)];
2018-01-11 22:48:14 +00:00
}
}
// u+h*k2/2
2023-04-11 18:45:45 -04:00
for(kx=0;kx<=K1;kx++){
2023-04-25 17:51:14 -04:00
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
2023-04-11 18:45:45 -04:00
tmp2[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+delta/2*tmp1[klookup_sym(kx,ky,K2)];
2018-01-11 22:48:14 +00:00
}
}
// k3
2023-04-05 20:33:38 -04:00
ns_rhs(tmp1, tmp2, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
2018-01-11 22:48:14 +00:00
// add to output
2023-04-11 18:45:45 -04:00
for(kx=0;kx<=K1;kx++){
2023-04-25 17:51:14 -04:00
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
2023-04-11 18:45:45 -04:00
tmp3[klookup_sym(kx,ky,K2)]+=delta/3*tmp1[klookup_sym(kx,ky,K2)];
2018-01-11 22:48:14 +00:00
}
}
// u+h*k3
2023-04-11 18:45:45 -04:00
for(kx=0;kx<=K1;kx++){
2023-04-25 17:51:14 -04:00
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
2023-04-11 18:45:45 -04:00
tmp2[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+delta*tmp1[klookup_sym(kx,ky,K2)];
2018-01-11 22:48:14 +00:00
}
}
// k4
2023-04-05 20:33:38 -04:00
ns_rhs(tmp1, tmp2, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
2018-01-11 22:48:14 +00:00
// add to output
2023-04-11 18:45:45 -04:00
for(kx=0;kx<=K1;kx++){
2023-04-25 17:51:14 -04:00
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
2023-04-11 18:45:45 -04:00
u[klookup_sym(kx,ky,K2)]=tmp3[klookup_sym(kx,ky,K2)]+delta/6*tmp1[klookup_sym(kx,ky,K2)];
2023-04-06 11:28:18 -04:00
}
}
2018-01-11 22:48:14 +00:00
return(0);
}
2023-04-26 11:13:50 -04:00
// RK 2 algorithm
int ns_step_rk2(
_Complex double* u,
int K1,
int K2,
int N1,
int N2,
double nu,
double delta,
double L,
_Complex double* g,
fft_vect fft1,
fft_vect fft2,
fft_vect ifft,
_Complex double* tmp1,
_Complex double* tmp2,
bool irreversible
){
int kx,ky;
// k1
ns_rhs(tmp1, u, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
// u+h*k1/2
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
tmp2[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+delta/2*tmp1[klookup_sym(kx,ky,K2)];
}
}
// k2
ns_rhs(tmp1, tmp2, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
// add to output
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
u[klookup_sym(kx,ky,K2)]+=delta*tmp1[klookup_sym(kx,ky,K2)];
}
}
return(0);
}
2023-05-15 20:29:06 -04:00
// next time step
// adaptive RK algorithm (Runge-Kutta-Fehlberg)
int ns_step_rkf45(
2023-05-15 20:29:06 -04:00
_Complex double* u,
double tolerance,
double factor,
double max_delta,
unsigned int adaptive_norm,
2023-05-15 20:29:06 -04:00
int K1,
int K2,
int N1,
int N2,
double nu,
double* delta,
double* next_delta,
2023-05-15 20:29:06 -04:00
double L,
_Complex double* g,
fft_vect fft1,
fft_vect fft2,
fft_vect ifft,
_Complex double* k1,
_Complex double* k2,
_Complex double* k3,
_Complex double* k4,
_Complex double* k5,
_Complex double* k6,
_Complex double* tmp,
bool irreversible,
// whether to compute k1 or leave it as is
bool compute_k1
2023-05-15 20:29:06 -04:00
){
int kx,ky;
double err,relative;
2023-05-15 20:29:06 -04:00
// k1: u(t)
if(compute_k1){
ns_rhs(k1, u, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
}
2023-05-15 20:29:06 -04:00
// k2 : u(t+1/4*delta)
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
tmp[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+(*delta)/4*k1[klookup_sym(kx,ky,K2)];
2023-05-15 20:29:06 -04:00
}
}
ns_rhs(k2, tmp, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
// k3 : u(t+3/8*delta)
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
tmp[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+(*delta)*(3./32*k1[klookup_sym(kx,ky,K2)]+9./32*k2[klookup_sym(kx,ky,K2)]);
2023-05-15 20:29:06 -04:00
}
}
ns_rhs(k3, tmp, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
// k4 : u(t+12./13*delta)
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
tmp[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+(*delta)*(1932./2197*k1[klookup_sym(kx,ky,K2)]-7200./2197*k2[klookup_sym(kx,ky,K2)]+7296./2197*k3[klookup_sym(kx,ky,K2)]);
2023-05-15 20:29:06 -04:00
}
}
ns_rhs(k4, tmp, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
// k5 : u(t+1*delta)
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
tmp[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+(*delta)*(439./216*k1[klookup_sym(kx,ky,K2)]-8*k2[klookup_sym(kx,ky,K2)]+3680./513*k3[klookup_sym(kx,ky,K2)]-845./4104*k4[klookup_sym(kx,ky,K2)]);
2023-05-15 20:29:06 -04:00
}
}
ns_rhs(k5, tmp, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
// k6 : u(t+1./2*delta)
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
tmp[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+(*delta)*(-8./27*k1[klookup_sym(kx,ky,K2)]+2*k2[klookup_sym(kx,ky,K2)]-3544./2565*k3[klookup_sym(kx,ky,K2)]+1859./4104*k4[klookup_sym(kx,ky,K2)]-11./40*k5[klookup_sym(kx,ky,K2)]);
2023-05-15 20:29:06 -04:00
}
}
ns_rhs(k6, tmp, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
// compute error
err=0;
if(adaptive_norm==NORM_L1){
relative=0;
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
err+=cabs((*delta)*(1./360*k1[klookup_sym(kx,ky,K2)]-128./4275*k3[klookup_sym(kx,ky,K2)]-2197./75240*k4[klookup_sym(kx,ky,K2)]+1./50*k5[klookup_sym(kx,ky,K2)]+2./55*k6[klookup_sym(kx,ky,K2)]));
// next step
tmp[klookup_sym(kx,ky,K2)]=(*delta)*(25./216*k1[klookup_sym(kx,ky,K2)]+1408./2565*k3[klookup_sym(kx,ky,K2)]+2197./4104*k4[klookup_sym(kx,ky,K2)]-1./5*k5[klookup_sym(kx,ky,K2)]);
relative+=cabs(tmp[klookup_sym(kx,ky,K2)]);
}
}
}
else if(adaptive_norm==NORM_k3){
relative=0;
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
err+=cabs((*delta)*(1./360*k1[klookup_sym(kx,ky,K2)]-128./4275*k3[klookup_sym(kx,ky,K2)]-2197./75240*k4[klookup_sym(kx,ky,K2)]+1./50*k5[klookup_sym(kx,ky,K2)]+2./55*k6[klookup_sym(kx,ky,K2)]))/pow(kx*kx+ky*ky,1.5);
// next step
tmp[klookup_sym(kx,ky,K2)]=(*delta)*(25./216*k1[klookup_sym(kx,ky,K2)]+1408./2565*k3[klookup_sym(kx,ky,K2)]+2197./4104*k4[klookup_sym(kx,ky,K2)]-1./5*k5[klookup_sym(kx,ky,K2)]);
relative+=cabs(tmp[klookup_sym(kx,ky,K2)])/pow(kx*kx+ky*ky,1.5);
}
}
}
else if(adaptive_norm==NORM_k32){
relative=0;
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
err+=cabs((*delta)*(1./360*k1[klookup_sym(kx,ky,K2)]-128./4275*k3[klookup_sym(kx,ky,K2)]-2197./75240*k4[klookup_sym(kx,ky,K2)]+1./50*k5[klookup_sym(kx,ky,K2)]+2./55*k6[klookup_sym(kx,ky,K2)]))/pow(kx*kx+ky*ky,0.75);
// next step
tmp[klookup_sym(kx,ky,K2)]=(*delta)*(25./216*k1[klookup_sym(kx,ky,K2)]+1408./2565*k3[klookup_sym(kx,ky,K2)]+2197./4104*k4[klookup_sym(kx,ky,K2)]-1./5*k5[klookup_sym(kx,ky,K2)]);
relative+=cabs(tmp[klookup_sym(kx,ky,K2)])/pow(kx*kx+ky*ky,0.75);
}
}
}
else if(adaptive_norm==NORM_ENSTROPHY){
double sumu, sumU;
sumu=0;
sumU=0;
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
err+=(kx*kx+ky*ky)*cabs2((*delta)*(1./360*k1[klookup_sym(kx,ky,K2)]-128./4275*k3[klookup_sym(kx,ky,K2)]-2197./75240*k4[klookup_sym(kx,ky,K2)]+1./50*k5[klookup_sym(kx,ky,K2)]+2./55*k6[klookup_sym(kx,ky,K2)]));
// next step
tmp[klookup_sym(kx,ky,K2)]=(*delta)*(25./216*k1[klookup_sym(kx,ky,K2)]+1408./2565*k3[klookup_sym(kx,ky,K2)]+2197./4104*k4[klookup_sym(kx,ky,K2)]-1./5*k5[klookup_sym(kx,ky,K2)]);
sumU+=(kx*kx+ky*ky)*cabs2((*delta)*(16./135*k1[klookup_sym(kx,ky,K2)]+6656./12825*k3[klookup_sym(kx,ky,K2)]+28561./56430*k4[klookup_sym(kx,ky,K2)]-9./50*k5[klookup_sym(kx,ky,K2)]+2./55*k6[klookup_sym(kx,ky,K2)]));
sumu+=(kx*kx+ky*ky)*cabs2(u[klookup_sym(kx,ky,K2)]+tmp[klookup_sym(kx,ky,K2)]);
}
2023-05-15 20:29:06 -04:00
}
err=sqrt(err);
relative=pow((sqrt(sumu)+sqrt(sumU))/sumu, 1./3);
}
else{
fprintf(stderr,"bug: unknown norm: %u, contact ian.jauslin@rutgers.edu\n", adaptive_norm);
exit(-1);
2023-05-15 20:29:06 -04:00
}
// compare relative error with tolerance
if(err<relative*tolerance){
2023-05-15 20:29:06 -04:00
// add to output
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
u[klookup_sym(kx,ky,K2)]+=tmp[klookup_sym(kx,ky,K2)];
2023-05-15 20:29:06 -04:00
}
}
// next delta to use in future steps (do not exceed max_delta)
*next_delta=fmin(max_delta, (*delta)*pow(relative*tolerance/err,0.2));
2023-05-15 20:29:06 -04:00
}
// error too big: repeat with smaller step
else{
*delta=factor*(*delta)*pow(relative*tolerance/err,0.2);
// do not recompute k1
ns_step_rkf45(u,tolerance,factor,max_delta,adaptive_norm,K1,K2,N1,N2,nu,delta,next_delta,L,g,fft1,fft2,ifft,k1,k2,k3,k4,k5,k6,tmp,irreversible,false);
2023-05-15 20:29:06 -04:00
}
return 0;
2023-05-15 20:29:06 -04:00
}
2023-05-17 16:47:15 -04:00
// next time step
// adaptive RK algorithm (Runge-Kutta-Bogacki-Shampine method)
2023-05-17 16:59:15 -04:00
int ns_step_rkbs32(
2023-05-17 16:47:15 -04:00
_Complex double* u,
double tolerance,
double factor,
double max_delta,
unsigned int adaptive_norm,
2023-05-17 16:47:15 -04:00
int K1,
int K2,
int N1,
int N2,
double nu,
double* delta,
double* next_delta,
double L,
_Complex double* g,
fft_vect fft1,
fft_vect fft2,
fft_vect ifft,
// the pointers k1 and k4 will be exchanged at the end of the routine
_Complex double** k1,
_Complex double* k2,
_Complex double* k3,
_Complex double** k4,
_Complex double* tmp,
bool irreversible,
// whether to compute k1
bool compute_k1
){
int kx,ky;
double err,relative;
// k1: u(t)
// only compute it if it is the first step (otherwise, it has already been computed due to the First Same As Last property)
if(compute_k1){
ns_rhs(*k1, u, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
}
// k2 : u(t+1/4*delta)
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
tmp[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+(*delta)/2*(*k1)[klookup_sym(kx,ky,K2)];
}
}
ns_rhs(k2, tmp, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
// k3 : u(t+3/4*delta)
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
tmp[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+(*delta)*(3./4*k2[klookup_sym(kx,ky,K2)]);
}
}
ns_rhs(k3, tmp, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
// k4 : u(t+delta)
// tmp cpmputed here is the next step
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
tmp[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+(*delta)*(2./9*(*k1)[klookup_sym(kx,ky,K2)]+1./3*k2[klookup_sym(kx,ky,K2)]+4./9*k3[klookup_sym(kx,ky,K2)]);
}
}
ns_rhs(*k4, tmp, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
// compute error
err=0;
if(adaptive_norm==NORM_L1){
relative=0;
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
err+=cabs((*delta)*(5./72*(*k1)[klookup_sym(kx,ky,K2)]-1./12*k2[klookup_sym(kx,ky,K2)]-1./9*k3[klookup_sym(kx,ky,K2)]+1./8*(*k4)[klookup_sym(kx,ky,K2)]));
relative+=cabs(tmp[klookup_sym(kx,ky,K2)]-u[klookup_sym(kx,ky,K2)]);
}
}
}
else if(adaptive_norm==NORM_k3){
relative=0;
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
err+=cabs((*delta)*(5./72*(*k1)[klookup_sym(kx,ky,K2)]-1./12*k2[klookup_sym(kx,ky,K2)]-1./9*k3[klookup_sym(kx,ky,K2)]+1./8*(*k4)[klookup_sym(kx,ky,K2)]))/pow(kx*kx+ky*ky,1.5);
relative+=cabs(tmp[klookup_sym(kx,ky,K2)]-u[klookup_sym(kx,ky,K2)])/pow(kx*kx+ky*ky,1.5);
}
2023-05-17 16:47:15 -04:00
}
}
else if(adaptive_norm==NORM_k32){
relative=0;
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
err+=cabs((*delta)*(5./72*(*k1)[klookup_sym(kx,ky,K2)]-1./12*k2[klookup_sym(kx,ky,K2)]-1./9*k3[klookup_sym(kx,ky,K2)]+1./8*(*k4)[klookup_sym(kx,ky,K2)]))/pow(kx*kx+ky*ky,0.75);
relative+=cabs(tmp[klookup_sym(kx,ky,K2)]-u[klookup_sym(kx,ky,K2)])/pow(kx*kx+ky*ky,0.75);
}
}
}
else if(adaptive_norm==NORM_ENSTROPHY){
double sumu, sumU;
sumu=0;
sumU=0;
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
err+=(kx*kx+ky*ky)*cabs2((*delta)*(5./72*(*k1)[klookup_sym(kx,ky,K2)]-1./12*k2[klookup_sym(kx,ky,K2)]-1./9*k3[klookup_sym(kx,ky,K2)]+1./8*(*k4)[klookup_sym(kx,ky,K2)]));
sumU+=(kx*kx+ky*ky)*cabs2((*delta)*(7./24*(*k1)[klookup_sym(kx,ky,K2)]+1./4*k2[klookup_sym(kx,ky,K2)]+1./3*k3[klookup_sym(kx,ky,K2)]+1./8*(*k4)[klookup_sym(kx,ky,K2)]));
sumu+=(kx*kx+ky*ky)*cabs2(tmp[klookup_sym(kx,ky,K2)]);
}
}
err=sqrt(err);
relative=pow((sqrt(sumu)+sqrt(sumU))/sumu, 1./3);
}
else{
fprintf(stderr,"bug: unknown norm: %u, contact ian.jauslin@rutgers,edu\n", adaptive_norm);
exit(-1);
}
2023-05-17 16:47:15 -04:00
// compare relative error with tolerance
if(err<relative*tolerance){
// add to output
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
u[klookup_sym(kx,ky,K2)]=tmp[klookup_sym(kx,ky,K2)];
}
}
// next delta to use in future steps (do not exceed max_delta)
*next_delta=fmin(max_delta, (*delta)*pow(relative*tolerance/err,1./3));
2023-05-17 16:47:15 -04:00
// k1 in the next step will be this k4 (first same as last)
tmp=*k1;
*k1=*k4;
*k4=tmp;
}
// error too big: repeat with smaller step
else{
*delta=factor*(*delta)*pow(relative*tolerance/err,1./3);
// this will reuse the same k1 without re-computing it
ns_step_rkbs32(u,tolerance,factor,max_delta,adaptive_norm,K1,K2,N1,N2,nu,delta,next_delta,L,g,fft1,fft2,ifft,k1,k2,k3,k4,tmp,irreversible,false);
2023-05-17 16:47:15 -04:00
}
return 0;
}
2023-05-17 17:41:00 -04:00
// next time step
// adaptive RK algorithm (Runge-Kutta-Dormand-Prince method)
int ns_step_rkdp54(
_Complex double* u,
double tolerance,
double factor,
2023-06-13 18:45:19 -04:00
double max_delta,
2023-06-13 23:56:35 -04:00
unsigned int adaptive_norm,
2023-05-17 17:41:00 -04:00
int K1,
int K2,
int N1,
int N2,
double nu,
double* delta,
double* next_delta,
double L,
_Complex double* g,
fft_vect fft1,
fft_vect fft2,
fft_vect ifft,
// the pointers k1 and k2 will be exchanged at the end of the routine
_Complex double** k1,
_Complex double** k2,
_Complex double* k3,
_Complex double* k4,
_Complex double* k5,
_Complex double* k6,
_Complex double* tmp,
bool irreversible,
// whether to compute k1
bool compute_k1
){
int kx,ky;
double err,relative;
// k1: u(t)
// only compute it if it is the first step (otherwise, it has already been computed due to the First Same As Last property)
if(compute_k1){
ns_rhs(*k1, u, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
}
// k2 : u(t+1/5*delta)
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
tmp[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+(*delta)/5*(*k1)[klookup_sym(kx,ky,K2)];
}
}
ns_rhs(*k2, tmp, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
// k3 : u(t+3/10*delta)
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
tmp[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+(*delta)*(3./40*(*k1)[klookup_sym(kx,ky,K2)]+9./40*(*k2)[klookup_sym(kx,ky,K2)]);
}
}
ns_rhs(k3, tmp, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
// k4 : u(t+4/5*delta)
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
tmp[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+(*delta)*(44./45*(*k1)[klookup_sym(kx,ky,K2)]-56./15*(*k2)[klookup_sym(kx,ky,K2)]+32./9*k3[klookup_sym(kx,ky,K2)]);
}
}
ns_rhs(k4, tmp, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
// k5 : u(t+8/9*delta)
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
tmp[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+(*delta)*(19372./6561*(*k1)[klookup_sym(kx,ky,K2)]-25360./2187*(*k2)[klookup_sym(kx,ky,K2)]+64448./6561*k3[klookup_sym(kx,ky,K2)]-212./729*k4[klookup_sym(kx,ky,K2)]);
}
}
ns_rhs(k5, tmp, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
// k6 : u(t+delta)
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
tmp[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+(*delta)*(9017./3168*(*k1)[klookup_sym(kx,ky,K2)]-355./33*(*k2)[klookup_sym(kx,ky,K2)]+46732./5247*k3[klookup_sym(kx,ky,K2)]+49./176*k4[klookup_sym(kx,ky,K2)]-5103./18656*k5[klookup_sym(kx,ky,K2)]);
}
}
ns_rhs(k6, tmp, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
// k7 : u(t+delta)
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
// tmp computed here is the step
tmp[klookup_sym(kx,ky,K2)]=u[klookup_sym(kx,ky,K2)]+(*delta)*(35./384*(*k1)[klookup_sym(kx,ky,K2)]+500./1113*k3[klookup_sym(kx,ky,K2)]+125./192*k4[klookup_sym(kx,ky,K2)]-2187./6784*k5[klookup_sym(kx,ky,K2)]+11./84*k6[klookup_sym(kx,ky,K2)]);
}
}
// store in k2, which is not needed anymore
ns_rhs(*k2, tmp, K1, K2, N1, N2, nu, L, g, fft1, fft2, ifft, irreversible);
// compute error
err=0;
2023-06-13 23:56:35 -04:00
if(adaptive_norm==NORM_L1){
relative=0;
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
err+=cabs((*delta)*(-71./57600*(*k1)[klookup_sym(kx,ky,K2)]+71./16695*k3[klookup_sym(kx,ky,K2)]-71./1920*k4[klookup_sym(kx,ky,K2)]+17253./339200*k5[klookup_sym(kx,ky,K2)]-22./525*k6[klookup_sym(kx,ky,K2)]+1./40*(*k2)[klookup_sym(kx,ky,K2)]));
relative+=cabs(tmp[klookup_sym(kx,ky,K2)]-u[klookup_sym(kx,ky,K2)]);
}
}
}
else if(adaptive_norm==NORM_k3){
relative=0;
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
err+=cabs((*delta)*(-71./57600*(*k1)[klookup_sym(kx,ky,K2)]+71./16695*k3[klookup_sym(kx,ky,K2)]-71./1920*k4[klookup_sym(kx,ky,K2)]+17253./339200*k5[klookup_sym(kx,ky,K2)]-22./525*k6[klookup_sym(kx,ky,K2)]+1./40*(*k2)[klookup_sym(kx,ky,K2)]))/pow(kx*kx+ky*ky,1.5);
relative+=cabs(tmp[klookup_sym(kx,ky,K2)]-u[klookup_sym(kx,ky,K2)])/pow(kx*kx+ky*ky,1.5);
}
2023-05-17 17:41:00 -04:00
}
}
2023-06-13 23:56:35 -04:00
else if(adaptive_norm==NORM_k32){
relative=0;
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
err+=cabs((*delta)*(-71./57600*(*k1)[klookup_sym(kx,ky,K2)]+71./16695*k3[klookup_sym(kx,ky,K2)]-71./1920*k4[klookup_sym(kx,ky,K2)]+17253./339200*k5[klookup_sym(kx,ky,K2)]-22./525*k6[klookup_sym(kx,ky,K2)]+1./40*(*k2)[klookup_sym(kx,ky,K2)]))/pow(kx*kx+ky*ky,0.75);
relative+=cabs(tmp[klookup_sym(kx,ky,K2)]-u[klookup_sym(kx,ky,K2)])/pow(kx*kx+ky*ky,0.75);
}
}
}
else if(adaptive_norm==NORM_ENSTROPHY){
double sumu, sumU;
sumu=0;
sumU=0;
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
err+=(kx*kx+ky*ky)*cabs2((*delta)*(-71./57600*(*k1)[klookup_sym(kx,ky,K2)]+71./16695*k3[klookup_sym(kx,ky,K2)]-71./1920*k4[klookup_sym(kx,ky,K2)]+17253./339200*k5[klookup_sym(kx,ky,K2)]-22./525*k6[klookup_sym(kx,ky,K2)]+1./40*(*k2)[klookup_sym(kx,ky,K2)]));
sumU+=(kx*kx+ky*ky)*cabs2(u[klookup_sym(kx,ky,K2)]+(*delta)*(5179./57600*(*k1)[klookup_sym(kx,ky,K2)]+7571./16695*k3[klookup_sym(kx,ky,K2)]+393./640*k4[klookup_sym(kx,ky,K2)]-92097./339200*k5[klookup_sym(kx,ky,K2)]+187./2100*k6[klookup_sym(kx,ky,K2)]+1./40*(*k2)[klookup_sym(kx,ky,K2)]));
sumu+=(kx*kx+ky*ky)*cabs2(tmp[klookup_sym(kx,ky,K2)]);
}
}
err=sqrt(err);
relative=pow((sqrt(sumu)+sqrt(sumU))/sumu, 1./3);
}
else{
fprintf(stderr,"bug: unknown norm: %u, contact ian.jauslin@rutgers,edu\n", adaptive_norm);
exit(-1);
}
2023-05-17 17:41:00 -04:00
// compare relative error with tolerance
if(err<relative*tolerance){
// add to output
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
u[klookup_sym(kx,ky,K2)]=tmp[klookup_sym(kx,ky,K2)];
}
}
2023-06-13 18:45:19 -04:00
// next delta to use in future steps (do not exceed max_delta)
*next_delta=fmin(max_delta, (*delta)*pow(relative*tolerance/err,1./5));
2023-05-17 17:41:00 -04:00
// k1 in the next step will be this k4 (first same as last)
tmp=*k1;
*k1=*k2;
*k2=tmp;
}
// error too big: repeat with smaller step
else{
*delta=factor*(*delta)*pow(relative*tolerance/err,1./5);
// this will reuse the same k1 without re-computing it
2023-06-13 23:56:35 -04:00
ns_step_rkdp54(u,tolerance,factor,max_delta,adaptive_norm,K1,K2,N1,N2,nu,delta,next_delta,L,g,fft1,fft2,ifft,k1,k2,k3,k4,k5,k6,tmp,irreversible,false);
2023-05-17 17:41:00 -04:00
}
return 0;
}
2023-04-05 20:33:38 -04:00
// right side of Irreversible/Reversible Navier-Stokes equation
int ns_rhs(
2022-05-18 09:57:06 +02:00
_Complex double* out,
_Complex double* u,
int K1,
int K2,
int N1,
int N2,
double nu,
2022-05-25 11:12:02 -04:00
double L,
_Complex double* g,
2022-05-18 09:57:06 +02:00
fft_vect fft1,
fft_vect fft2,
2023-04-05 20:33:38 -04:00
fft_vect ifft,
bool irreversible
2022-05-18 09:57:06 +02:00
){
2018-01-11 22:48:14 +00:00
int kx,ky;
2022-05-18 09:57:06 +02:00
int i;
2023-04-05 20:33:38 -04:00
double alpha;
2018-01-11 22:48:14 +00:00
// compute convolution term
ns_T(u,K1,K2,N1,N2,fft1,fft2,ifft);
2023-04-05 20:33:38 -04:00
if (irreversible) {
alpha=nu;
} else {
alpha=compute_alpha(u,K1,K2,g,L);
2023-04-05 20:33:38 -04:00
}
2022-05-18 22:22:42 +02:00
2023-04-25 17:51:14 -04:00
for(i=0; i<K1*(2*K2+1)+K2; i++){
out[i]=0;
}
2023-04-11 18:45:45 -04:00
for(kx=0;kx<=K1;kx++){
2023-04-25 17:51:14 -04:00
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
out[klookup_sym(kx,ky,K2)]=-4*M_PI*M_PI/L/L*alpha*(kx*kx+ky*ky)*u[klookup_sym(kx,ky,K2)]+g[klookup_sym(kx,ky,K2)]+4*M_PI*M_PI/L/L/sqrt(kx*kx+ky*ky)*ifft.fft[klookup(kx,ky,N1,N2)];
}
}
return(0);
}
// convolution term in right side of convolution equation
int ns_T(
_Complex double* u,
int K1,
int K2,
int N1,
int N2,
fft_vect fft1,
fft_vect fft2,
fft_vect ifft
){
int kx,ky;
int i;
2018-01-12 19:20:59 +00:00
// F(px/|p|*u)*F(qy*|q|*u)
2018-01-11 22:48:14 +00:00
// init to 0
2022-05-18 09:57:06 +02:00
for(i=0; i<N1*N2; i++){
fft1.fft[i]=0;
fft2.fft[i]=0;
ifft.fft[i]=0;
2018-01-11 22:48:14 +00:00
}
// fill modes
2022-05-18 09:57:06 +02:00
for(kx=-K1;kx<=K1;kx++){
for(ky=-K2;ky<=K2;ky++){
2018-01-11 22:48:14 +00:00
if(kx!=0 || ky!=0){
2023-04-11 18:45:45 -04:00
fft1.fft[klookup(kx,ky,N1,N2)]=kx/sqrt(kx*kx+ky*ky)*getval_sym(u, kx,ky,K2)/N1;
fft2.fft[klookup(kx,ky,N1,N2)]=ky*sqrt(kx*kx+ky*ky)*getval_sym(u, kx,ky,K2)/N2;
2018-01-11 22:48:14 +00:00
}
}
}
2018-01-12 19:20:59 +00:00
2018-01-11 22:48:14 +00:00
// fft
2022-05-18 09:57:06 +02:00
fftw_execute(fft1.fft_plan);
fftw_execute(fft2.fft_plan);
// write to ifft
for(i=0;i<N1*N2;i++){
ifft.fft[i]=fft1.fft[i]*fft2.fft[i];
2018-01-11 22:48:14 +00:00
}
2018-01-12 19:20:59 +00:00
// F(py/|p|*u)*F(qx*|q|*u)
2018-01-11 22:48:14 +00:00
// init to 0
2022-05-18 09:57:06 +02:00
for(i=0; i<N1*N2; i++){
fft1.fft[i]=0;
fft2.fft[i]=0;
2018-01-11 22:48:14 +00:00
}
// fill modes
2022-05-18 09:57:06 +02:00
for(kx=-K1;kx<=K1;kx++){
for(ky=-K2;ky<=K2;ky++){
2018-01-11 22:48:14 +00:00
if(kx!=0 || ky!=0){
2023-04-11 18:45:45 -04:00
fft1.fft[klookup(kx,ky,N1,N2)]=ky/sqrt(kx*kx+ky*ky)*getval_sym(u, kx,ky,K2)/N1;
fft2.fft[klookup(kx,ky,N1,N2)]=kx*sqrt(kx*kx+ky*ky)*getval_sym(u, kx,ky,K2)/N2;
2018-01-11 22:48:14 +00:00
}
}
}
2018-01-12 19:20:59 +00:00
2018-01-11 22:48:14 +00:00
// fft
2022-05-18 09:57:06 +02:00
fftw_execute(fft1.fft_plan);
fftw_execute(fft2.fft_plan);
// write to ifft
for(i=0;i<N1*N2;i++){
ifft.fft[i]=ifft.fft[i]-fft1.fft[i]*fft2.fft[i];
2018-01-11 22:48:14 +00:00
}
2018-01-12 19:20:59 +00:00
2018-01-11 22:48:14 +00:00
// inverse fft
2022-05-18 09:57:06 +02:00
fftw_execute(ifft.fft_plan);
2022-05-17 14:31:22 +02:00
2018-01-11 22:48:14 +00:00
return(0);
}
2022-05-18 22:22:42 +02:00
// convolution term in right side of convolution equation, computed without fourier transform
int ns_T_nofft(
_Complex double* out,
_Complex double* u,
int K1,
int K2,
int N1,
int N2
){
int kx,ky;
int px,py;
int qx,qy;
// loop over K's (needs N1>=2*K1+1 and N2>=2*K2+1)
if (N1<2*K1+1 || N2<2*K2+1){
fprintf(stderr,"error: N1 and N2 need t be >= 2*K1+1 and 2*K2+1 respectively\n");
2022-05-18 22:22:42 +02:00
return(-1);
}
for(kx=-K1;kx<=K1;kx++){
for(ky=-K2;ky<=K2;ky++){
2022-05-18 22:22:42 +02:00
// init
out[klookup(kx,ky,N1,N2)]=0.;
for(px=-K1;px<=K1;px++){
for(py=-K2;py<=K2;py++){
qx=kx-px;
qy=ky-py;
// cutoff in q
if(qx>=-K1 && qx<=K1 && qy>=-K2 && qy<=K2 && qx*qx+qy*qy>0 && px*px+py*py>0){
2023-04-11 18:45:45 -04:00
out[klookup(kx,ky,N1,N2)]+=(-qx*py+qy*px)*sqrt(qx*qx+qy*qy)/sqrt(px*px+py*py)*getval_sym(u, px,py,K2)*getval_sym(u, qx,qy,K2);
2022-05-18 22:22:42 +02:00
}
}
}
}
}
return 0;
}
2018-01-11 22:48:14 +00:00
2023-04-05 20:33:38 -04:00
// compute alpha
double compute_alpha(
_Complex double* u,
int K1,
int K2,
_Complex double* g,
double L
){
_Complex double num=0;
double denom=0;
int kx,ky;
num=0.;
denom=0.;
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
num+=L*L/4/M_PI/M_PI*(kx*kx+ky*ky)*getval_sym(g, kx,ky,K2)*conj(getval_sym(u, kx,ky,K2));
denom+=__real__ (kx*kx+ky*ky)*(kx*kx+ky*ky)*getval_sym(u, kx,ky,K2)*conj(getval_sym(u, kx,ky,K2));
}
}
return __real__ num/denom;
}
// compute energy
double compute_energy(
_Complex double* u,
int K1,
int K2
){
int kx,ky;
double out=0.;
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
2023-04-11 18:45:45 -04:00
out+=__real__ (getval_sym(u, kx,ky,K2)*conj(getval_sym(u, kx,ky,K2)));
}
}
return 2*out;
}
// compute enstrophy
double compute_enstrophy(
_Complex double* u,
int K1,
int K2,
double L
){
int kx,ky;
double out=0.;
for(kx=0;kx<=K1;kx++){
for(ky=(kx>0 ? -K2 : 1);ky<=K2;ky++){
2023-04-11 18:45:45 -04:00
out+=__real__ (4*M_PI*M_PI/L/L*(kx*kx+ky*ky)*getval_sym(u, kx,ky,K2)*conj(getval_sym(u, kx,ky,K2)));
}
}
return 2*out;
2018-01-11 22:48:14 +00:00
}
2022-05-18 09:57:06 +02:00
// get index for kx,ky in array of size S
int klookup(
int kx,
int ky,
int S1,
int S2
){
return (kx>=0 ? kx : S1+kx)*S2 + (ky>=0 ? ky : S2+ky);
}
2023-04-11 18:45:45 -04:00
2023-04-25 17:51:14 -04:00
// get index for kx,ky in array of size K1,K2 in which only the terms with kx>=0 and if kx=0, ky>0 are stored
2023-04-11 18:45:45 -04:00
int klookup_sym(
int kx,
int ky,
int K2
){
if (kx<0) {
fprintf(stderr, "bug!: attempting to access a symmetrized vector at kx<0\n Contact Ian at ian.jauslin@rutgers.edu!\n");
exit(-1);
}
2023-04-25 17:51:14 -04:00
if (kx==0) {
if (ky<=0){
fprintf(stderr, "bug!: attempting to access a symmetrized vector at kx=0 and ky<=0\n Contact Ian at ian.jauslin@rutgers.edu!\n");
exit(-1);
}
return ky-1;
}
return K2+(kx-1)*(2*K2+1) + (ky>=0 ? ky : (2*K2+1)+ky);
2023-04-11 18:45:45 -04:00
}
// get u_{kx,ky} from a vector u in which only the values for kx>=0 are stored, assuming u_{-k}=u_k^*
_Complex double getval_sym(
_Complex double* u,
int kx,
int ky,
int K2
){
2023-04-25 17:51:14 -04:00
if(kx>0 || (kx==0 && ky>0)){
return u[klookup_sym(kx,ky,K2)];
}
else if(kx==0 && ky==0){
return 0;
} else {
return conj(u[klookup_sym(-kx,-ky,K2)]);
}
2023-04-11 18:45:45 -04:00
}