This little notebook compares viiflow and XFOIL runtimes for different panel densities of the S805 airfoil. It is not 100% fair, because

  • xfoil does plot some things in the background and because XFOIL has to write some things into a text file. But that cost should stay quite the same with increasing panel densities and therefore seems negligible.
  • viiflow uses numpys solve call for the coupling Newton step which might use processor features unused in XFOIL (i.e. SIMD instructions).
  • the airfoil tested, the Selig S805, is the one I use during debugging, which may suggest that for different airfoils, the convergence rate of viiflow may be lower and the runtime higher. Failing an angle of attack means iterating the maximum number of iterations for both methods.

Note: These results were generated with a PC with a AMD Phenom 2 X6 1055T processor on a windows 10 64bit OS with numpy built with Intel MKL and the xfoil 6.99 binary version.

In [1]:
import os # For calling xfoil

'''
Numpy (with MKL in this case) might want to use multiple threads to do linalg.solve. 
Solving 150x150->500x500 matrices with multiple cores it not super efficient,  
especially if your processor uses a higher clock if only one core is used.
And even if it would be faster with multiple cores, it would not be fair to compare
these results with single-core using XFOIL.
'''
os.environ["OMP_NUM_THREADS"] = "1"

# viiflow imports
import viiflow as vf
import viiflowtools.vf_tools as vft
import numpy as np

import time # For timing

# For plotting at the end
import matplotlib.pyplot as plt
%matplotlib inline
%config InlineBackend.figure_format = 'svg'
import matplotlib
In [2]:
NVEC = np.arange(100,500,10) # Panel densities to compare

# AOA Sweep
AOA0 = -5
AOAE = 16
DAOA = .5

RE = 1e6 # Reynolds Number
maxiter = 50 # maximum iterations for both methods
FOIL = "S805.dat"

# Container for results
elapsedXF=[]
elapsedVF=[]

for N in NVEC:
    
    ## Build correct file with N panels by XFOIL, compare both solvers with this file
    # Make XFOIL command
    com = "load %s\nPPAR\nN %u\n\n\nsave tmpfoil.dat\ny\n"%(FOIL,N)
    # Save Xfoil Command
    with open('xfcom.inp', 'w') as f:
        print(com,file=f)
        f.close()
    # Run XFOIL
    os.system("xfoil < xfcom.inp > xf.out")
    
    
    ## XFOIL
    # Make XFOIL command
    com = "load %s\nOPER\nv %u\nITER %u\nASEQ %f %f %f\n\nquit\n"%('tmpfoil.dat',RE,maxiter,AOA0,AOAE,DAOA)
    #com = "load %s\nPPAR\nN %u\n\n\nOPER\nv %u\nITER %u\nASEQ %f %f %f\n\nquit\n"%(FOIL,N,RE,maxiter,AOA0,AOAE,DAOA)
    # Save Xfoil Command
    with open('xfcom.inp', 'w') as f:
        print(com,file=f)
        f.close()
    # Run XFOIL
    t = time.time()
    os.system("xfoil < xfcom.inp > xf.out")
    elapsedXF.append(time.time() - t)
    
    # Check for convergence
    f = open("xf.out", "r")
    lines = f.readlines()
    f.close()
    xf_unconverged=0
    for line in lines:
        if line.startswith(" VISCAL:  Convergence failed"):
            xf_unconverged+=1
    
    
    ## viiflow
    s = vf.setup(Re=RE,Ma=0.0,Ncrit=9.0,Alpha=AOA0)
    s.Silent = True # Do not output residual/iterations internally
    s.Itermax = maxiter

    
    t = time.time()
    # Set-up and initialize based on inviscid panel solution
    # This calculates panel operator
    [p,bl,x] = vf.init(vft.read_selig('tmpfoil.dat'),s)
    vf.set_forced_transition(bl,p,[0.5],[0.5])
    
    vf_unconverged = 0
    for alpha in np.arange(AOA0,AOAE+DAOA,DAOA):
    
        # Set current alpha and set res/grad to None to tell viiflow that they are not valid
        s.Alpha = alpha
        res = None
        grad = None

        # Run viiflow
        [x,flag,res,_,_] = vf.iter(x,bl,p,s,res,grad)

        # Print if not converged
        if flag<=0:
            #print('Not converged at AL: %f' % alpha)
            vf_unconverged+=1

    elapsedVF.append(time.time() - t)
    
    # Print comparison
    print("N: %u XF: %ss (%u failed) VF:%ss (%u failed)"%(N,elapsedXF[-1],xf_unconverged,elapsedVF[-1],vf_unconverged))
    
    
N: 100 XF: 1.4649670124053955s (0 failed) VF:1.8780105113983154s (0 failed)
N: 110 XF: 1.3460237979888916s (0 failed) VF:1.8899648189544678s (1 failed)
N: 120 XF: 1.616001844406128s (0 failed) VF:2.287841558456421s (1 failed)
N: 130 XF: 2.231051445007324s (1 failed) VF:2.0320303440093994s (0 failed)
N: 140 XF: 2.4480011463165283s (1 failed) VF:2.2030444145202637s (0 failed)
N: 150 XF: 2.682016611099243s (1 failed) VF:2.918943166732788s (1 failed)
N: 160 XF: 4.464014768600464s (4 failed) VF:3.068044424057007s (1 failed)
N: 170 XF: 3.8319904804229736s (1 failed) VF:3.148983955383301s (1 failed)
N: 180 XF: 4.529965877532959s (1 failed) VF:4.016063213348389s (1 failed)
N: 190 XF: 5.858054876327515s (1 failed) VF:3.8120663166046143s (1 failed)
N: 200 XF: 8.320648908615112s (1 failed) VF:4.857321739196777s (1 failed)
N: 210 XF: 7.651485443115234s (1 failed) VF:4.7580320835113525s (1 failed)
N: 220 XF: 9.569605112075806s (2 failed) VF:5.266568660736084s (1 failed)
N: 230 XF: 9.881593465805054s (1 failed) VF:4.96503210067749s (0 failed)
N: 240 XF: 9.89513897895813s (0 failed) VF:6.2639994621276855s (1 failed)
N: 250 XF: 11.28543496131897s (1 failed) VF:6.127026081085205s (2 failed)
N: 260 XF: 11.97486662864685s (1 failed) VF:7.138968467712402s (3 failed)
N: 270 XF: 14.610000610351562s (2 failed) VF:6.7560131549835205s (1 failed)
N: 280 XF: 16.955946445465088s (3 failed) VF:7.152999639511108s (2 failed)
N: 290 XF: 14.916030883789062s (1 failed) VF:6.954985618591309s (1 failed)
N: 300 XF: 13.336970567703247s (0 failed) VF:8.62898874282837s (2 failed)
N: 310 XF: 15.923629999160767s (0 failed) VF:9.36094856262207s (3 failed)
N: 320 XF: 21.14393639564514s (2 failed) VF:8.140997886657715s (1 failed)
N: 330 XF: 17.09503436088562s (0 failed) VF:8.469033479690552s (1 failed)
N: 340 XF: 27.852946519851685s (2 failed) VF:9.23200249671936s (1 failed)
N: 350 XF: 15.972874164581299s (4 failed) VF:9.399247169494629s (1 failed)
N: 360 XF: 29.75042223930359s (2 failed) VF:10.674961805343628s (2 failed)
N: 370 XF: 32.890053272247314s (2 failed) VF:10.619025468826294s (1 failed)
N: 380 XF: 29.8659348487854s (0 failed) VF:13.484004974365234s (3 failed)
N: 390 XF: 41.35123872756958s (3 failed) VF:11.295965433120728s (1 failed)
N: 400 XF: 42.52203321456909s (3 failed) VF:13.163064002990723s (2 failed)
N: 410 XF: 39.591697454452515s (1 failed) VF:12.76315426826477s (0 failed)
N: 420 XF: 37.881977558135986s (0 failed) VF:13.93502140045166s (1 failed)
N: 430 XF: 64.33236360549927s (5 failed) VF:14.013046503067017s (1 failed)
N: 440 XF: 69.2680275440216s (4 failed) VF:14.679842472076416s (1 failed)
N: 450 XF: 50.97595524787903s (0 failed) VF:19.33501958847046s (3 failed)
N: 460 XF: 56.86715316772461s (1 failed) VF:18.187987804412842s (2 failed)
N: 470 XF: 72.29701685905457s (3 failed) VF:18.460018396377563s (2 failed)
N: 480 XF: 83.77977252006531s (4 failed) VF:20.433030366897583s (2 failed)
N: 490 XF: 77.3111047744751s (3 failed) VF:19.273021459579468s (1 failed)
In [3]:
matplotlib.rcParams['figure.figsize'] = [11, 6] # Make plots bigger than default
n = len(elapsedVF)
plt.plot(NVEC[0:n],elapsedVF,'-o')
plt.plot(NVEC[0:n],elapsedXF,'-o')
plt.legend(["viiflow","xfoil"])
plt.xlabel("Panels")
plt.ylabel("Runtime [s]")
plt.grid(1)
plt.ylim([0,80])
plt.xlim([90,500]);

For less than 200 panels (NP) XFOIL is occasionally a bit faster than viiflow, and either way the runtime is similar. Above 200 panels viiflow is significantly faster. The runtime difference increases with increasing panel density. This is to be expected. XFOIL needs to solve a system of 3xNP linear equations at every Newton step, while viiflow only needs to solve a 1xNP+3xNF system (NF is the number of airfoils).