diff --git a/pyNastran/bdf/bdf_interface/dev/dedemap.py b/pyNastran/bdf/bdf_interface/dev/dedemap.py new file mode 100644 index 000000000..6f3fe8dc5 --- /dev/null +++ b/pyNastran/bdf/bdf_interface/dev/dedemap.py @@ -0,0 +1,56 @@ +import os +import sys +from collections import defaultdict +bdf_filename = sys.argv[1] +base, ext = os.path.splitext(bdf_filename) +bdf_filename_out = base + '.dedmap' + ext + +with open(bdf_filename, 'r') as bdf_file: + lines = bdf_file.readlines() +nlines = len(lines) + +blocks = defaultdict(list) +i = 0 +line = lines[i] +while 'N A S T R A N S O U R C E P R O G R A M C O M P I L A T I O N' not in line: + print(i, line.strip()) + i += 1 + line = lines[i] + +while i < nlines and 'N A S T R A N S O U R C E P R O G R A M C O M P I L A T I O N' in line: + line = lines[i] + print('*', i, line) + source, dmap = line.strip().split('SUBDMAP =') + #dmap = dmap.strip() + print(f'dmap = {dmap!r}') + + blocks[dmap].append(line) + i += 1 + line = lines[i] + + blocks[dmap].append(line) + while 'PAGE' not in line: + blocks[dmap].append(line) + i += 1 + line = lines[i] + for j in range(3): + i += 1 + line = lines[i] + print('**', i, line.strip()) + # i += 2 + # line = lines[i] + # print('**', i, line) + print('--------------------------') + + +with open(bdf_filename_out, 'w') as bdf_file_out: + for block_name, block in blocks.items(): + bdf_file_out.write(f'0 N A S T R A N S O U R C E P R O G R A M C O M P I L A T I O N SUBDMAP = {block_name}\n') + bdf_file_out.write(f' OLD NO. NEW NO. ( *I* = INSERTED, *D* = DELETED )\n') + for line in block: + if (line.startswith(' DMAP-DMAP INSTRUCTION') or + 'N A S T R A N S O U R C E P R O G R A M C O M P I L A T I O N' in line or + 'OLD NO. NEW NO. ( *I* = INSERTED, *D* = DELETED )' in line): + continue + bdf_file_out.write(line) +print(f'finished writing {bdf_filename_out}') diff --git a/pyNastran/bdf/test/run_jobs.py b/pyNastran/bdf/test/run_jobs.py new file mode 100644 index 000000000..7c1ba9988 --- /dev/null +++ b/pyNastran/bdf/test/run_jobs.py @@ -0,0 +1,56 @@ +import os +from pathlib import Path +import sys + +import argparse +import pyNastran + +def run_jobs_cmd_line(argv=None, quiet: bool=False): + """ + run_nastran_job dirname + run_nastran_job filename -x C:\bin\nastran.exe + """ + if argv is None: + argv = sys.argv + parser = argparse.ArgumentParser() + + parser.add_argument("bdf_dirname_filename", help='path to Nastran filename') + parser.add_argument('-x', '--exe', help='path to Nastran execuable') + #parent_parser.add_argument('-h', '--help', help='show this help message and exits', action='store_true') + parser.add_argument('-v', '--version', action='version', + version=pyNastran.__version__) + + args = parser.parse_args(args=argv[1:]) + if not quiet: # pragma: no cover + print(args) + + bdf_filename_dirname = Path(args.bdf_dirname_filename) + nastran_exe = args.exe + if nastran_exe is None: + nastran_exe = 'nastran' + elif '.bat' in nastran_exe or '.exe' in nastran_exe: + nastran_exe = Path(nastran_exe) + assert nastran_exe.exists(), nastran_exe + assert nastran_exe.is_file(), nastran_exe + + assert bdf_filename_dirname.exists(), bdf_filename_dirname + + from pyNastran.utils.nastran_utils import run_nastran + if bdf_filename_dirname.is_dir(): + dirname = bdf_filename_dirname + extensions = ['.dat', '.bdf'] + bdf_filenames = [dirname / fname.name for fname in dirname.iterdir() + if fname.suffix in extensions and '.test_bdf.' not in fname.name] + print('bdf_filenames =', bdf_filenames) + assert len(bdf_filenames) > 0, dirname + for bdf_filename in bdf_filenames: + run_nastran(bdf_filename, nastran_cmd=nastran_exe, cleanup=True) + + + elif bdf_filename_dirname.is_file(): + run_nastran(bdf_filename_dirname, nastran_cmd=nastran_exe, cleanup=True) + else: + raise NotImplementedError(bdf_filename_dirname) + +if __name__ == '__main__': + run_jobs_cmd_line() diff --git a/pyNastran/converters/nastran/gui/menus/element_menu.py b/pyNastran/converters/nastran/gui/menus/element_menu.py index 4f3fb8d56..ccc1e7550 100644 --- a/pyNastran/converters/nastran/gui/menus/element_menu.py +++ b/pyNastran/converters/nastran/gui/menus/element_menu.py @@ -335,31 +335,31 @@ def main(): # pragma: no cover 'icase_disp' : 2, 'icase_vector' : 3, - 'name' : 'cat', - 'time' : 2, - 'frames/sec' : 30, - 'resolution' : 1, - 'iframe' : 0, - 'is_scale' : False, - 'dirname' : os.getcwd(), - 'scale' : 2.0, - 'default_scale' : 10, - - 'arrow_scale' : 3.0, - 'default_arrow_scale' : 30, + 'name': 'cat', + 'time': 2, + 'frames/sec': 30, + 'resolution': 1, + 'iframe': 0, + 'is_scale': False, + 'dirname': os.getcwd(), + 'scale': 2.0, + 'default_scale': 10, + + 'arrow_scale': 3.0, + 'default_arrow_scale': 30, #'phase' : 0., - 'phase' : None, - 'default_phase' : 120., - #'default_phase' : None, - - #'start_time' : 0., - #'end_time' : 0., - 'default_time' : 0., - 'icase_start' : 10, - 'icase_delta' : 3, - 'stress_min' : 0., - 'stress_max' : 1000., + 'phase': None, + 'default_phase': 120., + #'default_phase': None, + + #'start_time': 0., + #'end_time': 0., + 'default_time': 0., + 'icase_start': 10, + 'icase_delta': 3, + 'stress_min': 0., + 'stress_max': 1000., } data2['phase'] = 0. # uncomment for phase diff --git a/pyNastran/gui/gui_objects/settings.py b/pyNastran/gui/gui_objects/settings.py index 035ff889e..56a67e1b5 100644 --- a/pyNastran/gui/gui_objects/settings.py +++ b/pyNastran/gui/gui_objects/settings.py @@ -106,6 +106,9 @@ MAGNIFY_MIN = 1 MAGNIFY_MAX = 10 +ANIMATION_FRAME_RATE = 30 +ANIMATION_TIME = 2.0 + PLOTEL_COLOR = RED_FLOAT CAERO_COLOR = YELLOW_FLOAT RBE_LINE_COLOR = LIGHT_GREEN_FLOAT @@ -469,6 +472,9 @@ def reset_settings(self, resize: bool=True, self.coord_text_scale = COORD_TEXT_SCALE # float self.coord_linewidth = 2.0 + self.animation_frame_rate = ANIMATION_FRAME_RATE + self.animation_time = ANIMATION_TIME + # string self.colormap = 'jet' # 'viridis' @@ -683,6 +689,12 @@ def load(self, settings: QSettings) -> bool: self.shear_moment_torque_line_width, min_value=LINE_WIDTH_MIN, max_value=LINE_WIDTH_MAX) + # animation + self._set_setting(settings, setting_keys, ['animation_time'], + default=ANIMATION_TIME, save=True, auto_type=float) + self._set_setting(settings, setting_keys, ['animation_frame_rate'], + default=ANIMATION_FRAME_RATE, save=True, auto_type=int) + # displacement_model_scale - unused self._set_setting(settings, setting_keys, ['displacement_model_scale'], default=DISPLACEMENT_MODEL_SCALE, save=True, auto_type=float) @@ -892,6 +904,8 @@ def save(self, settings: QSettings, # float settings.setValue('displacement_model_scale', self.displacement_model_scale) + settings.setValue('animation_time', self.animation_time), + settings.setValue('animation_frame_rate', self.animation_frame_rate), # logging settings.setValue('show_info', self.show_info) diff --git a/pyNastran/gui/menus/calculator/calculator.py b/pyNastran/gui/menus/calculator/calculator.py index 0cb774431..7615cb391 100644 --- a/pyNastran/gui/menus/calculator/calculator.py +++ b/pyNastran/gui/menus/calculator/calculator.py @@ -374,36 +374,36 @@ def main(): # pragma: no cover #from pyNastran.gui.menus.legend.animation import AnimationWindow data2 = { - 'font_size' : 8, - 'icase_fringe' : 1, - 'icase_disp' : 2, - 'icase_vector' : 3, - - 'title' : 'cat', - 'time' : 2, - 'frames/sec' : 30, - 'resolution' : 1, - 'iframe' : 0, - 'is_scale' : False, - 'dirname' : os.getcwd(), - 'scale' : 2.0, - 'default_scale' : 10, - - 'arrow_scale' : 3.0, - 'default_arrow_scale' : 30, - - #'phase' : 0., - 'phase' : None, - 'default_phase' : 120., - #'default_phase' : None, - - #'start_time' : 0., - #'end_time' : 0., - 'default_time' : 0., - 'icase_start' : 10, - 'icase_delta' : 3, - 'stress_min' : 0., - 'stress_max' : 1000., + 'font_size': 8, + 'icase_fringe': 1, + 'icase_disp': 2, + 'icase_vector': 3, + + 'title': 'cat', + 'time': 2, + 'frames/sec': 30, + 'resolution': 1, + 'iframe': 0, + 'is_scale': False, + 'dirname': os.getcwd(), + 'scale': 2.0, + 'default_scale': 10, + + 'arrow_scale': 3.0, + 'default_arrow_scale': 30, + + #'phase': 0., + 'phase': None, + 'default_phase': 120., + #'default_phase': None, + + #'start_time': 0., + #'end_time': 0., + 'default_time': 0., + 'icase_start': 10, + 'icase_delta': 3, + 'stress_min': 0., + 'stress_max': 1000., } data2['phase'] = 0. # uncomment for phase diff --git a/pyNastran/gui/menus/legend/animation.py b/pyNastran/gui/menus/legend/animation.py index ceba588e4..440489897 100644 --- a/pyNastran/gui/menus/legend/animation.py +++ b/pyNastran/gui/menus/legend/animation.py @@ -5,7 +5,7 @@ """ from __future__ import annotations import os -from typing import TYPE_CHECKING +from typing import Any, TYPE_CHECKING from qtpy.QtCore import Qt from qtpy.QtWidgets import ( @@ -1306,6 +1306,9 @@ def on_validate(self, wipe: bool=False) -> tuple[bool, scale, flag1 = check_float(self.scale_edit) time, flag2 = check_float(self.time_edit) fps, flag3 = check_float(self.fps_edit) + self.time = time + self.fps = fps + self._set_settings() min_value = max_value = None flag4 = flag5 = True @@ -1343,8 +1346,27 @@ def on_validate(self, wipe: bool=False) -> tuple[bool, #self.close() ##self.destroy() + def settings(self) -> dict[str, Any]: + if self.is_gui: + out = self.win_parent.settings + else: + out = {} + return out + + def _set_settings(self) -> None: + if not self.is_gui: + return + time, flag2 = check_float(self.time_edit) + fps, flag3 = check_float(self.fps_edit) + if flag2 and flag3: + self._default_time = time + self._default_fps = fps + self.settings.animation_time = time + self.settings.animation_frame_rate = fps + def on_cancel(self) -> None: """click the Cancel button""" + self._set_settings() self.on_stop() self.out_data['close'] = True self.close() @@ -1370,36 +1392,36 @@ def main(): # pragma: no cover #from pyNastran.gui.menus.legend.animation import AnimationWindow data2 = { - 'font_size' : 8, - 'icase_fringe' : 1, - 'icase_disp' : 2, - 'icase_vector' : 3, - - 'title' : 'cat', - 'time' : 2, - 'frames/sec' : 30, - 'resolution' : 1, - 'iframe' : 0, - 'is_scale' : False, - 'dirname' : os.getcwd(), - 'scale' : 2.0, - 'default_scale' : 10, - - 'arrow_scale' : 3.0, - 'default_arrow_scale' : 30, - - #'phase' : 0., - 'phase' : None, - 'default_phase' : 120., - #'default_phase' : None, - - #'start_time' : 0., - #'end_time' : 0., - 'default_time' : 0., - 'icase_start' : 10, - 'icase_delta' : 3, - 'stress_min' : 0., - 'stress_max' : 1000., + 'font_size': 8, + 'icase_fringe': 1, + 'icase_disp': 2, + 'icase_vector': 3, + + 'title': 'cat', + 'time': 2, + 'frames/sec': 30, + 'resolution': 1, + 'iframe': 0, + 'is_scale': False, + 'dirname': os.getcwd(), + 'scale': 2.0, + 'default_scale': 10, + + 'arrow_scale': 3.0, + 'default_arrow_scale': 30, + + #'phase': 0., + 'phase': None, + 'default_phase': 120., + #'default_phase': None, + + #'start_time': 0., + #'end_time': 0., + 'default_time': 0., + 'icase_start': 10, + 'icase_delta': 3, + 'stress_min': 0., + 'stress_max': 1000., } data2['phase'] = 0. # uncomment for phase @@ -1425,5 +1447,5 @@ def main(): # pragma: no cover app.exec_() -if __name__ == "__main__": # pragma: no cover +if __name__ == "__main__": # pragma: no cover main() diff --git a/pyNastran/gui/menus/legend/legend_object.py b/pyNastran/gui/menus/legend/legend_object.py index 15ea59216..49fb2f6b6 100644 --- a/pyNastran/gui/menus/legend/legend_object.py +++ b/pyNastran/gui/menus/legend/legend_object.py @@ -180,17 +180,17 @@ def set_animation_menu(self) -> None: arrow_scale, default_arrow_scale = self.get_legend_vector(gui.icase_vector) data = { - 'font_size' : self.settings.font_size, - 'icase_fringe' : gui.icase_fringe, - 'icase_disp' : gui.icase_disp, - 'icase_vector' : gui.icase_vector, - 'title' : legend_title, - 'time' : 2, - 'frames/sec' : 30, - 'resolution' : 1, - 'iframe' : 0, - 'scale' : scale, - 'default_scale' : default_scale, + 'font_size': self.settings.font_size, + 'icase_fringe': gui.icase_fringe, + 'icase_disp': gui.icase_disp, + 'icase_vector': gui.icase_vector, + 'title': legend_title, + 'time': self.settings.animation_time, + 'frames/sec': self.settings.animation_frame_rate, + 'resolution': 1, + 'iframe': 0, + 'scale': scale, + 'default_scale': default_scale, 'arrow_scale' : arrow_scale, 'default_arrow_scale' : default_arrow_scale, diff --git a/pyNastran/gui/menus/legend/qt_legend.py b/pyNastran/gui/menus/legend/qt_legend.py index 3cd96efab..f564cd5ba 100644 --- a/pyNastran/gui/menus/legend/qt_legend.py +++ b/pyNastran/gui/menus/legend/qt_legend.py @@ -5,7 +5,7 @@ """ from __future__ import annotations import os -from typing import Optional, TYPE_CHECKING +from typing import Optional, Any, TYPE_CHECKING from qtpy import QtCore from qtpy.QtGui import QFont @@ -130,6 +130,16 @@ def __init__(self, data: dict[str, Any], self.set_connections() self.set_font_size(data['font_size']) + @property + def settings(self) -> dict[str, Any]: + if not self.is_gui: + data = { + 'animation_frame_rate': 30, + 'animation_time': 2.0, + } + return data + return self.win_parent.settings + def _update_defaults_to_blank(self): """Changes the default (None) to a blank string""" if self._default_colormap in {None, ''}: @@ -700,28 +710,29 @@ def on_animate(self) -> None: if not flag1: scale = self._scale + settings = self.settings data = { - 'font_size' : self.out_data['font_size'], - 'icase_fringe' : self._icase_fringe, - 'icase_disp' : self._icase_disp, - 'icase_vector' : self._icase_vector, - 'title' : title, - 'time' : 2, - 'frames/sec' : 30, - 'resolution' : 1, - 'iframe' : 0, - 'scale' : scale, - 'default_scale' : self._default_scale, - - 'arrow_scale' : scale, - 'default_arrow_scale' : self._default_arrow_scale, - - 'is_scale' : self._default_phase is None, - 'phase' : self._phase, - 'default_phase' : self._default_phase, - 'dirname' : os.path.abspath(os.getcwd()), - 'clicked_ok' : False, - 'close' : False, + 'font_size': self.out_data['font_size'], + 'icase_fringe': self._icase_fringe, + 'icase_disp': self._icase_disp, + 'icase_vector': self._icase_vector, + 'title': title, + 'time': settings.animation_time, + 'frames/sec': settings.animation_frame_rate, + 'resolution': 1, + 'iframe': 0, + 'scale': scale, + 'default_scale': self._default_scale, + + 'arrow_scale': scale, + 'default_arrow_scale': self._default_arrow_scale, + + 'is_scale': self._default_phase is None, + 'phase': self._phase, + 'default_phase': self._default_phase, + 'dirname': os.path.abspath(os.getcwd()), + 'clicked_ok': False, + 'close': False, } self.win_parent.legend_obj.set_animation_window(data) @@ -935,45 +946,45 @@ def main(): # pragma: no cover app = QApplication(sys.argv) #The Main window data1 = { - 'icase_fringe' : 1, - 'icase_disp' : 62, - 'icase_vector' : 3, + 'icase_fringe': 1, + 'icase_disp': 62, + 'icase_vector': 3, 'is_fringe': True, # False=normals or no fringe - 'font_size' : 8, - 'title' : 'asdf', - 'min_value' : 0., - 'max_value' : 10, + 'font_size': 8, + 'title': 'asdf', + 'min_value': 0., + 'max_value': 10, 'filter_value': None, - 'scale' : 1e-12, - 'default_scale' : 1.0, + 'scale': 1e-12, + 'default_scale': 1.0, - 'arrow_scale' : 2e-2, - 'default_arrow_scale' : 2.0, + 'arrow_scale': 2e-2, + 'default_arrow_scale': 2.0, - 'phase' : 0.0, - #'default_phase' : 180.0, - 'default_phase' : None, + 'phase': 0.0, + #'default_phase': 180.0, + 'default_phase': None, - 'nlabels' : 11, - 'default_nlabels' : 11, + 'nlabels': 11, + 'default_nlabels': 11, - 'labelsize' : 12, - 'default_labelsize' : 12, + 'labelsize': 12, + 'default_labelsize': 12, - 'ncolors' : 13, - 'default_ncolors' : 13, + 'ncolors': 13, + 'default_ncolors': 13, - 'colormap' : 'jet', - 'default_colormap' : 'jet', + 'colormap': 'jet', + 'default_colormap': 'jet', - 'default_format' : '%s', - 'format' : '%g', + 'default_format': '%s', + 'format': '%g', 'is_low_to_high': True, - 'is_discrete' : False, - 'is_horizontal' : False, - 'is_shown' : True, + 'is_discrete': False, + 'is_horizontal': False, + 'is_shown': True, } main_window = LegendPropertiesWindow(data1) @@ -981,5 +992,6 @@ def main(): # pragma: no cover # Enter the main loop app.exec_() -if __name__ == "__main__": # pragma: no cover + +if __name__ == "__main__": # pragma: no cover main() diff --git a/pyNastran/gui/utils/qt/checks/qlineedit.py b/pyNastran/gui/utils/qt/checks/qlineedit.py index 9266756a1..8d525390e 100644 --- a/pyNastran/gui/utils/qt/checks/qlineedit.py +++ b/pyNastran/gui/utils/qt/checks/qlineedit.py @@ -5,7 +5,7 @@ from pyNastran.gui.utils.qt.checks.utils import (check_locale_float, is_ranged_value, check_format_str) if TYPE_CHECKING: # pragma: no cover - from qtpy.QtWidgets import QLineEdit + from qtpy.QtWidgets import QLineEdit, QSpinBox QLINEEDIT_ERROR = "QLineEdit{background: red;}" QLINEEDIT_GOOD = "QLineEdit{background: white;}" @@ -89,7 +89,7 @@ def check_positive_int_or_blank(cell: QLineEdit) -> tuple[int, bool]: #cell.setStyleSheet(QLINEEDIT_ERROR) #return None, False -def check_float(cell: QLineEdit) -> tuple[float, bool]: +def check_float(cell: QLineEdit | QSpinBox) -> tuple[float, bool]: """ Colors the cell red if the float is invalid diff --git a/pyNastran/utils/nastran_utils.py b/pyNastran/utils/nastran_utils.py index d33aef6d9..1094c321e 100644 --- a/pyNastran/utils/nastran_utils.py +++ b/pyNastran/utils/nastran_utils.py @@ -1,9 +1,11 @@ import os import subprocess from typing import Optional +from pyNastran.utils import PathLike -def run_nastran(bdf_filename: str, nastran_cmd: str='nastran', +def run_nastran(bdf_filename: PathLike, + nastran_cmd: PathLike='nastran', keywords: Optional[str | list[str] | dict[str, str]]=None, run: bool=True, run_in_bdf_dir: bool=True, cleanup: bool=False) -> tuple[Optional[int], list[str]]: @@ -55,7 +57,8 @@ def run_nastran(bdf_filename: str, nastran_cmd: str='nastran', assert os.path.exists(bdf_filename), bdf_filename #assert os.path.exists(nastran_cmd), nastran_cmd - call_args = [nastran_cmd, str(bdf_filename)] + keywords_list + nastran_cmd_str = str(nastran_cmd) + call_args = [nastran_cmd_str, str(bdf_filename)] + keywords_list return_code = None if run: return_code = subprocess.call(call_args)