Skip to content

Commit

Permalink
Now support overwriting cards on a card by card basis. Only implement…
Browse files Browse the repository at this point in the history
…ed GRID & masses so far
  • Loading branch information
SteveDoyle2 committed Jan 14, 2025
1 parent a2a0a63 commit c84ffe1
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 28 deletions.
16 changes: 10 additions & 6 deletions pyNastran/bdf/bdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import os
import sys
from copy import deepcopy
from functools import partial
from collections import Counter
from io import StringIO, IOBase
from pathlib import PurePath
Expand Down Expand Up @@ -621,6 +622,9 @@ def __init__(self, debug: str | bool | None=True,
# False: use strict parser (default)
self.is_strict_card_parser = True

# set of result types that overwrites work on
self.allow_overwrites_set: set[str] = set([])

# lines that were rejected b/c they were for a card that isn't supported
self.reject_lines: list[list[str]] = []

Expand Down Expand Up @@ -2241,7 +2245,7 @@ def add_card(cls, card: BDFCard, comment: str=''):
#'RADBND' : (Crash, None),

# nodes
'GRID' : (GRID, add_methods._add_node_object),
'GRID': (GRID, partial(add_methods._add_node_object, allow_overwrites='GRID' in self.allow_overwrites_set)),
'SPOINT' : (SPOINTs, add_methods._add_spoint_object),
'EPOINT' : (EPOINTs, add_methods._add_epoint_object),
'RINGAX' : (RINGAX, add_methods._add_ringax_object),
Expand Down Expand Up @@ -2441,11 +2445,11 @@ def add_card(cls, card: BDFCard, comment: str=''):
'NSM1' : (NSM1, add_methods._add_nsm_object),
'NSML1' : (NSML1, add_methods._add_nsm_object),

'CONM1' : (CONM1, add_methods._add_mass_object),
'CONM2' : (CONM2, add_methods._add_mass_object),
'CMASS1' : (CMASS1, add_methods._add_mass_object),
'CMASS2' : (CMASS2, add_methods._add_mass_object),
'CMASS3' : (CMASS3, add_methods._add_mass_object),
'CONM1': (CONM1, partial(add_methods._add_mass_object, allow_overwrites='CONM1' in self.allow_overwrites_set)),
'CONM2': (CONM2, partial(add_methods._add_mass_object, allow_overwrites='CONM2' in self.allow_overwrites_set)),
'CMASS1': (CMASS1, partial(add_methods._add_mass_object, allow_overwrites='CMASS1' in self.allow_overwrites_set)),
'CMASS2': (CMASS2, partial(add_methods._add_mass_object, allow_overwrites='CMASS2' in self.allow_overwrites_set)),
'CMASS3': (CMASS3, partial(add_methods._add_mass_object, allow_overwrites='CMASS3' in self.allow_overwrites_set)),
# CMASS4 - added later because documentation is wrong

'MPC' : (MPC, add_methods._add_constraint_mpc_object),
Expand Down
36 changes: 28 additions & 8 deletions pyNastran/bdf/bdf_interface/add_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,6 @@ def _add_node_object(self, node: GRID, allow_overwrites: bool=False) -> None:
"""adds a GRID card"""
key = node.nid
model = self.model
#allow_overwrites = True

assert key > 0, 'nid=%s node=%s' % (key, node)
if key not in model.nodes:
Expand Down Expand Up @@ -434,15 +433,36 @@ def _add_element_object(self, elem: Element,
allow_overwrites: bool=False) -> None:
key = elem.eid
model = self.model
assert key > 0, 'eid=%s must be positive; elem=\n%s' % (key, elem)
if key in model.elements and not allow_overwrites:
if not elem == model.elements[key]:
model._duplicate_elements.append(elem)
if model._stop_on_duplicate_error:
model.pop_parse_errors()
else:
assert key > 0, 'eid=%s elem=%s' % (key, elem)
if key not in model.elements:
model.elements[key] = elem
model._type_to_id_map[elem.type].append(key)
elif elem == model.elements[key]:
pass
elif allow_overwrites:
model.log.warning(f'replacing elements:\n{model.elements[key]}with:\n{elem}')
model.elements[key] = elem

# already handled
#model._type_to_id_map[elem.type].append(key)
else:
model._duplicate_elements.append(elem)
if model._stop_on_duplicate_error:
model.pop_parse_errors()
#raise RuntimeError('eid=%s\nold_element=\n%snew_element=\n%s' % (elem.eid, model.elements[key], elem))

if 0: # pragma: no cover
key = elem.eid
model = self.model
assert key > 0, 'eid=%s must be positive; elem=\n%s' % (key, elem)
if key in model.elements and not allow_overwrites:
if not elem == model.elements[key]:
model._duplicate_elements.append(elem)
if model._stop_on_duplicate_error:
model.pop_parse_errors()
else:
model.elements[key] = elem
model._type_to_id_map[elem.type].append(key)

def _add_ao_object(self, elem_flag: CBARAO,
allow_overwrites: bool=False) -> None:
Expand Down
2 changes: 2 additions & 0 deletions pyNastran/bdf/test/test_bdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,8 @@ def run_and_compare_fems(
if is_lax_parser:
fem1.log.warning('using lax card parser')
fem1.is_strict_card_parser = False
fem1.allow_overwrites_set = {'GRID', 'CONM2'}
fem1._make_card_parser()
#fem1.use_new_deck_parser = True
if version:
map_version(fem1, version)
Expand Down
2 changes: 1 addition & 1 deletion pyNastran/bdf/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def split_comment_to_femap_comment(comment: str) -> tuple[bool, str, tuple[str,
return False, comment, ('', -1, '')

lines = comment.split('\n')
lines = [line.rstrip('$\t\n\r ') for line in lines if line.rstrip('$\t\n\r ')]
lines = [line.rstrip('$\t\n\r ') for line in lines if line.rstrip('$\t\n\r ') and ':' in line]
if len(lines) == 1:
line = lines[0]
word, idi, name = _femap_comment_to_sline(line)
Expand Down
132 changes: 120 additions & 12 deletions pyNastran/f06/dev/flutter/gui_flutter.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@

class FlutterGui(LoggableGui):
def __init__(self, f06_filename: str=''):
super().__init__(html_logging=False)
super().__init__(html_logging=True)

self._export_settings_obj = PreferencesObject(self)
self._vtk_window_obj = VtkWindowObject(self, ICON_PATH)
Expand All @@ -93,7 +93,7 @@ def __init__(self, f06_filename: str=''):
self._units_out = ''
self.units_in = ''
self.units_out = ''
self.use_dock_widgets = False
self.use_dock_widgets = self.html_logging
self.qactions = {}
self.nrecent_files_max = 20
self.recent_files = []
Expand All @@ -102,6 +102,10 @@ def __init__(self, f06_filename: str=''):

self.data = {}
self.f06_filename = ''
self.bdf_filename = ''
self.op2_filename = ''
self._bdf_filename_default = ''
self._op2_filename_default = ''
self.subcase = 0
self.x_plot_type = 'eas'
self.plot_type = 'x-damp-freq'
Expand Down Expand Up @@ -136,13 +140,15 @@ def __init__(self, f06_filename: str=''):
self.on_load_settings()
if f06_filename:
self.f06_filename_edit.setText(f06_filename)
self._set_f06_default_names(f06_filename)

self.setup_toolbar()
self._update_recent_files_actions()
self.setup_connections()
self._set_window_title()
self.on_font_size()
self.on_plot_type()
self._set_f06_default_names(self.f06_filename_edit.text())
#self.on_open_new_window()
self.show()

Expand Down Expand Up @@ -376,8 +382,26 @@ def _apply_settings(self, data: dict[str, Any]) -> None:
if self.font_size != font_size0:
self.on_set_font_size(self.font_size)

def on_enable_bdf(self) -> None:
state = self.bdf_filename_checkbox.isChecked()
self.bdf_filename_edit.setEnabled(state)
self.bdf_filename_browse.setEnabled(state)
if state:
self.bdf_filename_edit.setText(self.bdf_filename)
else:
self.bdf_filename_edit.setText(self._bdf_filename_default)

def on_enable_op2(self) -> None:
state = self.op2_filename_checkbox.isChecked()
self.op2_filename_edit.setEnabled(state)
self.op2_filename_browse.setEnabled(state)
if state:
self.op2_filename_edit.setText(self.op2_filename)
else:
self.op2_filename_edit.setText(self._op2_filename_default)

def on_browse_f06(self) -> None:
"""pops a dialgo to select the f06 file"""
"""pops a dialog to select the f06 file"""
title = 'Load Nastran Flutter F06 File'
qt_wildcard = 'F06 File (*.f06)'
basedir = os.path.dirname(self.f06_filename)
Expand All @@ -387,6 +411,43 @@ def on_browse_f06(self) -> None:
return
self.f06_filename_edit.setText(fname)
self.ok_button.setEnabled(False)
self._set_f06_default_names(fname)
def _set_f06_default_names(self, f06_filename: str) -> None:
base = os.path.splitext(f06_filename)[0]
self._bdf_filename_default = base + '.bdf'
self._op2_filename_default = base + '.op2'
if self.bdf_filename == '':
self.bdf_filename = self._bdf_filename_default
if self.op2_filename == '':
self.op2_filename = self._op2_filename_default
self.bdf_filename_edit.setText(self._bdf_filename_default)
self.op2_filename_edit.setText(self._op2_filename_default)

def on_browse_bdf(self) -> None:
"""pops a dialog to select the bdf file"""
title = 'Load Nastran Flutter BDF/DAT File'
qt_wildcard = 'F06 File (*.bdf, *.dat)'
basedir = os.path.dirname(self.bdf_filename)
fname, wildcard_level = getopenfilename(
self, caption=title, basedir=basedir, filters=qt_wildcard,)
if fname == '':
return
self.bdf_filename_edit.setText(fname)
self.bdf_filename = fname
#self.ok_button.setEnabled(False)

def on_browse_op2(self) -> None:
"""pops a dialog to select the op2 file"""
title = 'Load Nastran Flutter OP2 File'
qt_wildcard = 'OP2 File (*.o2p)'
basedir = os.path.dirname(self.op2_filename)
fname, wildcard_level = getopenfilename(
self, caption=title, basedir=basedir, filters=qt_wildcard,)
if fname == '':
return
self.op2_filename_edit.setText(fname)
self.pop_vtk_gui_button.setEnabled(True)
self.op2_filename = fname

# @dontcrash
def on_load_settings(self) -> None:
Expand Down Expand Up @@ -419,6 +480,22 @@ def setup_widgets(self) -> None:
self.f06_filename_edit = QLineEdit()
self.f06_filename_browse = QPushButton('Browse...')

self.bdf_filename_checkbox = QCheckBox('BDF Filename:')
self.bdf_filename_edit = QLineEdit()
self.bdf_filename_browse = QPushButton('Browse...')
self.bdf_filename_checkbox.setChecked(False)
self.bdf_filename_edit.setEnabled(False)
self.bdf_filename_browse.setEnabled(False)
self.bdf_filename_edit.setToolTip('Loads the Nastran Geometry')

self.op2_filename_checkbox = QCheckBox('OP2 Filename:')
self.op2_filename_edit = QLineEdit()
self.op2_filename_browse = QPushButton('Browse...')
self.op2_filename_checkbox.setChecked(False)
self.op2_filename_edit.setEnabled(False)
self.op2_filename_browse.setEnabled(False)
self.op2_filename_edit.setToolTip('Loads the Nastran Results (and geometry if BDF Filename is empty)')

self.use_rhoref_checkbox = QCheckBox('Sea Level Rho Ref')
self.use_rhoref_checkbox.setChecked(False)

Expand Down Expand Up @@ -575,13 +652,15 @@ def setup_widgets(self) -> None:
self.ok_button = QPushButton('Run', self)


self.gui_button = QPushButton('Open GUI', self)
self.pop_vtk_gui_button = QPushButton('Open GUI', self)
self.solution_type_label = QLabel('Solution Type:')
self.solution_type_pulldown = QComboBox(self)
self.mode2_label = QLabel('Mode:')
self.mode2_pulldown = QComboBox(self)
self.setup_modes()
self.on_plot_type()
self.on_enable_bdf()
self.on_enable_op2()

def on_plot_type(self) -> None:
x_plot_type = self.x_plot_type_pulldown.currentText()
Expand Down Expand Up @@ -732,10 +811,26 @@ def on_plot_type(self) -> None:
item.setVisible(show_hide)

def setup_layout(self) -> None:
hbox = QHBoxLayout()
hbox.addWidget(self.f06_filename_label)
hbox.addWidget(self.f06_filename_edit)
hbox.addWidget(self.f06_filename_browse)
if 0:
hbox = QHBoxLayout()
hbox.addWidget(self.f06_filename_label)
hbox.addWidget(self.f06_filename_edit)
hbox.addWidget(self.f06_filename_browse)
else:
file_row = 0
hbox = QGridLayout()
hbox.addWidget(self.f06_filename_label, file_row, 0)
hbox.addWidget(self.f06_filename_edit, file_row, 1)
hbox.addWidget(self.f06_filename_browse, file_row, 2)
file_row += 1
hbox.addWidget(self.bdf_filename_checkbox, file_row, 0)
hbox.addWidget(self.bdf_filename_edit, file_row, 1)
hbox.addWidget(self.bdf_filename_browse, file_row, 2)
file_row += 1
hbox.addWidget(self.op2_filename_checkbox, file_row, 0)
hbox.addWidget(self.op2_filename_edit, file_row, 1)
hbox.addWidget(self.op2_filename_browse, file_row, 2)
file_row += 1

grid = QGridLayout()
irow = 0
Expand Down Expand Up @@ -895,7 +990,7 @@ def setup_layout(self) -> None:
vbox.addLayout(hbox_check)
vbox.addStretch(1)
vbox.addLayout(ok_cancel_hbox)
vbox.addWidget(self.gui_button)
vbox.addWidget(self.pop_vtk_gui_button)
vbox.addLayout(grid_modes)
#log_widget = ApplicationLogWidget(self)

Expand All @@ -906,6 +1001,7 @@ def setup_layout(self) -> None:
self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.log_dock_widget)
vbox2 = vbox
else:
asf
#self.log_dock_widget.hide()
vbox2 = QHBoxLayout()
vbox2.addWidget(self.modes_widget)
Expand All @@ -930,17 +1026,24 @@ def _grid_modes(self) -> QGridLayout:

def setup_connections(self) -> None:
self.f06_load_button.clicked.connect(self.on_load_f06)
#self.bdf_load_button.clicked.connect(self.on_load_bdf)
#self.op2_load_button.clicked.connect(self.on_load_op2)

self.x_plot_type_pulldown.currentIndexChanged.connect(self.on_plot_type)
self.plot_type_pulldown.currentIndexChanged.connect(self.on_plot_type)
self.subcase_edit.currentIndexChanged.connect(self.on_subcase)
self.bdf_filename_checkbox.stateChanged.connect(self.on_enable_bdf)
self.op2_filename_checkbox.stateChanged.connect(self.on_enable_op2)
self.f06_filename_browse.clicked.connect(self.on_browse_f06)
self.bdf_filename_browse.clicked.connect(self.on_browse_bdf)
self.op2_filename_browse.clicked.connect(self.on_browse_op2)
#self.modes_widget.itemSelectionChanged.connect(self.on_modes)
# self.modes_widget.itemClicked.connect(self.on_modes)
# self.modes_widget.currentRowChanged.connect(self.on_modes)
self.ok_button.clicked.connect(self.on_ok)
self.units_out_pulldown.currentIndexChanged.connect(self.on_units_out)

self.gui_button.clicked.connect(self.on_open_new_window)
self.pop_vtk_gui_button.clicked.connect(self.on_open_new_window)

def on_units_out(self):
units_out = self.units_out_pulldown.currentText()
Expand Down Expand Up @@ -1497,6 +1600,8 @@ def validate(self) -> bool:
# (response.eigr_eigi_velocity is not None)
# ) or (self.plot_type != 'modal-participation'))
data = {
'bdf_filename': self.bdf_filename,
'op2_filename': self.op2_filename,
'log_scale_x': self.log_xscale_checkbox.isChecked(),
'log_scale_y1': self.log_yscale1_checkbox.isChecked(),
'log_scale_y2': self.log_yscale2_checkbox.isChecked(),
Expand All @@ -1513,7 +1618,6 @@ def validate(self) -> bool:
'recent_files': self.recent_files,
'font_size': self.font_size,
'plot_font_size': self.plot_font_size,
'point_spacing': self.point_spacing,
'subcase': subcase,
#'modes': modes,
'selected_modes': selected_modes,
Expand Down Expand Up @@ -1565,7 +1669,11 @@ def validate(self) -> bool:
return is_passed

def on_open_new_window(self):
self._vtk_window_obj.show(BDF_FILENAME, OP2_FILENAME)
#bdf_filename = self.bdf_filename if not (self.bdf_filename and os.path.exists(self.bdf_filename)) else BDF_FILENAME
#op2_filename = self.op2_filename if not (self.op2_filename and os.path.exists(self.op2_filename)) else OP2_FILENAME
bdf_filename = self.bdf_filename if os.path.exists(self.bdf_filename) else ''
op2_filename = self.op2_filename if os.path.exists(self.op2_filename) else ''
self._vtk_window_obj.show(bdf_filename, op2_filename)
return
try:
from pyNastran.f06.dev.flutter.gui_flutter_vtk import VtkWindow
Expand Down
2 changes: 1 addition & 1 deletion pyNastran/f06/dev/flutter/vtk_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,7 @@ def on_tree_item_changed(self, item: QStandardItem) -> None:
text0 = item.text()
#print(f'text0 = {text0}')
is_checked = (item.checkState() == Qt.Checked)
3print(f'is_checked = {is_checked}')
#print(f'is_checked = {is_checked}')
if 1:
selected_texts = [item.text()]
else:
Expand Down

0 comments on commit c84ffe1

Please sign in to comment.