Source code for pyNastran.utils.nastran_utils

import os
import warnings
import subprocess
from typing import Optional
from pyNastran.utils import PathLike, print_bad_path
from cpylog import SimpleLogger

KEYWORDS_CHECK = {
    'old': ['yes', 'no'],     # throw away old results
    'bat': ['yes', 'no'],     # linux only
    'news': ['yes', 'no', 'auto'],    # doesn't seem to work, but shrinks f06 size
    'notify': ['yes', 'no'],  # make nastran beep
    'fpe': ['yes', 'no'],  # ???
    'msgbell': ['yes', 'no'],  # ???
    'scr': ['yes', 'no', 'mini', 'post'],
    'endian': ['big', 'little'],
    #'mem=15gb'
    #'parallel=4'
}
# auth = 28000@LocalHost
# memory = 0.45*physical
# smem = 20.0X
# buffpool = 20.0X
# ishellpath = %NXN_BASE%\bin
# diag = 8
# program=FEMAP


[docs] def run_nastran(bdf_filename: PathLike, nastran_cmd: PathLike='nastran', keywords: Optional[str | list[str] | dict[str, str]]=None, run: bool=True, run_if_op2_exists: bool=True, run_in_bdf_dir: bool=True, cleanup: bool=False, debug: bool=False, log: Optional[SimpleLogger]=None) -> tuple[Optional[int], list[str]]: """ Call a nastran subprocess with the given filename Parameters ---------- bdf_filename: string Filename of the Nastran .bdf file nastran_cmd: str; default='nastran' path to Nastran executable keywords: str/dict/list of strings, optional Default keywords are `'scr=yes'`, `'bat=no'`, `'old=no'`, and `'news=no'` run: bool; default=True lets you disable actually running Nastran to test out code/get the call arguments run_in_bdf_dir: bool; default=True True : output (e.g., *.f06) will go to the current working directory (default) False : outputs (e.g., *.f06) will go to the input BDF directory cleanup: bool; default=False removes the *.asg, *.asm, *.log, *.f04, *.mon1, *.mon2 and *.plt files Returns ------- return_code : int the nastran flag cmd_args : list[str] the nastran commands that go into subprocess Example ------- keywords_str = 'scr=yes old=no mem=1024mb' keywords_list = ['scr=yes', 'old=no', 'mem=1024mb'] keywords_dict = { 'scr' : 'yes', 'old' : 'no', 'mem' : '1024mb', } run_nastran(bdf_filename, keywords_str) run_nastran(bdf_filename, keywords_list) run_nastran(bdf_filename, keywords_dict) """ keywords_list = _get_keywords_list( keywords=keywords, log=log) assert os.path.exists(bdf_filename), bdf_filename base = os.path.basename(bdf_filename)[0] op2_filename = os.path.splitext(bdf_filename)[0] + '.op2' pwd = os.getcwd() bdf_directory = os.path.dirname(bdf_filename) switch_dir = bdf_directory != '' and run_in_bdf_dir if switch_dir: os.chdir(bdf_directory) if nastran_cmd != 'nastran': assert os.path.exists(nastran_cmd), nastran_cmd nastran_cmd_str = str(nastran_cmd) call_args = [nastran_cmd_str, str(bdf_filename)] + keywords_list if '=' in str(bdf_filename): msg1 = f'Nastran cant run files with an = sign in them; {str(bdf_filename)}' if log: log.debug(msg1) else: warnings.warn(msg1) if debug: msg2 = f'call_args = {call_args}' if log: log.debug(msg2) else: print(msg2) return_code = None if run and (not os.path.exists(op2_filename) or run_if_op2_exists): try: return_code = subprocess.call(call_args) except FileNotFoundError: # pragma: no cover print(call_args) raise if run and cleanup: fnames = [ base + '.asg', base + '.asm', base + '.f04', base + '.log', base + '.plt', base + '.mon1', base + '.mon2', ] for fname in fnames: if os.path.exists(fname): print(f'removing {fname}') os.remove(fname) if switch_dir: os.chdir(pwd) return return_code, call_args
def _get_keywords_list(keywords: Optional[str | list[str] | dict[str, str]]=None, log: Optional[SimpleLogger]=None) -> list[str]: if keywords is None: keywords_list = ['scr=yes', 'bat=no', 'old=no', 'news=no', 'notify=no'] # 'mem=1024mb', elif isinstance(keywords, str): keywords_list = keywords.split() else: if isinstance(keywords, (list, tuple)): keywords_list = list(keywords) else: keywords_list = [] for keyword, value in keywords.items(): if value is None: continue keywords_list.append(f'{keyword}={value}') #print(f'keywords_list = {keywords_list}') allowed_keywords = list(KEYWORDS_CHECK) + [ 'parallel', 'mem', 'sdirectory', 'sdir', 'buffsize'] # CHEMIN={path} allowed_keywords.sort() for keyword_value in keywords_list: keyword, value = keyword_value.split('=', 1) keyword = keyword.lower() if keyword in {'parallel', 'buffsize'}: # parallel=number of processors (should check this...) # buffsize: default=32769 (size of buffer; larger=better by 2x) # authqueue: default=100000 (time in queue; seconds?) value_int = int(value) elif keyword == 'mem': assert value.upper().endswith(('MB', 'GB')), value num_str = value[:-2] num = float(num_str) elif keyword in {'sdirectory', 'sdir'}: # Directory for scratch files path = value assert os.path.exists(path), print_bad_path(path) elif keyword in {'auth', 'authorize'}: pass elif keyword in KEYWORDS_CHECK: expected_values = KEYWORDS_CHECK[keyword] assert value.lower() in expected_values, f'keyword={keyword} value={value}; expected={expected_values}' else: msg = f'unsupported keyword; {keyword!r}={value!r}; allowed={allowed_keywords}' if log: log.warning(msg) else: warnings.warn(msg) return keywords_list