トップ - 技術ドキュメント

OpenFOAM を Excel から実行する

更新:2017/9/9
OpenFOAM 4.1Microsoft Office 2013 Excel

概要

Bash on Ubuntu on Windows(以下 BoUoW)と OpenFOAM をインストール済みの Windows 上で、GUI から OpenFOAM の解析を実行できるプログラムを Excel(VBA, 32 ビット版)で作成します。

これによって BoUoW を意識することなく、通常の Windows 上での操作のみで OpenFOAM による解析を行なえる状態になります。

※Windows への OpenFOAM のインストール方法については「OpenFOAM の Windows 10 へのインストール」を参照。

プログラム

作成した GUI は以下の通りです。「登録」ボタンで「ジョブ」欄にジョブを登録し、「実行」ボタンを押すとジョブが実行されます。実行終了したジョブは「実行済みジョブ」欄に表示されます。OpenFOAM からの標準出力は「メッセージ」欄に出力されます。

ExcelによるキューシステムのGUI ExcelによるキューシステムのGUI

プログラム(VBA)部分は以下の通りです。簡単のためにソースコードは全て UserForm1 にまとめています。

'''
' @brief JobListにジョブを登録
Private Sub Register_Click()
    With Application.FileDialog(msoFileDialogFolderPicker)
        If .Show = True Then
            Dim path: path = .SelectedItems(1)
            
            If CreateObject("Scripting.FileSystemObject").fileExists(path & "\Allrun") Then
                JobListBox.AddItem (path)
            Else
                MsgBox "指定されたフォルダ内にAllrunファイルが無いため登録できません。"
            End If
        End If
    End With
End Sub

'''
' @brief JobList内にあるジョブを順次実行
Private Sub Run_Click()
    If JobListBox.ListCount < 1 Then
        MsgBox "ジョブがありません。"
        Exit Sub
    End If
    
    MessageTextBox.Text = "Start." & vbCrLf
    Do
        If JobListBox.ListCount < 1 Then
            Exit Do
        End If

        Dim item: item = JobListBox.List(0)
        RunOpenFOAM item, MessageTextBox
        
        JobListBox.RemoveItem (0)
        FinishedJobListBox.AddItem (item)
        MessageTextBox.Text = MessageTextBox.Text & "----------" & vbCrLf
        
        DoEvents
    Loop
    
    MessageTextBox.Text = MessageTextBox.Text & "Done." & vbCrLf

    MsgBox "全てのジョブが終了しました。"
End Sub

'''
' @brief Windows形式のパスをBash on Winsowsでマウントされるパスに変換
' @param Windows形式のパス(ex. C:\MyFolder\MyFile)
' @param Bash on Winsowsでマウントされるパス(ex. /mnt/c/MyFolder/MyFile)
' @return 常にTrue
Function WindowsPathToLinuxPath(src)
    Dim regex: Set regex = CreateObject("VBScript.RegExp")
    
    ' ドライブレターを小文字に変換
    regex.Pattern = "[A-Z][:]"
    regex.Global = False
    Set Match = regex.Execute(src)
    If 0 < Match.Count Then
        src = Replace(src, Match(0), LCase(Match(0)))
    End If
    
    ' セパレーターを/に変換
    regex.Pattern = ":*\\"
    regex.Global = True
    Dim dest: dest = regex.Replace(src, "/")
    
    ' 先頭に/mnt/をつけて返す
    WindowsPathToLinuxPath = "/mnt/" & dest
End Function

'''
' @brief 指定されたOpenFOAM 用ケースを実行
' @param windowsPath[in] 実行するOpenFOAM用ケースフォルダパス(Windows形式パス)
' @param MessageTextBox[in|out] 標準出力、標準エラーを出力するためのテキストボックス
' @return 常にTrue
Function RunOpenFOAM(ByVal windowsPath, ByRef MessageTextBox)
    linuxPath = WindowsPathToLinuxPath(windowsPath)
    
    ' Bashで実行するコマンド
    linuxCmd = _
        "source /opt/openfoam4/etc/bashrc;" & _
        "cd " & linuxPath & ";" & _
        "./Allrun;"
    
    ' コマンドプロンプトで実行するコマンド
    Dim shell: Set shell = CreateObject("WScript.Shell")
    Dim winCmd: winCmd = "C:\Windows\sysnative\bash.exe -c """ & linuxCmd & """"

    Dim message
    Dim errorMessage

    ' コマンドを実行して終了するまでループで待機
    Dim wExec: Set wExec = shell.Exec("%ComSpec% /c " & winCmd)
    Do While wExec.Status = 0
        ' 標準出力を出力
        message = wExec.StdOut.ReadAll
        If message <> "" Then
            MessageTextBox.Text = MessageTextBox.Text & message & vbCrLf
        End If
        
        ' 標準エラーを出力
        errorMessage = wExec.stderr.ReadAll
        If errorMessage <> "" Then
            MessageTextBox.Text = MessageTextBox.Text & errorMessage & vbCrLf
        End If
        
        DoEvents
    Loop

    ' 標準出力に文字列が残っている場合を考えて出力
    message = wExec.StdOut.ReadAll
    If message <> "" Then
        MessageTextBox.Text = MessageTextBox.Text & message & vbCrLf
    End If
    
    ' 標準エラーに文字列が残っている場合を考えて出力
    errorMessage = wExec.stderr.ReadAll
    If errorMessage <> "" Then
        MessageTextBox.Text = MessageTextBox.Text & errorMessage & vbCrLf
    End If
    
    RunOpenFOAM = True
End Function

以下で Windows 上で動作するプログラムから BoUoW を使って OpenFOAM を動作させるソースコードを描く場合の注意点を説明します。

プログラム作成上の注意点

ファイルパスの変換

Windows のファイルパスは BoUoW では「/mnt」以下にマウントされます。例えば「C:\MyFolder\MyFile」は次の様になります。

  • Windows:C:\MyFolder\MyFile
  • BoUoW:/mnt/c/MyFolder/MyFile

このため、あらかじめ Windows 上でのファイルパスを BoUoW 用に変換しておく必要があり、上記プログラムでは WindowsPathToLinuxPath() という関数にその処理をまとめています。

Linux コマンドの実行

BoUoW を端末として起動せずにコマンド単位で実行させる場合には「-c」オプションを使用します。またプログラムから実行する場合、使用する bash はフルパスで「C:\Windows\sysnative\bash.exe」を指定します。

従って実行したい linux コマンドに対して、実際に実行すべき Windows コマンドは以下の様になります。

C:\Windows\sysnative\bash.exe -c "(実行したい Linux コマンド)"

※ファイルパス上の bash.exe の実体は「C:\Windows\System32\bash.exe」ですが、32 ビット版の Excel では自動的に「C:\Windows\SysWOW64\bash.exe」にリダイレクトされます。これを防ぐために仮想ディレクトリ sysnative を使用します。

VBA の場合にはこのコマンドを WshShell オブジェクトの Exec メソッドを使用して実行し、返される WshScriptExec オブジェクトを使用して標準出力、標準エラーを読み取っています。

まとめ

以上で Windows 上で動作するプログラムから BoUoW 上で動作する Linux 用プログラムである OpenFOAM を実行することができました。ただし今回、使用したプログラム開発環境である Excel(VBA)の性質上、次の様な問題があります。

  1. OpenFOAM での計算中にプログラムが固まってしまう
  2. OpenFOAM での計算中にコマンドプロンプトウィンドウが表示されてしまう

こうした問題を解決するためにはワーカースレッドを使用でき、 WshShell オブジェクト以外の仕組みで外部プログラムを実行できる機能が必要です。

今回は簡単のために Excel を使用しましたが、 Excel では上記の問題を解消するための機能を作りこむことが難しいので、実際の使用に耐えるソフトウェアを作成する場合は .NET Framework(C#、VB.NET)、Qt(C++)、PySide(Python)といった本格的な GUI フレームワークを使用した方がいいでしょう。

参照