sandbox/acastillo/input_fields/initial_conditions_dimonte_fft1.h

    Initializing an interface using a 1D spectrum

    To initialize an interface using a 2D spectum see here.

    To initialize the interface we’ll use a 1 Fourier transform and some interpolation routines from GSL.

    #include <gsl/gsl_fft_complex.h>
    #include <gsl/gsl_complex.h>
    #include <gsl/gsl_complex_math.h>
    #include <gsl/gsl_spline.h>
    #pragma autolink -lgsl -lgslcblas
    #define REAL(z,i) ((z)[2*(i)])
    #define IMAG(z,i) ((z)[2*(i)+1])

    Save the initial perturbation in a gnuplot-compatible format

    save_data_for_gnuplot_complex(): saves a (complex) 1D array as a .dat file

    void save_data_for_gnuplot_complex(double *data, int NX, const char *filename){
      FILE *file = fopen(filename, "w");
      if (file == NULL){
        fprintf(stderr, "Error opening file %s\n", filename);
        return;
      }
    
      for (int i = 0; i < NX; i++){
        double magnitude = sqrt(sq(REAL(data, i)) + sq(IMAG(data, i)));
        fprintf(file, "%d %f %f %f\n", i, REAL(data,i), IMAG(data,i), magnitude);
      }
      fclose(file);
    }

    save_data_for_gnuplot_real(): saves a 1D array as a .dat file

    void save_data_for_gnuplot_real(double *data, int NX, const char *filename){
      FILE *file = fopen(filename, "w");
      if (file == NULL){
        fprintf(stderr, "Error opening file %s\n", filename);
        return;
      }
    
      for (int i = 0; i < NX; i++){
        fprintf(file, "%d %f \n", i, data[i]);
      }
      fclose(file);
    }

    Generate the perturbation in Fourier space and return to physical space

    init_1D_complex(): initializes the perturbation in Fourier space

    void init_1D_complex(double *data, int N, double kmin, double kmax, double eta0_target=1){
      double *kx = malloc(N * sizeof(double));
      double cst = eta0_target / sqrt((2./kmin)-(2./kmax));
    
      // Calculate wavenumbers
      for (int i = 0; i <= N / 2; ++i)
        kx[i] = 2 * pi * i / L0;
    
      for (int i = N / 2 + 1; i < N; ++i)
        kx[i] = 2 * pi * (i - N) / L0;
    
      // Initialize spectrum in the specified range with magnitude 1/k and random phase
      double dkx = kx[1]-kx[0];
      double eta0 = 0.;
      memset(data, 0, 2 * N * sizeof(double));
      for (int i = 0; i < N; ++i){
        double k = sqrt(sq(kx[i]));
        if ((k >= kmin) && (k < kmax)){
          double magnitude = cst / k;
          double phase = noise() * pi;
          REAL(data, i) = magnitude * cos(phase);
          IMAG(data, i) = magnitude * sin(phase);
          eta0 += sq(magnitude)*dkx;
        }    
      }
      fprintf(stdout, "real eta0 is %g \n", sqrt(eta0));
    
      free(kx);
    }

    Apply the initial condition to a scalar field

    initial_condition_dimonte_fft(): initializes a scalar field f

    This function initializes a scalar field phi with a perturbation generated using a 1D Fast Fourier Transform (FFT). The perturbation is generated by the main process and broadcasted to other processes if MPI is used. The perturbation can be applied directly to the field or used to generate a Volume of Fluid (VOF) surface.

    The arguments and their default values are:

    phi
    vertex scalar field to be initialized.
    amplitude
    amplitude of the perturbation. Default is 1.
    NX
    number of points along the x-dimension. Default is N.
    kmin
    minimum wavenumber for the perturbation. Default is 1.
    kmax
    maximum wavenumber for the perturbation. Default is 1.

    Example Usage

    initial_condition_dimonte_fft(phi, 0.5, 128, 0.1, 10.0);

    see, also example.

    void initial_condition_dimonte_fft(vertex scalar phi, double amplitude=1, int NX=N, double kmin=1, double kmax=1){
      
      // We declare the arrays and initialize the physical space
      double *data = malloc(2 * NX * sizeof(double));
      double *xdata = (double *)malloc(NX * sizeof(double));
      double *ydata = (double *)malloc(NX * sizeof(double));
    
      double dx = L0 / (NX - 2);
      for (int i = 0; i < NX; i++){
        xdata[i] = i * dx + X0;
      }
    
      // The perturbation is generated only by the main process
      if (pid() == 0){
        // Initialize the spectrum
        init_1D_complex(data, NX, kmin, kmax, eta0_target=amplitude);
        save_data_for_gnuplot_complex(data, NX, "initial_spectra.dat");
    
        // Perform the FFT2D 
        gsl_fft_complex_radix2_backward(data, 1, NX);
        save_data_for_gnuplot_complex(data, NX, "final_deformation.dat");
    
        // Save the results into a 2D array
        for (int i = 0; i < NX; i++){
          ydata[i] = REAL(data,i);
        }
        save_data_for_gnuplot_real(ydata, NX, "final_deformation2.dat");
      }
    
      // and broadcasted to the other processes if MPI
      @ if _MPI
        MPI_Bcast(ydata, NX, MPI_DOUBLE, 0, MPI_COMM_WORLD);
      @endif
    
      // Now, we'll interpolate the perturbation into the mesh
      gsl_interp *interp = gsl_interp_alloc(gsl_interp_linear, NX);
      gsl_interp_accel *acc = gsl_interp_accel_alloc();
    
      // Initialize the interpolation object
      gsl_interp_init(interp, xdata, ydata, NX);

    Apply initial condition to the scalar field. Here, we take care to normalize the perturbation using the standard deviation and multiply it by some amplitude.

      foreach_vertex()
        phi[] = gsl_interp_eval(interp, xdata, ydata, x, acc) - y;
      
    
      // Release interpolation objects
      gsl_interp_free(interp);
      gsl_interp_accel_free(acc);
    
      // Free Dynamically Allocated Memory
      free(xdata);
      free(ydata);
      free(data);
    }