Skip to content

Instantly share code, notes, and snippets.

@kinonotofu
Created November 30, 2021 21:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kinonotofu/c194430047c44a05a96471ee4db6f6e4 to your computer and use it in GitHub Desktop.
Save kinonotofu/c194430047c44a05a96471ee4db6f6e4 to your computer and use it in GitHub Desktop.
butterflyでWSL上のOpenFOAMを使う。結局細かい仕様の違いがあってあまり使えないかもしれない。

1. runmanager_wsl.py

  • ここにあるrunmanager_wsl.pyをC:\Users%USERNAME%\AppData\Roaming\McNeel\Rhinoceros\6.0\scripts\butterflyに配置
  • 49行目はOpenFOAMのバージョンやインストールディレクトリに応じての適宜修正する。

2.config.yaml

  • C:\Users%USERNAME%\AppData\Roaming\McNeel\Rhinoceros\6.0\scripts\butterfly\config.yamlの6行目に以下の記述を挿入
  - WSL

3.case.py

  • C:\Users%USERNAME%\AppData\Roaming\McNeel\Rhinoceros\6.0\scripts\butterfly\case.pyの55-56行目に以下の記述を挿入
elif butterfly.config['runner'] == 'WSL':
    from .runmanager_wsl import RunManagerWSL as RunManager

4.init.py

  • C:\Users%USERNAME%\AppData\Roaming\McNeel\Rhinoceros\6.0\scripts\butterfly_init_.pyの7行に以下の記述を挿入
        'WSL': '{}'.format(os.path.expanduser("~")),
  • if not _ofrunners:の上(上記で編集したdictの下)に以下の記述を追加(ダミーのフォルダなので実際は使わない)
    if not os.path.exists(os.path.expanduser("~") + "\\WSL"):
        os.mkdir(os.path.expanduser("~") + "\\WSL")

5.version.py

  • C:\Users%USERNAME%\AppData\Roaming\McNeel\Rhinoceros\6.0\scripts\butterfly\version.pyは気が向いたらいじる

6.元に戻す方法

  • OpenFOAMのバージョンによりデフォルトの境界条件では動かないので注意。
  • blueCFDに戻すにはC:\Users%USERNAME%\AppData\Roaming\McNeel\Rhinoceros\6.0\scripts\butterfly\config.yamlでblueCFDをWSLより上に書けばよい。
# coding=utf-8
"""Runmanager for butterfly.
Run OpenFOAM in Windows Subsystem for Linux(WSL).
This class has been tested against OpenFOAM-v2006:
"""
import os
import ctypes
import time
import sys
from subprocess import PIPE, Popen
from collections import namedtuple
from copy import deepcopy
from .version import Version
import platform
import butterfly
class UserNotAdminError(Exception):
"""Exception for non-admin users."""
pass
class RunManagerWSL(object):
"""RunManager to write and run OpenFOAM commands through batch files."""
shellinit = None
__containerId = None
def __init__(self, project_name):
u"""Init run manager for project.
Project path will be set to: C:/Users/%USERNAME%/butterfly/project_name
Args:
project_name: A string for project name.
"""
assert os.name == 'nt', "Currently RunManager is only supported on Windows."
self._project_name = project_name
self._project_folder = r'c:\Users\{}\butterfly\{}'.format(
os.getenv('USERNAME'), self._project_name)
self.log_folder = './log'
self.errFolder = './log'
# self._pid = None
self._process = None
self._wsl_env = 'wsl source /opt/OpenFOAM/OpenFOAM-v2006/etc/bashrc ; '
@property
def process(self):
"""Return PID for the latest command."""
return self._process
@property
def is_user_admin(self):
"""Return True if user is admin."""
if ctypes.windll.shell32.IsUserAnAdmin():
return True
else:
return False
def ensure_user_is_admin(self):
"""Ensure user is logged in as admin.
If user is not admin raise UserNotAdminError.
"""
if self.is_user_admin:
raise UserNotAdminError(
'In order to run OpenFOAM using butterfly you must use an admin '
'account or run the program as administrator.')
else:
return True
def terminate(self, pid=None, force=False):
"""Kill the command using the pid."""
process = pid or self.process
if not pid:
return
process.terminate()
@property
def is_ironpython(self):
"""Check if the platform is IronPython."""
iron_python = True
try:
iron_python = True if platform.python_implementation() == 'IronPython' \
else False
except ValueError as e:
# older versions of IronPython fail to parse version correctly
# failed to parse IronPython sys.version: '2.7.5 (IronPython 2.7.5 (2.7.5.0)
# on .NET 4.0.30319.42000 (64-bit))'
if 'IronPython' in str(e):
iron_python = True
return iron_python
# TODO(): Update controlDict.application for multiple commands
def command(self, cmd, args=None, decomposeParDict=None, include_header=True):
"""Get command line for an OpenFOAM command in parallel or serial.
Args:
cmd: An OpenFOAM command.
args: List of optional arguments for command. e.g. ('c', 'latestTime')
decomposeParDict: decomposeParDict for parallel runs (default: None).
include_header: Include header lines to set up the environment
(default: True).
tee: Include tee in command line.
Returns:
(cmd, logfiles, errorfiles)
"""
if isinstance(cmd, str):
return self.__command(cmd, args, decomposeParDict, include_header)
elif isinstance(cmd, (list, tuple)):
# a list of commands
res = namedtuple('log', 'cmd logfiles errorfiles')
logs = range(len(cmd)) # create a place holder for commands
for count, c in enumerate(cmd):
if count > 0:
include_header = False
if c == 'blockMesh':
decomposeParDict = None
try:
arg = args[count]
except TypeError:
arg = args
logs[count] = self.__command(c, (arg,), decomposeParDict,
include_header)
command = tuple(log.cmd for log in logs)
logfiles = tuple(ff for log in logs for ff in log.logfiles)
errorfiles = tuple(ff for log in logs for ff in log.errorfiles)
return res(command, logfiles, errorfiles)
def __command(self, cmd, args=None, decomposeParDict=None, include_header=True):
"""Get command line for an OpenFOAM command in parallel or serial.
Args:
cmd: An OpenFOAM command.
args: List of optional arguments for command. e.g. ('-c', '-latestTime')
decomposeParDict: decomposeParDict for parallel runs (default: None).
include_header: Include header lines to set up the environment
(default: True).
tee: Include tee in command line.
Returns:
(cmd, logfiles, errorfiles)
"""
res = namedtuple('log', 'cmd logfiles errorfiles')
# join arguments for the command
arguments = '' if not args else '{}'.format(' '.join(args))
if decomposeParDict:
# run in parallel
n = decomposeParDict.numberOfSubdomains
arguments = arguments + ' -parallel'
if cmd == 'snappyHexMesh':
cmd_list = (self._wsl_env + 'decomposePar',
self._wsl_env + 'mpirun -np %s %s' % (n, cmd),
self._wsl_env + 'reconstructParMesh', 'rm')
arg_list = ('', arguments, '-constant', '-r proc*')
cmd_name_list = ('decomposePar', cmd, 'reconstructParMesh', 'rm')
else:
cmd_list = (self._wsl_env + 'decomposePar',
self._wsl_env + 'mpirun -np %s %s' % (n, cmd),
self._wsl_env + 'reconstructPar', 'rm')
arg_list = ('', arguments, '', '-r proc*')
cmd_name_list = ('decomposePar', cmd, 'reconstructPar', 'rm')
cmds = tuple(' '.join((c, arg))
for c, arg in zip(cmd_list, arg_list))
# join commands together
errfiles = tuple('{}/{}.err'.format(self.errFolder, name)
for name in cmd_name_list)
logfiles = tuple('{}/{}.log'.format(self.log_folder, name)
for name in cmd_name_list)
else:
# run is serial
cmds = (self._wsl_env + cmd,)
errfiles = ('{}/{}.err'.format(self.errFolder, cmd),)
logfiles = ('{}/{}.log'.format(self.log_folder, cmd),)
return res(cmds, logfiles, errfiles)
def _run_ironpython(self, cmds, logfiles, errfiles, wait):
"""Run commands in IronPython.
The command is running from inside Grasshopper or Dynamo
make a batch file and run the command from inside batch file
so it pops up in the screen.
"""
log = namedtuple('log', 'process logfiles errorfiles')
header = 'cd {}\n'.format(self._project_folder)
commands = [cmd + ' | wsl tee log/%s.log' % cmd.split()[0]
for cmd in cmds]
# write all the commnds in one go!
cmd = '\n'.join(commands)
with open('ir.bat', 'w') as batchfile:
batchfile.write(header)
batchfile.write(cmd)
process = Popen('ir.bat', stderr=PIPE, shell=False)
if not wait:
return log(process, logfiles, errfiles)
self._handle_process(process, logfiles[0], errfiles[0], True)
return log(process, logfiles, errfiles)
def run(self, command, args=None, decomposeParDict=None, wait=True):
"""Run OpenFOAM command."""
# get the command as a single line
cmds, logfiles, errfiles = self.command(command, args, decomposeParDict)
is_ironpython = self.is_ironpython
# run the command.
# shell should be True to run multiple commands at the same time.
log = namedtuple('log', 'process logfiles errorfiles')
# update env variables
env = os.environ.copy()
# full_path = self._env["PATH"] + env["PATH"]
# env.update(self._env)
# env["PATH"] = full_path
os.chdir(self._project_folder)
if is_ironpython:
return self._run_ironpython(cmds, logfiles, errfiles, wait)
elif not wait and len(cmds) > 1:
# put all commands in a single line otherwise it won't wait for all of them
# it is Winsows so I use &&
cmds = ['&&'.join(cmds)]
for counter, cmd in enumerate(cmds):
try:
process = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True, env=env,
encoding='utf8')
except TypeError:
# python 2
process = Popen(cmd, stdout=PIPE, stderr=PIPE,
shell=True, env=env)
if not wait:
return log(process, logfiles, errfiles)
self._handle_process(
process, logfiles[counter], errfiles[counter], False)
return log(p, logfiles, errfiles)
def _handle_process(self, process, logfile, errfile, is_ironpython):
# wait for process to finish while printing and logging
with open(logfile, 'w') as outf:
while True:
if is_ironpython:
output = ''
else:
output = process.stdout.readline()
if output == '' and process.poll() is not None:
err = process.stderr.read()
break
if output:
outf.write(output.strip())
print(output.strip())
if process.returncode != 0:
if str(err).strip():
# pass cases that the user closes the window
with open(errfile, 'w') as outf:
outf.write(err)
raise Exception(err)
else:
self.terminate()
print('The process is interrupted by user!')
else:
with open(errfile, 'w') as outf:
# create an empty file
pass
def check_file_contents(self, files, mute=False):
"""Check files for content and print them out if any.
args:
files: A list of ASCII files.
returns:
(hasContent, content)
hasContent: A boolean that shows if there is any contents.
content: Files content if any
"""
def read_file(f):
try:
with open(f, 'rb') as log:
return log.read().strip()
except Exception as e:
err = 'Failed to read {}:\n\t{}'.format(f, e)
print(err)
return ''
_lines = '\n'.join(tuple(read_file(f) for f in files)).strip()
if len(_lines) > 0:
if not mute:
print(_lines)
return True, _lines
else:
return False, _lines
def duplicate(self):
"""Return a copy of this object."""
return deepcopy(self)
def ToString(self):
"""Overwrite .NET ToString method."""
return self.__repr__()
def __repr__(self):
"""Run manager representation."""
return """RunManager::{}""".format(self._project_name)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment