PR

【xlwings】Excel VBAを使ってXFOILで2次元翼型を解析する

Excel から xfoil-python を呼び出して2次元翼型を解析する方法について説明する

スポンサーリンク

xlwingsとは

xlwingsとは,pythonでExcelを操作したりExcelからpythonを操作したりできるようになるライブラリである

公式ドキュメント
xlwings Documentation

詳しいことはいろいろなブログでまとめられているのでそちらを参考に
openpyxl と xlwings の比較 - Qiita
【Python】openpyxlとxlwingsとの比較 _ プログラミングで人生が楽になりました

今回の記事の目的は前回の記事で導入した xfoil-python を Excel VBA から呼び出せるようにすることである

↓xfoil-python についてはこちら

xlwings のインストール

公式ドキュメントに従って,xlwingsのパッケージとアドインをインストールする
インストール - xlwings Documentation

前提条件
・xlwingsはExcelのインストールを前提としているため、WindowsとmacOSでしか動作しません。また、macOSでは現在UDFをサポートしていません。
・xlwingsはPython 3.6以上で動作します。

インストール - xlwings Documentation

ちなみにVBAのプログラムの中でpythonで定義した関数と引数や戻り値をやり取りするにはUDF(ユーザー定義関数)を使う必要がある

パッケージのインストール

パッケージのインストールは pip install xlwings で行う(前回の記事同様pythonのバージョンは3.6を使用)

C:\Users\XXX\Desktop\xfoil>py -3.6 -m pip install xlwings 
Collecting xlwings
  Downloading xlwings-0.25.3.tar.gz (807 kB)
     |████████████████████████████████| 807 kB 1.7 MB/s
  Preparing metadata (setup.py) ... done
Collecting pywin32
  Downloading pywin32-303-cp36-cp36m-win_amd64.whl (9.3 MB)
     |████████████████████████████████| 9.3 MB 6.4 MB/s
Using legacy 'setup.py install' for xlwings, since package 'wheel' is not installed.
Installing collected packages: pywin32, xlwings
    Running setup.py install for xlwings ... done
Successfully installed pywin32-303 xlwings-0.25.3

できた

どこぞの xf〇il とは大違いである

pip list で確認しても問題なくインストールできている

C:\Users\XXX\Desktop\xfoil>py -3.6 -m pip list
Package         Version
--------------- -------
cycler          0.11.0 
kiwisolver      1.3.1  
matplotlib      3.3.4  
numpy           1.19.5 
Pillow          8.4.0  
pip             21.3.1 
pyparsing       3.0.6  
python-dateutil 2.8.2  
pywin32         303    
setuptools      40.6.2 
six             1.16.0 
xfoil           1.1.1  
xlwings         0.25.3 
アドインのインストール

xlwings addin install でアドインをインストールする

C:\Users\XXX\Desktop\xfoil>xlwings addin install
xlwings version: 0.25.3
Successfully installed the xlwings add-in! Please restart Excel.

Excelを開いてみると,タブに「xlwings」が追加されていることがわかる

クイックスタート

xlwings quickstart myproject のコマンドを実行することで「myproject」というディレクトリが作成される

quickstart
Run "xlwings quickstart myproject" to create a folder called "myproject" in the current directory with an Excel file and a Python file, ready to be used.

コマンド ライン クライアント(CLI) - xlwings Documentation

ディレクトリの中にはサンプルコードである myproject.py とマクロ有効ブック myproject.xlsm が作成されている

# myproject.py
import xlwings as xw

def main():
    wb = xw.Book.caller()
    sheet = wb.sheets[0]
    if sheet["A1"].value == "Hello xlwings!":
        sheet["A1"].value = "Bye xlwings!"
    else:
        sheet["A1"].value = "Hello xlwings!"

@xw.func
def hello(name):
    return f"Hello {name}!"

if __name__ == "__main__":
    xw.Book("myproject.xlsm").set_mock_caller()
    main()

マクロ有効ブックのほうには2つのシートがあり,「_xlwings.conf」はリボンで行うxlwingsの設定をシートで行う時に使うものである

ワークブック設定: xlwings.conf シート
ワークブック固有の設定は、グローバル設定やワークブック ディレクトリの設定を上書きします。 xlwings.conf という名前のシートに設定のキーと値を入力すれば、ワークブック固有の設定を行えます。 xlwings quickstart で新しいプロジェクトを始めれば、ワークブックにそのようなシートが初めから含まれており、シート名を xlwings.conf に変更すると設定が有効になります。

アドインおよび設定 - xlwings Documentation

「Alt+F11」でVBEを開くと,VBAのサンプルプログラムも確認できる

Sub SampleCall()
    mymodule = Left(ThisWorkbook.name, (InStrRev(ThisWorkbook.name, ".", -1, vbTextCompare) - 1))
    RunPython "import " & mymodule & ";" & mymodule & ".main()"
End Sub

サンプルプログラムの内容についてはこちら
RunPython - xlwings Documentation

サンプルプログラムの実行

せっかくなのでサンプルプログラムを実行してみる

「Alt+F8」でマクロのウィンドウを開き,SampleCall > Run をクリックする

Sheet1のセルA1に「Hello xlwings!」が表示された

xfoil-pythonの実行

サンプルプログラムをいじって xfoil を実行してみる

使うのはユーザー定義関数
ユーザー定義関数 (UDFs) - xlwings Documentation

プログラム

myproject.py のプログラムはこんな感じ

define() はマクロ有効ブックの絶対パス wb_path と解析する翼型の名前 airfoil_name を引数として受け取り,翼型を読み込んで各種設定を行う関数

xf_a() は迎角 alpha とレイノルズ数 Re を引数として受け取り,xf.a()を実行して空力係数を計算して計算結果を返す関数である

#!python3.6
import os

import numpy as np
from xfoil import XFoil
from xfoil.model import Airfoil
import xlwings as xw

xf = XFoil() # Create an instance of the XFoil class

@xw.func
def define(wb_path, airfoil_name):
    # Read airfoil
    with open(os.path.join(wb_path,airfoil_name+".dat"), "r") as f:
        airfoil = f.readlines() # Read an airfoil in the same format as XFLR5 (.dat)
    airfoil = [line.split() for line in airfoil] # Split strings by tab
    airfoil = np.array(airfoil[1:],dtype='float64') # Convert strings in list to numpy array 
    xf.airfoil = Airfoil(airfoil[:,0],airfoil[:,1]) # Airfoil: Instance of the Airfoil class

    # Difine analysis
    xf.M = 0            # float: Mach number.
    xf.n_crit = 9       # float: Critical amplification ratio
    xf.xtr = (1, 1)     # tuple(float, float): Top and bottom flow trip x/c locations
    xf.max_iter = 100   # int: Maximum number of iterations
    xf.repanel          # Re-panel airfoil
    
@xw.func
def xf_a(alpha, Re):
    xf.reset_bls        # Reset the boundary layers to be reinitialized on the next analysis
    xf.Re = Re          # float: Reynolds number
    cl, cd, cm, cp = xf.a(alpha) # Analyze airfoil at a fixed angle of attack
    return cl, cd, cm, cp

if __name__ == "__main__":
    wb = xw.Book("myproject.xlsm")
    macro = wb.macro('SampleCall')
    macro()

参考
xfoil-python_xfoil.py at master
Pythonのif name == main とは何ですか?への回答 - Python学習チャンネル by PyQ

myproject.xlsm のプログラムはこれ

airfoil_name,alpha,Re はシートから読み取り,計算結果をシートに出力する

Sub SampleCall()

    Dim airfoil_name As String: airfoil_name = Range("A2")
    Dim alpha As Double: alpha = Range("B2")
    Dim Re As Double: Re = Range("C2")
    Dim coeff As Variant

    ' define
    wb_path = ThisWorkbook.Path
    Call xlwings_udfs.define(wb_path, airfoil_name)
    
    'xfoil_a
    coeff = xlwings_udfs.xf_a(alpha, Re)
    Range("D2:G2") = coeff

End Sub

エクセルシートを置いているフォルダをOneDriveと同期している場合はThisWorkbook.Pathが使えないのでこちらを参考
[VBA]OneDriveで同期しているファイルまたはフォルダのURLをローカルパスに変換する関数

OneDriveUrlToLocalPath(ByRef Url As String)

エクセルシートの配置はこんな感じ

今回解析する DAE31.dat はこれ

DAE31
 1.0000000     0.0000000
 0.9910549     0.0019676
 0.9751725     0.0056508
 0.9562892     0.0102773
 0.9348845     0.0157716
 0.9118290     0.0219602
 0.8877617     0.0287011
 0.8629994     0.0358851
 0.8377395     0.0434059
 0.8121316     0.0511492
 0.7867002     0.0588254
 0.7619602     0.0661241
 0.7378954     0.0729224
 0.7142982     0.0791842
 0.6909062     0.0849452
 0.6675955     0.0902334
 0.6443157     0.0950530
 0.6210077     0.0994086
 0.5976155     0.1033104
 0.5741033     0.1067704
 0.5504558     0.1097981
 0.5266740     0.1124031
 0.5027839     0.1145914
 0.4788216     0.1163647
 0.4548339     0.1177244
 0.4308940     0.1186644
 0.4070451     0.1191690
 0.3832867     0.1192302
 0.3596416     0.1188447
 0.3361549     0.1180073
 0.3128750     0.1167072
 0.2898298     0.1149313
 0.2670436     0.1126721
 0.2445728     0.1099286
 0.2225073     0.1067006
 0.2009441     0.1029850
 0.1799680     0.0987808
 0.1596995     0.0941120
 0.1403288     0.0890216
 0.1220556     0.0835674
 0.1050817     0.0778315
 0.0895859     0.0719246
 0.0757089     0.0659851
 0.0635164     0.0601440
 0.0529609     0.0544931
 0.0439060     0.0490943
 0.0361794     0.0439863
 0.0296006     0.0391763
 0.0239969     0.0346545
 0.0192170     0.0304007
 0.0151340     0.0263895
 0.0116467     0.0225925
 0.0086870     0.0189712
 0.0062058     0.0154863
 0.0041396     0.0121274
 0.0024387     0.0088917
 0.0011249     0.0057470
 0.0005111     0.0036795
 0.0002956     0.0026546
 0.0001444     0.0016368
 0.0000000     0.0000000
 0.0000732    -0.0033344
 0.0003224    -0.0052847
 0.0005253    -0.0062484
 0.0007913    -0.0071995
 0.0011270    -0.0081316
 0.0015386    -0.0090365
 0.0020315    -0.0099069
 0.0026069    -0.0107343
 0.0032641    -0.0115095
 0.0040024    -0.0122247
 0.0048172    -0.0128750
 0.0057020    -0.0134557
 0.0066512    -0.0139647
 0.0076586    -0.0144080
 0.0087170    -0.0147934
 0.0098213    -0.0151289
 0.0121629    -0.0156765
 0.0160127    -0.0162722
 0.0203174    -0.0166809
 0.0251704    -0.0169573
 0.0307077    -0.0170976
 0.0371267    -0.0170789
 0.0447105    -0.0168835
 0.0538609    -0.0164863
 0.0651185    -0.0158460
 0.0791218    -0.0148852
 0.0964621    -0.0135084
 0.1173137    -0.0116815
 0.1411530    -0.0094668
 0.1669774    -0.0070044
 0.1937839    -0.0044371
 0.2208949    -0.0018732
 0.2480412     0.0006298
 0.2751500     0.0030496
 0.3021781     0.0053706
 0.3291109     0.0075769
 0.3559543     0.0096567
 0.3827039     0.0116006
 0.4093341     0.0133929
 0.4358625     0.0150181
 0.4622963     0.0164689
 0.4886091     0.0177316
 0.5148084     0.0187870
 0.5409500     0.0196233
 0.5670984     0.0202439
 0.5932355     0.0206558
 0.6193428     0.0208512
 0.6454416     0.0208262
 0.6715560     0.0205806
 0.6977112     0.0201197
 0.7238936     0.0194539
 0.7500470     0.0185860
 0.7761535     0.0175065
 0.8022314     0.0162106
 0.8282915     0.0146975
 0.8543374     0.0129730
 0.8802830     0.0110562
 0.9059293     0.0089751
 0.9307780     0.0068061
 0.9538566     0.0046630
 0.9740739     0.0026703
 0.9908034     0.0009545
 1.0000000     0.0000000

実行

myproject.py,myproject.xlsm,DAE31.dat を同じディレクトリに置き,マクロ有効ブックを開いてxlwings > Import Functions をクリックする

「Alt+F11」でVBEを開くと,「xlwings_udfs」というモジュールが作成され,define と xf_a という Function が作成されていることがわかる

VBEを閉じて,「Alt+F8」でマクロウィンドウを開き,SampleCall > Run をクリックしてマクロを実行する

できた

ちなみにVisual Studio Codeのターミナルからも実行できる

C:\Users\XXX\Desktop\xfoil\myproject>py myproject.py

PYTHONPATH と UDF Modules

クイックスタートで作成したpyとxlsmは次のように設定されている

quickstart で作成した場合、デフォルトの設定では、Pythonのソース ファイルは次のようになっていなければなりません:
・Excelファイルと同じディレクトリ
・Excelファイルと同じ名前で、拡張子が .xlsm ではなく .py

ユーザー定義関数 (UDFs) - xlwings Documentation

実際には,Excelファイルと違うディレクトリにあるExcelファイルと違う名前の python のファイルを使いたいことが多い

PYTHONPATH: コードのソースファイルが見つからない場合、ここにディレクトリーのパスを追加してください。

UDF Modules: UDFをインポートするPythonモジュールの名前(.py拡張子を除く)。モジュールが複数の場合は";"で区切ります。例: UDF_MODULES = "common_udfs;myproject" 何も入力しなければ、Excel スプレッドシートと同じディレクトリにある同じ名前のファイル(拡張子は .py)をインポートします。

アドインおよび設定 - xlwings Documentation

例えば,「C:\Users\XXX\Desktop\xfoil\src」というディレクトリにある「xfoil_a.py」というファイルを参照したいとすると xlwings > PYTHONPATH に「C:\Users\XXX\Desktop\xfoil\src」を入力し,xlwings > UDF modules に「xfoil_a」を入力する

もう一度 xlwings > Import Functions をクリックすれば,マクロが実行できるようになる

まとめ

Excel から xfoil-python を呼び出して2次元翼型を解析する方法について説明した

エクセルで3次元翼解析のプログラムを組みたい時に役立つのではないだろうか

↓まとめ記事

コメント