sandbox/acastillo/filaments/tests_biot_savart/test_vortex_ring0.c

    Motion of a thin vortex ring (using the vortex filament framework)

    In this example, we consider the motion of a vortex ring of radius R and core size a using the vortex filament framework.

    The filament approach is perfectly justified as long as the core size remains small compared to other spatial scales (local curvature). We use the same framework as in Durán Venegas & Le Dizès (2019), Castillo-Castellanos, Le Dizès & Durán Venegas (2021), and Castillo-Castellanos & Le Dizès (2019). All the vorticity is considered as being concentrated along space-curves \vec{x}\in\mathcal{C} which move as material lines in the fluid according to: \begin{aligned} \frac{d\vec{x}_c}{dt} = \vec{U}_{ind} + \vec{U}_{\infty} \end{aligned} where \vec{x}_c is the position vector of vortex filament, \vec{U}_{ind} the velocity induced by the vortex filament and \vec{U}_{\infty} an external velocity field. The induced velocity \vec{U}_{ind} is given by the Biot-Savart law.

    • In this test, we verify that the translational velocity u_z is independent of the discretization:
    import numpy as np
    import matplotlib.pyplot as plt
    	
    plt.figure()
    data = np.loadtxt('curve.txt', delimiter=' ', usecols=[0,2,3,4])
    plt.plot(data[:,0], data[:,1], ls='--', lw=1.4, label='local contributions');
    plt.plot(data[:,0], data[:,2], ls='-.', lw=1.6, label='non-local contributions');
    plt.plot(data[:,0], data[:,3], ls='-',  lw=2,   label='total velocity');
    plt.legend(loc='best');
    plt.xlabel(r'$n_{seg}$')
    plt.ylabel(r'$U_z$')
    plt.xlim([8,128])
    plt.tight_layout()
    plt.savefig('plot_Uz_vs_nseg.svg')
    Contributions to the total axial velocity from different sources (script)
    #include "grid/octree.h"
    #include "run.h"
    #include "view.h"
    #include "acastillo/input_fields/filaments.h"
    #include "acastillo/input_fields/draw_filaments.h"
    #include "acastillo/filaments/biot-savart.h"
    
    int minlevel = 4;
    int maxlevel = 8;
    vector u[];
    int main(){
      L0 = 10.0;
      X0 = Y0 = Z0 = -L0 / 2;
      
      N = 1 << minlevel;  
      init_grid(N);  
    
      if (pid() == 0){
        FILE *fp = fopen("curve.txt", "w"); 
        fclose(fp);
      }  
      
      double R = 1.0;
      double a=0.01;
      struct vortex_filament filament1;
      coord Uinfty = {0., 0., 0.};  
      for (int nseg = 8; nseg < 128; nseg+=1){
    
        double dphi = 2*pi/((double)nseg);
        double phi[nseg];
        double a1[nseg];
        double vol1[nseg];
        coord C1[nseg];
    
        // Define a curve 
        for (int j = 0; j < nseg; j++){
          phi[j] = dphi * (double)j;
          C1[j] = (coord) { R * cos(phi[j]), R * sin(phi[j]), 0.};
          vol1[j] = pi * sq(a) * R * dphi;
          a1[j] = a;
        } 
        
        // We store the space-curve in a structure   
        allocate_vortex_filament_members(&filament1, nseg);
        memcpy(filament1.phi, phi, nseg * sizeof(double));
        memcpy(filament1.C, C1, nseg * sizeof(coord));
        memcpy(filament1.a, a1, nseg * sizeof(double));  
        memcpy(filament1.vol, vol1, nseg * sizeof(double));
    
        local_induced_velocity(&filament1, Gamma=1.0);  
        for (int j = 0; j < nseg; j++) {
          filament1.a[j] = sqrt(filament1.vol[j]/(pi*filament1.s[j]));
        }      
        local_induced_velocity(&filament1, Gamma=1.0);  
        for (int j = 0; j < nseg; j++) {
          filament1.Uauto[j] = nonlocal_induced_velocity(filament1.C[j], &filament1, Gamma=1.0);            
          foreach_dimension(){
            filament1.U[j].x = filament1.Uauto[j].x + filament1.Ulocal[j].x - Uinfty.x;      
          }
        }
        if (pid() == 0){
          FILE *fp = fopen("curve.txt", "a");
          for (int j = 0; j < nseg; j++) {
            fprintf(fp, "%d %d ", nseg, j);        
            fprintf(fp, "%g ", filament1.Ulocal[j].z);
            fprintf(fp, "%g ", filament1.Uauto[j].z);
            fprintf(fp, "%g \n", filament1.U[j].z);
          }
          fputs("\n", fp);  
          fclose(fp);
        }
        free_vortex_filament_members(&filament1);
      }
    • We also verify that the translational velocity U_z has a log-dependency on the core size a, according to: U_z = \frac{\Gamma}{4\pi}\left(\log\left(\frac{8}{a}\right)-0.5\right) which is all contained inside the local contributions.
    import numpy as np
    import matplotlib.pyplot as plt
    	
    plt.figure()
    data = np.loadtxt('curve2.txt', delimiter=' ', usecols=[2,3,4,5])
    plt.semilogx(data[:,0], data[:,1], ls='--', lw=1.4, label='local contributions');
    plt.semilogx(data[:,0], data[:,2], ls='-.', lw=1.6, label='non-local contributions');
    plt.semilogx(data[:,0], data[:,3], ls='-',  lw=2,   label='total velocity');
    
    def func(x):
      return (1/(4*np.pi))*(np.log(8/x)-0.5)
    plt.semilogx(data[:,0], func(data[:,0]), '-k', label=r'$(\Gamma/4\pi)(\log(8/a)-0.5$')
    
    plt.legend(loc='best');
    plt.xlabel(r'$a$')
    plt.ylabel(r'$U_z$')
    plt.xlim([5e-4,5e-1])
    plt.tight_layout()
    plt.savefig('plot_Uz_vs_a.svg')
    Contributions to the total axial velocity from different sources as function of a (script)
      if (pid() == 0){
        FILE *fp = fopen("curve2.txt", "w"); 
        fclose(fp);
      }  
    
      for (int i = 1; i < 1000; i++){
        int nseg = 64;
        double a = 0.0005*(double)i;
        double dphi = 2*pi/((double)nseg);
        double phi[nseg];
        double a1[nseg];
        double vol1[nseg];
        coord C1[nseg];
    
        // Define a curve 
        for (int j = 0; j < nseg; j++){
          phi[j] = dphi * (double)j;
          C1[j] = (coord) { R * cos(phi[j]), R * sin(phi[j]), 0.};
          vol1[j] = pi * sq(a) * R * dphi;
          a1[j] = a;
        } 
        
        // We store the space-curve in a structure   
        allocate_vortex_filament_members(&filament1, nseg);
        memcpy(filament1.phi, phi, nseg * sizeof(double));
        memcpy(filament1.C, C1, nseg * sizeof(coord));
        memcpy(filament1.a, a1, nseg * sizeof(double));  
        memcpy(filament1.vol, vol1, nseg * sizeof(double));
        
        local_induced_velocity(&filament1, Gamma=1.0);  
        for (int j = 0; j < nseg; j++) {
          filament1.a[j] = sqrt(filament1.vol[j]/(pi*filament1.s[j]));
        }      
        local_induced_velocity(&filament1, Gamma=1.0);  
        for (int j = 0; j < nseg; j++) {
          filament1.Uauto[j] = nonlocal_induced_velocity(filament1.C[j], &filament1, Gamma=1.0);            
          foreach_dimension(){
            filament1.U[j].x = filament1.Uauto[j].x + filament1.Ulocal[j].x - Uinfty.x;      
          }
        }
        if (pid() == 0){
          FILE *fp = fopen("curve2.txt", "a");
          for (int j = 0; j < nseg; j++) {
            fprintf(fp, "%d ", i);        
            fprintf(fp, "%d ", j);        
            fprintf(fp, "%g ", filament1.a[j]);        
            fprintf(fp, "%g ", filament1.Ulocal[j].z);
            fprintf(fp, "%g ", filament1.Uauto[j].z);
            fprintf(fp, "%g \n", filament1.U[j].z);
          }
          fputs("\n", fp);  
          fclose(fp);
        }
        free_vortex_filament_members(&filament1);
      }
    }

    References

    [castillo2022]

    A. Castillo-Castellanos and S. Le Dizès. Closely spaced co-rotating helical vortices: long-wave instability. Journal of Fluid Mechanics, 946:A10, 2022. [ DOI ]

    [castillo2021]

    A. Castillo-Castellanos, S. Le Dizès, and E. Durán Venegas. Closely spaced corotating helical vortices: General solutions. Phys. Rev. Fluids, 6:114701, Nov 2021. [ DOI | http ]

    [duran2019]

    E. Durán Venegas and S. Le Dizès. Generalized helical vortex pairs. Journal of Fluid Mechanics, 865:523–545, 2019. [ DOI ]