diff --git a/pyNastran/bdf/bdf_interface/attributes.py b/pyNastran/bdf/bdf_interface/attributes.py index d86144fff..33782cc57 100644 --- a/pyNastran/bdf/bdf_interface/attributes.py +++ b/pyNastran/bdf/bdf_interface/attributes.py @@ -1396,44 +1396,44 @@ def get_param(self, key: str, default: int | float | str | list[float] #-------------------- # deprecations - @property - def dmis(self) -> dict[str, DMI]: - self.deprecated('dmis', 'dmi', '1.5') - return self.dmi - @property - def dmigs(self) -> dict[str, DMIG]: - self.deprecated('dmig', 'dmigs', '1.5') - return self.dmig - @property - def dmiks(self) -> dict[str, DMIK]: - self.deprecated('dmik', 'dmiks', '1.5') - return self.dmik - @property - def dmijs(self) -> dict[str, DMIJ]: - self.deprecated('dmij', 'dmijs', '1.5') - return self.dmij - @property - def dmijis(self) -> dict[str, DMIJI]: - self.deprecated('dmiji', 'dmiji', '1.5') - return self.dmiji - - @dmis.setter - def dmis(self, dmi): - self.deprecated('dmis', 'dmi', '1.5') - self.dmi = dmi - @dmigs.setter - def dmigs(self, dmig): - self.deprecated('dmig', 'dmigs', '1.5') - self.dmig = dmig - @dmiks.setter - def dmiks(self, dmik): - self.deprecated('dmik', 'dmiks', '1.5') - self.dmik = dmik - @dmijs.setter - def dmijs(self, dmij): - self.deprecated('dmij', 'dmijs', '1.5') - self.dmij = dmij - @dmijis.setter - def dmijis(self, dmiji): - self.deprecated('dmiji', 'dmiji', '1.5') - self.dmiji = dmiji + # @property + # def dmis(self) -> dict[str, DMI]: + # self.deprecated('dmis', 'dmi', '1.5') + # return self.dmi + # @property + # def dmigs(self) -> dict[str, DMIG]: + # self.deprecated('dmig', 'dmigs', '1.5') + # return self.dmig + # @property + # def dmiks(self) -> dict[str, DMIK]: + # self.deprecated('dmik', 'dmiks', '1.5') + # return self.dmik + # @property + # def dmijs(self) -> dict[str, DMIJ]: + # self.deprecated('dmij', 'dmijs', '1.5') + # return self.dmij + # @property + # def dmijis(self) -> dict[str, DMIJI]: + # self.deprecated('dmiji', 'dmiji', '1.5') + # return self.dmiji + # + # @dmis.setter + # def dmis(self, dmi): + # self.deprecated('dmis', 'dmi', '1.5') + # self.dmi = dmi + # @dmigs.setter + # def dmigs(self, dmig): + # self.deprecated('dmig', 'dmigs', '1.5') + # self.dmig = dmig + # @dmiks.setter + # def dmiks(self, dmik): + # self.deprecated('dmik', 'dmiks', '1.5') + # self.dmik = dmik + # @dmijs.setter + # def dmijs(self, dmij): + # self.deprecated('dmij', 'dmijs', '1.5') + # self.dmij = dmij + # @dmijis.setter + # def dmijis(self, dmiji): + # self.deprecated('dmiji', 'dmiji', '1.5') + # self.dmiji = dmiji diff --git a/pyNastran/bdf/bdf_interface/write_mesh.py b/pyNastran/bdf/bdf_interface/write_mesh.py index 2791d6c61..5fae8f3f7 100644 --- a/pyNastran/bdf/bdf_interface/write_mesh.py +++ b/pyNastran/bdf/bdf_interface/write_mesh.py @@ -8,7 +8,7 @@ import sys from io import IOBase from pathlib import PurePath -from typing import Optional, Any, cast, TYPE_CHECKING +from typing import TextIO, Optional, Any, cast, TYPE_CHECKING from pyNastran.bdf.field_writer_8 import print_card_8 from pyNastran.bdf.field_writer_16 import print_card_16 @@ -27,6 +27,7 @@ from cpylog import SimpleLogger from pyNastran.utils import PathLike from pyNastran.bdf.bdf import BDF, DESVAR + TextFile = StringIO | TextIO class WriteMesh(BDFAttributes): @@ -46,7 +47,7 @@ def __init__(self): self.cards_to_read = set() def get_encoding(self, encoding: Optional[str]=None) -> str: - """gets the file encoding""" + """gets the TextFile encoding""" if encoding is not None: pass else: @@ -147,6 +148,8 @@ def write_bdf(self, out_filename: Optional[str | StringIO]=None, nodes_size: Optional[int]=None, elements_size: Optional[int]=None, loads_size: Optional[int]=None, + #table_size: Optional[int]=None, + flfact_size: int=0, is_double: bool=False, interspersed: bool=False, enddata: Optional[bool]=None, write_header: bool=True, close: bool=True) -> None: @@ -226,6 +229,7 @@ def write_bdf(self, out_filename: Optional[str | StringIO]=None, interspersed=interspersed, enddata=enddata, close=close, nodes_size=nodes_size, elements_size=elements_size, loads_size=loads_size, + flfact_size=flfact_size, is_long_ids=is_long_ids) def write_bulk_data(self, bdf_file, @@ -235,6 +239,7 @@ def write_bulk_data(self, bdf_file, nodes_size: Optional[int]=None, elements_size: Optional[int]=None, loads_size: Optional[int]=None, + flfact_size: Optional[int]=None, is_long_ids: bool=False) -> None: """ Writes the BDF. @@ -266,8 +271,8 @@ def write_bulk_data(self, bdf_file, you're calling the new sub-function, you might need it. Chances are you won't. """ - size, nodes_size, elements_size, loads_size = _fix_sizes( - size, nodes_size, elements_size, loads_size) + size, nodes_size, elements_size, loads_size, flfact_size = _fix_sizes( + size, nodes_size, elements_size, loads_size, flfact_size) self._write_params(bdf_file, size, is_double, is_long_ids=is_long_ids) self._write_model_groups(bdf_file) @@ -292,7 +297,8 @@ def write_bulk_data(self, bdf_file, self._write_rigid_elements(bdf_file, size, is_double, is_long_ids=is_long_ids) self._write_aero(bdf_file, size, is_double, is_long_ids=is_long_ids) - self._write_common(bdf_file, loads_size, is_double, is_long_ids=is_long_ids) + self._write_common(bdf_file, loads_size, flfact_size, + is_double, is_long_ids=is_long_ids) if (enddata is None and 'ENDDATA' in self.card_count) or enddata: bdf_file.write('ENDDATA\n') if close: @@ -447,7 +453,7 @@ def _write_elements_interspersed(self, bdf_file: Any, size: int=8, is_double: bo bdf_file.write(snorm.write_card(size, is_double)) self._write_nsm(bdf_file, size, is_double) - def _write_aero(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_aero(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the aero cards""" if self.caeros or self.paeros or self.monitor_points or self.splines: @@ -462,7 +468,7 @@ def _write_aero(self, bdf_file: Any, size: int=8, is_double: bool=False, bdf_file.write(monitor_point.write_card(size, is_double)) self.zona.write_bdf(bdf_file, size=8, is_double=False) - def _write_aero_control(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_aero_control(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the aero control surface cards""" if(self.aecomps or self.aefacts or self.aeparams or self.aelinks or @@ -488,7 +494,7 @@ def _write_aero_control(self, bdf_file: Any, size: int=8, is_double: bool=False, for (unused_id, aefact) in sorted(self.aefacts.items()): bdf_file.write(aefact.write_card(size, is_double)) - def _write_static_aero(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_static_aero(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the static aero cards""" if self.aeros or self.trims or self.divergs: @@ -502,7 +508,9 @@ def _write_static_aero(self, bdf_file: Any, size: int=8, is_double: bool=False, bdf_file.write(diverg.write_card(size, is_double)) - def _write_flutter(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_flutter(self, bdf_file: TextFile, size: int=8, + flfact_size: int=8, + is_double: bool=False, write_aero_in_flutter: bool=True, is_long_ids: Optional[bool]=None) -> None: """Writes the flutter cards""" @@ -513,11 +521,11 @@ def _write_flutter(self, bdf_file: Any, size: int=8, is_double: bool=False, for (unused_id, flutter) in sorted(self.flutters.items()): bdf_file.write(flutter.write_card(size, is_double)) for (unused_id, flfact) in sorted(self.flfacts.items()): - bdf_file.write(flfact.write_card(size, is_double)) + bdf_file.write(flfact.write_card(flfact_size, is_double)) for mkaero in self.mkaeros: bdf_file.write(mkaero.write_card(size, is_double)) - def _write_gust(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_gust(self, bdf_file: TextFile, size: int=8, is_double: bool=False, write_aero_in_gust: bool=True, is_long_ids: Optional[bool]=None) -> None: """Writes the gust cards""" @@ -529,7 +537,9 @@ def _write_gust(self, bdf_file: Any, size: int=8, is_double: bool=False, for (unused_id, gust) in sorted(self.gusts.items()): bdf_file.write(gust.write_card(size, is_double)) - def _write_common(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_common(self, bdf_file: TextFile, + size: int=8, flfact_size: int=8, + is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """ Write the common outputs so none get missed... @@ -551,7 +561,9 @@ def _write_common(self, bdf_file: Any, size: int=8, is_double: bool=False, self._write_static_aero(bdf_file, size, is_double, is_long_ids=is_long_ids) write_aero_in_flutter, write_aero_in_gust = find_aero_location(self) - self._write_flutter(bdf_file, size, is_double, write_aero_in_flutter, + self._write_flutter(bdf_file, size=size, flfact_size=flfact_size, + is_double=is_double, + write_aero_in_flutter=write_aero_in_flutter, is_long_ids=is_long_ids) self._write_gust(bdf_file, size, is_double, write_aero_in_gust, is_long_ids=is_long_ids) @@ -569,7 +581,7 @@ def _write_common(self, bdf_file: Any, size: int=8, is_double: bool=False, self._write_matcids(bdf_file, size, is_double, is_long_ids=is_long_ids) - def _write_constraints(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_constraints(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the constraint cards sorted by ID""" size, is_long_ids = self._write_mesh_long_ids_size(size, is_long_ids) @@ -606,7 +618,7 @@ def _write_constraints(self, bdf_file: Any, size: int=8, is_double: bool=False, for mpc in mpcs: bdf_file.write(mpc.write_card(size, is_double)) - def _write_contact(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_contact(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the contact cards sorted by ID""" is_contact = (self.bcrparas or self.bctadds or self.bctparas @@ -646,7 +658,7 @@ def _write_contact(self, bdf_file: Any, size: int=8, is_double: bool=False, for (unused_id, bgset) in sorted(self.bgsets.items()): bdf_file.write(bgset.write_card(size, is_double)) - def _write_coords(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_coords(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the coordinate cards in a sorted order""" size, is_long_ids = self._write_mesh_long_ids_size(size, is_long_ids) @@ -660,7 +672,7 @@ def _write_coords(self, bdf_file: Any, size: int=8, is_double: bool=False, except RuntimeError: bdf_file.write(coord.write_card_16(is_double)) - def _write_matcids(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_matcids(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the MATCID cards in a sorted order""" size, is_long_ids = self._write_mesh_long_ids_size(size, is_long_ids) @@ -674,7 +686,7 @@ def _write_matcids(self, bdf_file: Any, size: int=8, is_double: bool=False, except RuntimeError: bdf_file.write(matcid.write_card_16(is_double)) - def _write_dmigs(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_dmigs(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """ Writes the DMIG cards @@ -698,7 +710,7 @@ def _write_dmigs(self, bdf_file: Any, size: int=8, is_double: bool=False, for (unused_name, dmiax) in natsorted(self.dmiax.items()): bdf_file.write(dmiax.write_card(size, is_double)) - def _write_dynamic(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_dynamic(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the dynamic cards sorted by ID""" is_dynamic = (self.dareas or self.dphases or self.nlparms or self.frequencies or @@ -758,7 +770,7 @@ def _write_mesh_long_ids_size(self, size: bool, is_long_ids: bool) -> tuple[int, is_long_ids = False return size, is_long_ids - def _write_loads(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_loads(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the load cards sorted by ID""" size, is_long_ids = self._write_mesh_long_ids_size(size, is_long_ids) @@ -801,7 +813,7 @@ def _write_loads(self, bdf_file: Any, size: int=8, is_double: bool=False, bdf_file.write(cyjoin.write_card(size, is_double)) self._write_dloads(bdf_file, size=size, is_double=is_double, is_long_ids=is_long_ids) - def _write_dloads(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_dloads(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the dload cards sorted by ID""" size, is_long_ids = self._write_mesh_long_ids_size(size, is_long_ids) @@ -824,7 +836,7 @@ def _write_dloads(self, bdf_file: Any, size: int=8, is_double: bool=False, raise - def _write_masses(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_masses(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the mass cards sorted by ID""" size, is_long_ids = self._write_mesh_long_ids_size(size, is_long_ids) @@ -846,7 +858,7 @@ def _write_masses(self, bdf_file: Any, size: int=8, is_double: bool=False, print(f'failed printing masses...type={mass.type} eid={eid}') raise - def _write_materials(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_materials(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the materials in a sorted order""" size, is_long_ids = self._write_mesh_long_ids_size(size, is_long_ids) @@ -894,7 +906,7 @@ def _write_materials(self, bdf_file: Any, size: int=8, is_double: bool=False, for unused_mid, mat in sorted(self.big_materials.items()): bdf_file.write(mat.write_card_16(is_double)) - def _write_model_groups(self, bdf_file: Any): + def _write_model_groups(self, bdf_file: TextFile): if self.model_groups: #bdf_file.write('$ MODELGROUPS\n') for group in self.model_groups.values(): @@ -902,7 +914,7 @@ def _write_model_groups(self, bdf_file: Any): print(group) #x = 1 - def _write_nodes(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_nodes(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the NODE-type cards""" if self.spoints: @@ -937,7 +949,7 @@ def _write_nodes(self, bdf_file: Any, size: int=8, is_double: bool=False, #if 0: # not finished #self._write_nodes_associated(bdf_file, size, is_double) - def _write_grids(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_grids(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the GRID-type cards""" size, is_long_ids = self._write_mesh_long_ids_size(size, is_long_ids) @@ -982,7 +994,7 @@ def _write_grids(self, bdf_file: Any, size: int=8, is_double: bool=False, #else: #bdf_file.write('$ Missing NodeID=%s' % key) - def _write_optimization(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_optimization(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the optimization cards sorted by ID""" is_optimization = ( @@ -1042,7 +1054,7 @@ def _write_optimization(self, bdf_file: Any, size: int=8, is_double: bool=False, for (unused_id, dmncon) in sorted(self.dmncon.items()): bdf_file.write(dmncon.write_card(size, is_double)) - def _write_parametric(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_parametric(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the optimization cards sorted by ID""" is_parametric = self.pset or self.pval or self.gmcurv or self.feedge or self.feface @@ -1066,7 +1078,7 @@ def _write_parametric(self, bdf_file: Any, size: int=8, is_double: bool=False, for (unused_id, feface) in sorted(self.feface.items()): bdf_file.write(feface.write_card(size, is_double)) - def _write_params(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_params(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the PARAM cards""" size, is_long_ids = self._write_mesh_long_ids_size(size, is_long_ids) @@ -1080,7 +1092,7 @@ def _write_params(self, bdf_file: Any, size: int=8, is_double: bool=False, if self.mdlprm: bdf_file.write(self.mdlprm.write_card(size, is_double)) - def _write_properties(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_properties(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the properties in a sorted order""" size, is_long_ids = self._write_mesh_long_ids_size(size, is_long_ids) @@ -1106,7 +1118,8 @@ def _write_properties(self, bdf_file: Any, size: int=8, is_double: bool=False, for unused_pid, prop in sorted(self.big_properties.items()): bdf_file.write(prop.write_card_16(is_double)) - def _write_properties_by_element_type(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_properties_by_element_type(self, bdf_file: TextFile, size: int=8, + is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """ Writes the properties in a sorted order by property type grouping @@ -1138,7 +1151,7 @@ def _write_properties_by_element_type(self, bdf_file: Any, size: int=8, is_doubl bdf_file.write(prop.write_card(size, is_double)) bdf_file.write('$' + '-' * 80 + '\n') - def _write_rejects(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_rejects(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """ Writes the rejected (processed) cards and the rejected unprocessed @@ -1184,7 +1197,7 @@ def _write_rejects(self, bdf_file: Any, size: int=8, is_double: bool=False, else: raise TypeError(reject_lines) - def _write_rigid_elements(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_rigid_elements(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the rigid elements in a sorted order""" size, is_long_ids = self._write_mesh_long_ids_size(size, is_long_ids) @@ -1208,7 +1221,7 @@ def _write_rigid_elements(self, bdf_file: Any, size: int=8, is_double: bool=Fals bdf_file.write('$PLOT ELEMENTS\n') write_dict(bdf_file, self.plotels, size, is_double, is_long_ids) - def _write_sets(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_sets(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the SETx cards sorted by ID""" is_sets = (self.sets or self.asets or self.omits or self.bsets or self.csets or self.qsets @@ -1231,7 +1244,7 @@ def _write_sets(self, bdf_file: Any, size: int=8, is_double: bool=False, for set_obj in usets: # list bdf_file.write(set_obj.write_card(size, is_double)) - def _write_superelements(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_superelements(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """ Writes the Superelement cards @@ -1293,7 +1306,7 @@ def _write_superelements(self, bdf_file: Any, size: int=8, is_double: bool=False bdf_file.write(release.write_card(size, is_double)) - def _write_tables(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_tables(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the TABLEx cards sorted by ID""" if self.tables or self.tables_d or self.tables_m or self.tables_sdamping: @@ -1312,7 +1325,7 @@ def _write_tables(self, bdf_file: Any, size: int=8, is_double: bool=False, for (unused_id, table) in sorted(self.random_tables.items()): bdf_file.write(table.write_card(size, is_double)) - def _write_thermal(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_thermal(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the thermal cards""" # PHBDY @@ -1344,7 +1357,7 @@ def _write_thermal(self, bdf_file: Any, size: int=8, is_double: bool=False, bdf_file.write(radcav.write_card(size, is_double)) - def _write_thermal_materials(self, bdf_file: Any, size: int=8, is_double: bool=False, + def _write_thermal_materials(self, bdf_file: TextFile, size: int=8, is_double: bool=False, is_long_ids: Optional[bool]=None) -> None: """Writes the thermal materials in a sorted order""" if self.thermal_materials: @@ -1355,14 +1368,17 @@ def _write_thermal_materials(self, bdf_file: Any, size: int=8, is_double: bool=F def _fix_sizes(size: int, nodes_size: Optional[int], elements_size: Optional[int], - loads_size: Optional[int]) -> tuple[int, int, int, int]: + loads_size: Optional[int], + flfact_size: Optional[int]) -> tuple[int, int, int, int, int]: if nodes_size is None: nodes_size = size if elements_size is None: elements_size = size if loads_size is None: loads_size = size - return size, nodes_size, elements_size, loads_size + if flfact_size is None: + flfact_size = size + return size, nodes_size, elements_size, loads_size, flfact_size def _output_helper(out_filename: Optional[str], interspersed: bool, size: int, is_double: bool, log: SimpleLogger) -> tuple[str, int]: diff --git a/pyNastran/bdf/test/run_jobs.py b/pyNastran/bdf/test/run_jobs.py index ddfc06986..5253774f8 100644 --- a/pyNastran/bdf/test/run_jobs.py +++ b/pyNastran/bdf/test/run_jobs.py @@ -8,13 +8,14 @@ import pyNastran from pyNastran.utils import print_bad_path from pyNastran.utils.nastran_utils import run_nastran +from pyNastran.utils.dev import get_files_of_type def cmd_line_run_jobs(argv=None, quiet: bool=False): """ run_nastran_job dirname run_nastran_job filename.bdf -x C:\bin\nastran.exe - run_nastran_job . -x C:\bin\nastran.exe + run_nastran_job . -x C:\bin\nastran.exe --cleanup -r """ FILE = os.path.abspath(__file__) if argv is None: @@ -27,7 +28,8 @@ def cmd_line_run_jobs(argv=None, quiet: bool=False): parser.add_argument("bdf_dirname_filename", help='path to Nastran filename') #parser.add_argument('-o', '--overwrite', default=False, help='overwrite files') parser.add_argument('-x', '--exe', default='nastran', help='path to Nastran execuable') - parser.add_argument('-c', '--cleanup', default=True, help='cleanup the junk output files (log, f04, plt)') + parser.add_argument('-c', '--cleanup', action='store_true', help='cleanup the junk output files (log, f04, plt)') + parser.add_argument('-r', '--recursive', action='store_false', help='recursively search for directories') #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__) @@ -36,11 +38,12 @@ def cmd_line_run_jobs(argv=None, quiet: bool=False): if not quiet: # pragma: no cover print(args) + recursive = args.recursive 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: + # if nastran_exe is None: + # nastran_exe = 'nastran' + if '.bat' in nastran_exe or '.exe' in nastran_exe: nastran_exe = Path(nastran_exe) assert nastran_exe.exists(), print_bad_path(nastran_exe) assert nastran_exe.is_file(), nastran_exe @@ -48,11 +51,13 @@ def cmd_line_run_jobs(argv=None, quiet: bool=False): cleanup = args.cleanup extensions = ['.dat', '.bdf'] run_jobs(bdf_filename_dirname, nastran_exe, - extensions=extensions, cleanup=cleanup) + extensions=extensions, cleanup=cleanup, + recursive=True) def get_bdf_filenames_to_run(bdf_filename_dirname: Path | list[Path], - extensions: list[str]) -> list[Path]: + extensions: list[str], + recursive: bool=False) -> list[Path]: if isinstance(extensions, str): extensions = [extensions] assert isinstance(extensions, list), extensions @@ -71,10 +76,18 @@ def get_bdf_filenames_to_run(bdf_filename_dirname: Path | list[Path], for bdf_filename_dirnamei in bdf_filename_dirname_list: assert bdf_filename_dirnamei.exists(), bdf_filename_dirnamei if bdf_filename_dirnamei.is_dir(): - dirname = bdf_filename_dirnamei - #suffixs = [fname.suffix for fname in dirname.iterdir() if '.test_bdf.' not in fname.name] - bdf_filenamesi = [dirname / fname.name for fname in dirname.iterdir() - if fname.suffix in extensions and '.test_bdf.' not in fname.name] + dirname = os.path.abspath(bdf_filename_dirnamei) + if recursive: + bdf_filenamesi = [] + for ext in extensions: + files = get_files_of_type( + dirname, extension=ext, max_size=0.) # no size limit (in MB) + bdf_filenamesi += [Path(fname) for fname in files + if '.test_bdf.' not in os.path.basename(fname)] + else: + #suffixs = [fname.suffix for fname in dirname.iterdir() if '.test_bdf.' not in fname.name] + bdf_filenamesi = [dirname / fname.name for fname in dirname.iterdir() + if fname.suffix in extensions and '.test_bdf.' not in fname.name] assert len(bdf_filenamesi) > 0, dirname elif bdf_filename_dirnamei.is_file(): @@ -84,15 +97,24 @@ def get_bdf_filenames_to_run(bdf_filename_dirname: Path | list[Path], bdf_filenames.extend(bdf_filenamesi) for bdf_filename in bdf_filenames: assert bdf_filename.exists(), print_bad_path(bdf_filename) + assert len(bdf_filenames) > 0, bdf_filenames return bdf_filenames def run_jobs(bdf_filename_dirname: Path, nastran_exe: str | Path, extensions: list[str], cleanup: bool=True, + recursive: bool=False, run: bool=True) -> int: - """runs a series of jobs in a specific folder""" + """runs a series of jobs in a specific folder + + Parameters + ---------- + recursive: bool; default=False + finds all bdf/dat files in all sub-directories + NOTE: doesn't apply to files + """ bdf_filenames = get_bdf_filenames_to_run( - bdf_filename_dirname, extensions) + bdf_filename_dirname, extensions, recursive=recursive) log = SimpleLogger(level='debug') bdf_filenames_str = [str(bdf_filename) for bdf_filename in bdf_filenames] @@ -101,6 +123,7 @@ def run_jobs(bdf_filename_dirname: Path, nastran_exe: str | Path, assert bdf_filename.exists(), print_bad_path(bdf_filename) nfiles = len(bdf_filenames) + t_run_min = 0. t_est_min = 0. t_est_hr = 0. t0 = time.time() @@ -111,15 +134,24 @@ def run_jobs(bdf_filename_dirname: Path, nastran_exe: str | Path, log.warning(f'skipping {str(bdf_filename)} because {op2_filename} already exists') continue - percent = (ifile + 1) / nfiles * 100 - log.debug(f'estimated time remaining: {t_est_min:.0f} min = {t_est_hr:.1f} hr') - log.info(f'running {ifile+1}/{nfiles}={percent:.0f}%: {str(bdf_filename)}') + #nfiles_remaining0 = nfiles - ifile + nfiles_remaining1 = nfiles - (ifile + 1) + percent0 = ifile / nfiles * 100 + percent1 = (ifile + 1) / nfiles * 100 + log.debug(f'estimated time remaining: {t_est_min:.0f} min = {t_est_hr:.1f} hr; time/run={t_run_min:.1f} min') + log.info(f'running {ifile+1}/{nfiles}={percent0:.0f}%: {str(bdf_filename)}') run_nastran(bdf_filename, nastran_cmd=nastran_exe, cleanup=cleanup, run=run) - log.debug(f'finished {ifile+1}/{nfiles}={percent:.0f}%: {str(bdf_filename)}') + log.debug(f'finished {ifile+1}/{nfiles}={percent1:.0f}%: {str(bdf_filename)}') + + # if 0: dt = time.time() - t0 - t_est = (ifile / nfiles) * dt - t_est_min = t_est / 60. + # else: + # dt = 20. * 60. * (ifile + 1) + + t_run_min = dt / (ifile + 1) / 60 + t_est_sec = dt * nfiles_remaining1 / (ifile + 1) + t_est_min = t_est_sec / 60. t_est_hr = t_est_min / 60. log.info('done') return nfiles diff --git a/pyNastran/f06/dev/flutter/gui_flutter.py b/pyNastran/f06/dev/flutter/gui_flutter.py index 27da9a46e..f8291ee26 100644 --- a/pyNastran/f06/dev/flutter/gui_flutter.py +++ b/pyNastran/f06/dev/flutter/gui_flutter.py @@ -116,7 +116,7 @@ def __init__(self, f06_filename: str=''): self.alt_lim = [] self.q_lim = [] self.rho_lim = [] - #self.x_lim = [] + self.eas_damping_lim = [None, None] self.freq_lim = [None, None] self.damping_lim = [None, None] self.kfreq_lim = [None, None] @@ -334,9 +334,10 @@ def _apply_settings(self, data: dict[str, Any]) -> None: ('alt_lim', self.alt_lim_edit_min, self.alt_lim_edit_max), ('q_lim', self.q_lim_edit_min, self.q_lim_edit_max), ('rho_lim', self.rho_lim_edit_min, self.rho_lim_edit_max), - ('xlim', self.xlim_edit_min, self.xlim_edit_max), + ('ikfreq_lim', self.ikfreq_lim_edit_min, self.ikfreq_lim_edit_max), ('damp_lim', self.damp_lim_edit_min, self.damp_lim_edit_max), + ('eas_damping_lim', self.eas_damping_lim_edit_min, self.eas_damping_lim_edit_max), ('freq_lim', self.freq_lim_edit_min, self.freq_lim_edit_max), ('kfreq_lim', self.kfreq_lim_edit_min, self.kfreq_lim_edit_max), ] @@ -490,41 +491,41 @@ def _set_window_title(self) -> None: self.setWindowTitle(f'Flutter Plot: {self.save_filename}') def setup_widgets(self) -> None: - self.f06_filename_label = QLabel('F06 Filename:') - self.f06_filename_edit = QLineEdit() - self.f06_filename_browse = QPushButton('Browse...') + self.f06_filename_label = QLabel('F06 Filename:', self) + self.f06_filename_edit = QLineEdit(self) + self.f06_filename_browse = QPushButton('Browse...', self) - self.bdf_filename_checkbox = QCheckBox('BDF Filename:') - self.bdf_filename_edit = QLineEdit() - self.bdf_filename_browse = QPushButton('Browse...') + self.bdf_filename_checkbox = QCheckBox('BDF Filename:', self) + self.bdf_filename_edit = QLineEdit(self) + self.bdf_filename_browse = QPushButton('Browse...', self) 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 = QCheckBox( 'OP2 Filename:', self) + self.op2_filename_edit = QLineEdit(self) + self.op2_filename_browse = QPushButton('Browse...', self) 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 = QCheckBox('Sea Level Rho Ref', self) self.use_rhoref_checkbox.setChecked(False) - self.log_xscale_checkbox = QCheckBox('Log Scale x') - self.log_yscale1_checkbox = QCheckBox('Log Scale y1') - self.log_yscale2_checkbox = QCheckBox('Log Scale y2') + self.log_xscale_checkbox = QCheckBox('Log Scale x', self) + self.log_yscale1_checkbox = QCheckBox('Log Scale y1', self) + self.log_yscale2_checkbox = QCheckBox('Log Scale y2', self) self.log_xscale_checkbox.setChecked(False) self.log_yscale1_checkbox.setChecked(False) self.log_yscale2_checkbox.setChecked(False) - self.show_points_checkbox = QCheckBox('Show Points') - self.show_mode_number_checkbox = QCheckBox('Show Mode Number') - self.point_spacing_label = QLabel('Point Spacing') - self.point_spacing_spinner = QSpinBox() - self.show_lines_checkbox = QCheckBox('Show Lines') + self.show_points_checkbox = QCheckBox('Show Points', self) + self.show_mode_number_checkbox = QCheckBox('Show Mode Number', self) + self.point_spacing_label = QLabel('Point Spacing', self) + self.point_spacing_spinner = QSpinBox(self) + self.show_lines_checkbox = QCheckBox('Show Lines', self) self.show_points_checkbox.setChecked(True) self.show_lines_checkbox.setChecked(True) self.show_points_checkbox.setToolTip('The points are symbols') @@ -532,74 +533,73 @@ def setup_widgets(self) -> None: self.point_spacing_spinner.setToolTip('Skip Every Nth Point; 0=Plot All') self.point_spacing_spinner.setValue(0) self.point_spacing_spinner.setMinimum(0) - self.point_spacing_spinner.setMaximum(10) + self.point_spacing_spinner.setMaximum(30) - self.eas_lim_label = QLabel('EAS Limits:') - self.eas_lim_edit_min = QFloatEdit('0') - self.eas_lim_edit_max = QFloatEdit() + self.eas_lim_label = QLabel('EAS Limits:', self) + self.eas_lim_edit_min = QFloatEdit('0', self) + self.eas_lim_edit_max = QFloatEdit(self) - self.tas_lim_label = QLabel('TAS Limits:') - self.tas_lim_edit_min = QFloatEdit('0') - self.tas_lim_edit_max = QFloatEdit() + self.tas_lim_label = QLabel('TAS Limits:', self) + self.tas_lim_edit_min = QFloatEdit('0', self) + self.tas_lim_edit_max = QFloatEdit(self) - self.mach_lim_label = QLabel('Mach Limits:') - self.mach_lim_edit_min = QFloatEdit() - self.mach_lim_edit_max = QFloatEdit() + self.mach_lim_label = QLabel('Mach Limits:', self) + self.mach_lim_edit_min = QFloatEdit(self) + self.mach_lim_edit_max = QFloatEdit(self) - self.alt_lim_label = QLabel('Alt Limits:') - self.alt_lim_edit_min = QFloatEdit() - self.alt_lim_edit_max = QFloatEdit() + self.alt_lim_label = QLabel('Alt Limits:', self) + self.alt_lim_edit_min = QFloatEdit(self) + self.alt_lim_edit_max = QFloatEdit(self) - self.q_lim_label = QLabel('Q Limits:') - self.q_lim_edit_min = QFloatEdit() - self.q_lim_edit_max = QFloatEdit() + self.q_lim_label = QLabel('Q Limits:', self) + self.q_lim_edit_min = QFloatEdit(self) + self.q_lim_edit_max = QFloatEdit(self) - self.rho_lim_label = QLabel('Rho Limits:') - self.rho_lim_edit_min = QFloatEdit('0') - self.rho_lim_edit_max = QFloatEdit() + self.rho_lim_label = QLabel('Rho Limits:', self) + self.rho_lim_edit_min = QFloatEdit('0', self) + self.rho_lim_edit_max = QFloatEdit(self) - self.xlim_label = QLabel('X Limits:') - self.xlim_edit_min = QFloatEdit('0') - self.xlim_edit_max = QFloatEdit() + self.damp_lim_label = QLabel('Damping Limits (g):', self) + self.damp_lim_edit_min = QFloatEdit('-0.3', self) + self.damp_lim_edit_max = QFloatEdit('0.3', self) - self.damp_lim_label = QLabel('Damping Limits (g):') - self.damp_lim_edit_min = QFloatEdit('-0.3') - self.damp_lim_edit_max = QFloatEdit('0.3') + self.freq_lim_label = QLabel('Freq Limits (Hz):', self) + self.freq_lim_edit_min = QFloatEdit('0', self) + self.freq_lim_edit_max = QFloatEdit(self) - self.freq_lim_label = QLabel('Freq Limits (Hz):') - self.freq_lim_edit_min = QFloatEdit('0') - self.freq_lim_edit_max = QFloatEdit() + self.kfreq_lim_label = QLabel('KFreq Limits:', self) + self.kfreq_lim_edit_min = QFloatEdit(self) + self.kfreq_lim_edit_max = QFloatEdit(self) - self.kfreq_lim_label = QLabel('KFreq Limits:') - self.kfreq_lim_edit_min = QFloatEdit() - self.kfreq_lim_edit_max = QFloatEdit() + self.ikfreq_lim_label = QLabel('1/KFreq Limits:', self) + self.ikfreq_lim_edit_min = QFloatEdit(self) + self.ikfreq_lim_edit_max = QFloatEdit(self) + self.eigr_lim_label = QLabel('Real Eigenvalue:', self) + self.eigr_lim_edit_min = QFloatEdit(self) + self.eigr_lim_edit_max = QFloatEdit(self) - self.eigr_lim_label = QLabel('Real Eigenvalue:') - self.eigr_lim_edit_min = QFloatEdit() - self.eigr_lim_edit_max = QFloatEdit() - - self.eigi_lim_label = QLabel('Imag Eigenvalue:') - self.eigi_lim_edit_min = QFloatEdit() - self.eigi_lim_edit_max = QFloatEdit() + self.eigi_lim_label = QLabel('Imag Eigenvalue:', self) + self.eigi_lim_edit_min = QFloatEdit(self) + self.eigi_lim_edit_max = QFloatEdit(self) #-------------------------------------------- - self.freq_tol_label = QLabel('dFreq Tol (Hz) Dash:') - self.freq_tol_edit = QFloatEdit('-1.0') + self.freq_tol_label = QLabel('dFreq Tol (Hz) Dash:', self) + self.freq_tol_edit = QFloatEdit('-1.0', self) self.freq_tol_edit.setToolTip("Applies a dotted line for modes that don't change by more than some amount") - self.mag_tol_label = QLabel('Magnitude Tol:') - self.mag_tol_edit = QFloatEdit('-1.0') + self.mag_tol_label = QLabel('Magnitude Tol:', self) + self.mag_tol_edit = QFloatEdit('-1.0', self) self.mag_tol_edit.setToolTip("Filters modal participation factors based on magnitude") - self.freq_tol_remove_label = QLabel('dFreq Tol (Hz) Hide:') - self.freq_tol_remove_edit = QFloatEdit('-1.0') + self.freq_tol_remove_label = QLabel('dFreq Tol (Hz) Hide:', self) + self.freq_tol_remove_edit = QFloatEdit('-1.0', self) self.freq_tol_remove_edit.setToolTip("Completely remove modes that don't change by more than some amount") self.freq_tol_remove_label.setVisible(False) self.freq_tol_remove_edit.setVisible(False) - self.subcase_label = QLabel('Subcase:') - self.subcase_edit = QComboBox() + self.subcase_label = QLabel('Subcase:', self) + self.subcase_edit = QComboBox(self) units_msg = ( "english_in: inch/s, slich/in^3\n" @@ -608,17 +608,17 @@ def setup_widgets(self) -> None: "si: m/s, kg/m^3\n" "si-mm: mm/s, Mg/mm^3\n" ) - self.x_plot_type_label = QLabel('X-Axis Plot Type:') + self.x_plot_type_label = QLabel('X-Axis Plot Type:', self) self.x_plot_type_pulldown = QComboBox(self) self.x_plot_type_pulldown.addItems(X_PLOT_TYPES) self.x_plot_type_pulldown.setToolTip('sets the x-axis') - self.plot_type_label = QLabel('Plot Type:') + self.plot_type_label = QLabel('Plot Type:', self) self.plot_type_pulldown = QComboBox(self) self.plot_type_pulldown.addItems(PLOT_TYPES) #self.plot_type_pulldown.setToolTip(units_msg) - self.units_in_label = QLabel('Units In:') + self.units_in_label = QLabel('Units In:', self) self.units_in_pulldown = QComboBox(self) self.units_in_pulldown.addItems(UNITS_IN) self.units_in_pulldown.setToolTip(units_msg) @@ -626,40 +626,46 @@ def setup_widgets(self) -> None: self.units_in_pulldown.setCurrentIndex(iunits_in) self.units_in_pulldown.setToolTip('Sets the units for the F06/OP2; set when loaded') - self.units_out_label = QLabel('Units Out:') - self.units_out_pulldown = QComboBox() + self.units_out_label = QLabel('Units Out:', self) + self.units_out_pulldown = QComboBox(self) self.units_out_pulldown.addItems(UNITS_OUT) self.units_out_pulldown.setToolTip(units_msg) iunits_out = UNITS_IN.index('english_kt') self.units_out_pulldown.setCurrentIndex(iunits_out) self.units_out_pulldown.setToolTip('Sets the units for the plot; may be updated') - self.output_directory_label = QLabel('Output Directory:') + self.output_directory_label = QLabel('Output Directory:', self) self.output_directory_edit = QLineEdit('', self) self.output_directory_browse = QPushButton('Browse...', self) self.output_directory_edit.setDisabled(True) self.output_directory_browse.setDisabled(True) - self.VL_label = QLabel('VL, Limit:') - self.VL_edit = QFloatEdit('') + self.VL_label = QLabel('VL, Limit:', self) + self.VL_edit = QFloatEdit('', self) self.VL_edit.setToolTip('Makes a vertical line for VL') - self.VF_label = QLabel('VF, Flutter:') - self.VF_edit = QFloatEdit('') + self.VF_label = QLabel('VF, Flutter:', self) + self.VF_edit = QFloatEdit('', self) self.VF_edit.setToolTip('Makes a vertical line for VF') - self.damping_label = QLabel('Damping, g:') - self.damping_edit = QFloatEdit('') + self.damping_label = QLabel('Damping, g:', self) + self.damping_edit = QFloatEdit('', self) self.damping_edit.setToolTip('Enables the flutter crossing (e.g., 0.03 for 3%)') - self.mode_label = QLabel('Mode:') - self.mode_edit = QSpinBox() + self.eas_damping_lim_label = QLabel('EAS Crossing Range:', self) + self.eas_damping_lim_edit_min = QFloatEdit('', self) + self.eas_damping_lim_edit_max = QFloatEdit('', self) + self.eas_damping_lim_edit_min.setToolTip('Defines the flutter crossing range') + self.eas_damping_lim_edit_max.setToolTip('Defines the flutter crossing range') + + self.mode_label = QLabel('Mode:', self) + self.mode_edit = QSpinBox(self) self.mode_edit.setMinimum(1) #self.mode_edit.SetValue(3) self.mode_edit.setToolTip('Sets the mode') - self.velocity_label = QLabel('Velocity Point:') - self.velocity_edit = QComboBox() + self.velocity_label = QLabel('Velocity Point:', self) + self.velocity_edit = QComboBox(self) self.velocity_edit.setToolTip('Sets the velocity (input units)') self.f06_load_button = QPushButton('Load F06', self) @@ -667,9 +673,9 @@ def setup_widgets(self) -> None: self.pop_vtk_gui_button = QPushButton('Open GUI', self) - self.solution_type_label = QLabel('Solution Type:') + self.solution_type_label = QLabel('Solution Type:', self) self.solution_type_pulldown = QComboBox(self) - self.mode2_label = QLabel('Mode:') + self.mode2_label = QLabel('Mode:', self) self.mode2_pulldown = QComboBox(self) self.setup_modes() self.on_plot_type() @@ -702,6 +708,11 @@ def on_plot_type(self) -> None: else: show_kfreq = False + if x_plot_type == 'ikfreq': + show_ikfreq = True + else: + show_ikfreq = False + if plot_type == 'x-damp-freq': show_xlim = True show_damp = True @@ -739,6 +750,12 @@ def on_plot_type(self) -> None: elif 'rho' == x_plot_type: show_rho_lim = True show_xlim = False + elif 'kfreq' == x_plot_type: + show_kfreq_lim = True + show_xlim = False + elif 'ikfreq' == x_plot_type: + show_ikfreq_lim = True + show_xlim = False #print(f'x_plot_type={x_plot_type} show_damp={show_damp}; show_xlim={show_xlim}') #assert show_xlim is False, show_xlim @@ -776,10 +793,6 @@ def on_plot_type(self) -> None: self.rho_lim_edit_min.setVisible(show_rho_lim) self.rho_lim_edit_max.setVisible(show_rho_lim) - self.xlim_label.setVisible(show_xlim) - self.xlim_edit_min.setVisible(show_xlim) - self.xlim_edit_max.setVisible(show_xlim) - self.damp_lim_label.setVisible(show_damp) self.damp_lim_edit_min.setVisible(show_damp) self.damp_lim_edit_max.setVisible(show_damp) @@ -794,6 +807,10 @@ def on_plot_type(self) -> None: self.kfreq_lim_edit_min.setVisible(show_kfreq) self.kfreq_lim_edit_max.setVisible(show_kfreq) + self.ikfreq_lim_label.setVisible(show_ikfreq) + self.ikfreq_lim_edit_min.setVisible(show_ikfreq) + self.ikfreq_lim_edit_max.setVisible(show_ikfreq) + self.eigr_lim_label.setVisible(show_root_locus) self.eigr_lim_edit_min.setVisible(show_root_locus) self.eigr_lim_edit_max.setVisible(show_root_locus) @@ -802,10 +819,10 @@ def on_plot_type(self) -> None: self.eigi_lim_edit_min.setVisible(show_root_locus) self.eigi_lim_edit_max.setVisible(show_root_locus) - self.VL_label.setVisible(show_xlim) - self.VL_edit.setVisible(show_xlim) - self.VF_label.setVisible(show_xlim) - self.VF_edit.setVisible(show_xlim) + self.VL_label.setVisible(show_eas_lim) + self.VL_edit.setVisible(show_eas_lim) + self.VF_label.setVisible(show_eas_lim) + self.VF_edit.setVisible(show_eas_lim) show_items = [ (show_modal_participation, ( @@ -900,15 +917,15 @@ def setup_layout(self) -> None: grid.addWidget(self.rho_lim_edit_max, irow, 2) irow += 1 - grid.addWidget(self.xlim_label, irow, 0) - grid.addWidget(self.xlim_edit_min, irow, 1) - grid.addWidget(self.xlim_edit_max, irow, 2) - irow += 1 - grid.addWidget(self.kfreq_lim_label, irow, 0) grid.addWidget(self.kfreq_lim_edit_min, irow, 1) grid.addWidget(self.kfreq_lim_edit_max, irow, 2) irow += 1 + + grid.addWidget(self.ikfreq_lim_label, irow, 0) + grid.addWidget(self.ikfreq_lim_edit_min, irow, 1) + grid.addWidget(self.ikfreq_lim_edit_max, irow, 2) + irow += 1 #-------------------------------------------------- # y-axes grid.addWidget(self.damp_lim_label, irow, 0) @@ -972,6 +989,11 @@ def setup_layout(self) -> None: grid.addWidget(self.damping_edit, irow, 1) irow += 1 + grid.addWidget(self.eas_damping_lim_label, irow, 0) + grid.addWidget(self.eas_damping_lim_edit_min, irow, 1) + grid.addWidget(self.eas_damping_lim_edit_max, irow, 2) + irow += 1 + jrow = 0 grid_check = QGridLayout() grid_check.addWidget(self.log_xscale_checkbox, jrow, 0) @@ -1278,10 +1300,14 @@ def plot(self, modes: list[int]) -> None: xlim = self.alt_lim elif x_plot_type == 'q': xlim = self.q_lim + elif x_plot_type == 'kfreq': + xlim = self.kfreq_lim + elif x_plot_type == 'ikfreq': + xlim = self.ikfreq_lim else: # pragma: no cover log.error(f'x_plot_type={x_plot_type!r} is not supported') #raise RuntimeError(x_plot_type) - xlim = self.xlim + xlim = (None, None) #log.info(f'xlim={xlim}\n') assert xlim[0] != '' and xlim[1] != '', (xlim, x_plot_type) @@ -1308,6 +1334,7 @@ def plot(self, modes: list[int]) -> None: ylim_damping = self.ydamp_lim ylim_freq = self.freq_lim damping_limit = self.damping # % damping + eas_range = self.eas_damping_lim # changing directory so we don't make a long filename # in the plot header @@ -1338,12 +1365,12 @@ def plot(self, modes: list[int]) -> None: response.noline = noline response.set_symbol_settings( nopoints, self.show_mode_number, self.point_spacing) - log.info(f'self.plot_font_size = {self.plot_font_size}') + #log.info(f'self.plot_font_size = {self.plot_font_size}') response.set_font_settings(self.plot_font_size) response.log = log #print('trying plots...') - log.info(f'getting logs\n') + #log.info(f'getting logs\n') log_scale_x = self.data['log_scale_x'] log_scale_y1 = self.data['log_scale_y1'] log_scale_y2 = self.data['log_scale_y2'] @@ -1405,17 +1432,20 @@ def plot(self, modes: list[int]) -> None: #log.info(f'modes={modes!r}') #log.info(f'freq_tol={freq_tol!r}') #log.info(f'v_lines={v_lines!r}') + damping_required = [ + (0.00, 0.01), + (0.03, 0.03), + ] response.plot_vg_vf( fig=fig, damp_axes=damp_axes, freq_axes=freq_axes, plot_type=x_plot_type, modes=modes, xlim=xlim, ylim_damping=ylim_damping, ylim_freq=ylim_freq, + eas_range=eas_range, freq_tol=freq_tol, show=True, clear=False, close=False, legend=True, v_lines=v_lines, - #vl_limit=vl, - #vd_limit=vd, damping_limit=damping_limit, png_filename=png_filename, ) @@ -1464,8 +1494,6 @@ def get_xlim(self) -> tuple[Limit, Limit, Limit, Limit, q_lim_max, is_passed5b = get_float_or_none(self.q_lim_edit_max) rho_lim_min, is_passed6a = get_float_or_none(self.rho_lim_edit_min) rho_lim_max, is_passed6b = get_float_or_none(self.rho_lim_edit_max) - xlim_min, is_passed7a = get_float_or_none(self.xlim_edit_min) - xlim_max, is_passed7b = get_float_or_none(self.xlim_edit_max) is_passed_x = all([is_passed1a, is_passed1b, is_passed2a, is_passed2b, @@ -1473,7 +1501,6 @@ def get_xlim(self) -> tuple[Limit, Limit, Limit, Limit, is_passed4a, is_passed4b, is_passed5a, is_passed5b, is_passed6a, is_passed6b, - is_passed7a, is_passed7a, ]) damp_lim_min, is_passed_damp1 = get_float_or_none(self.damp_lim_edit_min) @@ -1482,6 +1509,8 @@ def get_xlim(self) -> tuple[Limit, Limit, Limit, Limit, freq_lim_max, is_passed_freq2 = get_float_or_none(self.freq_lim_edit_max) kfreq_lim_min, is_passed_kfreq1 = get_float_or_none(self.kfreq_lim_edit_min) kfreq_lim_max, is_passed_kfreq2 = get_float_or_none(self.kfreq_lim_edit_max) + ikfreq_lim_min, is_passed_ikfreq1 = get_float_or_none(self.ikfreq_lim_edit_min) + ikfreq_lim_max, is_passed_ikfreq2 = get_float_or_none(self.ikfreq_lim_edit_max) eigr_lim_min, is_passed_eigr1 = get_float_or_none(self.eigr_lim_edit_min) eigr_lim_max, is_passed_eigr2 = get_float_or_none(self.eigr_lim_edit_max) @@ -1506,18 +1535,27 @@ def get_xlim(self) -> tuple[Limit, Limit, Limit, Limit, alt_lim = [alt_lim_min, alt_lim_max] q_lim = [q_lim_min, q_lim_max] rho_lim = [rho_lim_min, rho_lim_max] - xlim = [xlim_min, xlim_max] + #kfreq_lim = [kfreq_lim_min, kfreq_lim_max] + ikfreq_lim = [ikfreq_lim_min, ikfreq_lim_max] vl, is_passed_vl = get_float_or_none(self.VL_edit) vf, is_passed_vf = get_float_or_none(self.VF_edit) damping, is_passed_damping = get_float_or_none(self.damping_edit) + eas_damping_lim_min, is_passed_eas_damping_lim1 = get_float_or_none(self.eas_damping_lim_edit_min) + eas_damping_lim_max, is_passed_eas_damping_lim2 = get_float_or_none(self.eas_damping_lim_edit_max) + if is_passed_vl and vl is None: vl = -1.0 if is_passed_vf and vf is None: vf = -1.0 if is_passed_damping and damping is None: damping = -1.0 + #if is_passed_eas_damping_lim1 and eas_damping_lim_min is None: + # eas_damping_lim_min = None + #if is_passed_eas_damping_lim2 and eas_damping_lim_max is None: + # eas_damping_lim_max = None + eas_damping_lim = [eas_damping_lim_min, eas_damping_lim_max] damp_lim = [damp_lim_min, damp_lim_max] freq_lim = [freq_lim_min, freq_lim_max] kfreq_lim = [kfreq_lim_min, kfreq_lim_max] @@ -1529,6 +1567,7 @@ def get_xlim(self) -> tuple[Limit, Limit, Limit, Limit, is_passed_damp1, is_passed_damp2, is_passed_freq1, is_passed_freq2, is_passed_kfreq1, is_passed_kfreq2, + is_passed_ikfreq1, is_passed_ikfreq2, is_passed_eig, is_passed_tol1, is_passed_tol2, is_passed_tol3, is_passed_vl, is_passed_vf, is_passed_damping, @@ -1538,11 +1577,11 @@ def get_xlim(self) -> tuple[Limit, Limit, Limit, Limit, #self.log.warning(f'is_passed_flags = {is_passed_flags}') #print(f'freq_tol = {freq_tol}') out = ( - eas_lim, tas_lim, mach_lim, alt_lim, q_lim, rho_lim, xlim, - damp_lim, freq_lim, kfreq_lim, + eas_lim, tas_lim, mach_lim, alt_lim, q_lim, rho_lim, + damp_lim, freq_lim, kfreq_lim, ikfreq_lim, eigr_lim, eigi_lim, freq_tol, freq_tol_remove, mag_tol, - vl, vf, damping, is_passed, + vl, vf, damping, eas_damping_lim, is_passed, ) return out @@ -1556,11 +1595,11 @@ def get_selected_modes(self) -> list[int]: def validate(self) -> bool: #self.log.warning('validate') - (eas_lim, tas_lim, mach_lim, alt_lim, q_lim, rho_lim, xlim, - ydamp_lim, freq_lim, kfreq_lim, + (eas_lim, tas_lim, mach_lim, alt_lim, q_lim, rho_lim, + ydamp_lim, freq_lim, kfreq_lim, ikfreq_lim, eigr_lim, eigi_lim, freq_tol, freq_tol_remove, mag_tol, - vl, vf, damping, is_valid_xlim) = self.get_xlim() + vl, vf, damping, eas_damping_lim, is_valid_xlim) = self.get_xlim() selected_modes = [] subcase, is_subcase_valid = self._get_subcase() @@ -1576,7 +1615,7 @@ def validate(self) -> bool: self.alt_lim = alt_lim self.q_lim = q_lim self.rho_lim = rho_lim - self.xlim = xlim + self.ikfreq_lim = ikfreq_lim self.ydamp_lim = ydamp_lim self.kfreq_lim = kfreq_lim self.freq_lim = freq_lim @@ -1588,6 +1627,7 @@ def validate(self) -> bool: self.vl = vl self.vf = vf self.damping = damping + self.eas_damping_lim = eas_damping_lim self.x_plot_type = self.x_plot_type_pulldown.currentText() self.plot_type = self.plot_type_pulldown.currentText() @@ -1648,7 +1688,7 @@ def validate(self) -> bool: 'alt_lim': alt_lim, 'q_lim': q_lim, 'rho_lim': rho_lim, - 'xlim': xlim, + 'ikfreq_lim': ikfreq_lim, 'damp_lim': ydamp_lim, 'freq_lim': freq_lim, @@ -1664,6 +1704,7 @@ def validate(self) -> bool: 'vl': vl, 'vf': vf, 'damping': damping, + 'eas_damping_lim': eas_damping_lim, } self.units_in = units_in self.units_out = units_out diff --git a/pyNastran/f06/flutter_response.py b/pyNastran/f06/flutter_response.py index 5ea1ce550..b2ce02465 100644 --- a/pyNastran/f06/flutter_response.py +++ b/pyNastran/f06/flutter_response.py @@ -1044,12 +1044,13 @@ def _plot_x_y(self, ix: int, iy: int, symbol = symbols[jcolor] color = colors[jcolor] freq = self.results[imode, :, self.ifreq].ravel() + damping = self.results[imode, :, self.idamping].ravel() xs = self.results[imode, :, ix].ravel() ys = self.results[imode, :, iy].ravel() #print('freq, xs, ys') jcolor, color2, linestyle2, symbol2, texti = _increment_jcolor( mode, jcolor, color, linestyle, symbol, - freq, freq_tol=freq_tol, + freq, damping, freq_tol=freq_tol, show_mode_number=self.show_mode_number) #print(f'plot_xy: jcolor={jcolor}; color={color2}; linstyle={linestyle2}; symbol={symbol2}') @@ -1176,12 +1177,13 @@ def _plot_x_y2(self, ix: int, iy1: int, iy2: int, color = colors[jcolor] freq = self.results[imode, :, self.ifreq].ravel() + damping = self.results[imode, :, self.idamping].ravel() xs = self.results[imode, :, ix].ravel() y1s = self.results[imode, :, iy1].ravel() y2s = self.results[imode, :, iy2].ravel() jcolor, color2, linestyle2, symbol2, texti = _increment_jcolor( mode, jcolor, color, linestyle, symbol, - freq, freq_tol=freq_tol, + freq, damping, freq_tol=freq_tol, show_mode_number=self.show_mode_number) iplot = np.where(freq != np.nan) @@ -1447,7 +1449,6 @@ def plot_vg_vf(self, fig=None, damp_axes=None, freq_axes=None, modes=None, jcolor = 0 imodes_crossing = [] xcrossing_dict = {} - if hasattr(self, 'ieas') and plot_type == 'eas': xcrossing_dict = self.get_flutter_crossings( damping_required=damping_required, modes=modes, @@ -1475,7 +1476,7 @@ def plot_vg_vf(self, fig=None, damp_axes=None, freq_axes=None, modes=None, jcolor, color, linestyle2, symbol2, texti = _increment_jcolor( mode, jcolor, color, linestyle, symbol, - freq, freq_tol=freq_tol, + freq, damping, freq_tol=freq_tol, show_mode_number=self.show_mode_number) if color != 'gray': imodes_crossing.append(imode) @@ -1490,10 +1491,10 @@ def plot_vg_vf(self, fig=None, damp_axes=None, freq_axes=None, modes=None, #print(color, symbol, linestyle) #dfreq = freq.max() - freq.min() label = _get_mode_freq_label(mode, freq[0]) - if filter_freq and freq.min() > ylim_freq[1]: + if filter_freq and freq.min() > ylim_freq[1] and damping.max() < 0.0: # if we're entirely greater than the max, skip line continue - if filter_freq and freq.max() < ylim_freq[0]: + if filter_freq and freq.max() < ylim_freq[0] and damping.max() < 0.0: # if we're entirely below than the min, skip line continue #print(mode, color, symbol, linestyle, dfreq, freq) @@ -1623,7 +1624,7 @@ def _plot_crossings(self, # freq = self.results[imode, :, self.ifreq].ravel() # jcolor, color, linestyle2, symbol2 = _increment_jcolor( # jcolor, color, linestyle, symbol, - # freq, freq_tol) + # freq, damping, freq_tol) for case in xcrossing_dict[mode]: damping0, freq0, eas0 = case if np.isnan(eas0): @@ -2257,14 +2258,14 @@ def _get_mode_freq_label(mode: int, freq: float) -> str: freq_num = f'{freq:.3g}' # strip silly scientific notation freq_num = freq_num.replace('-0', '-').replace('-0', '-').replace('+0', '+') - label = f'Mode {mode:d}; freq={freq_num} Hz' + label = f'Mode {mode:d}; {freq_num} Hz' return label def _increment_jcolor(mode: int, jcolor: int, color: str, linestyle: str, symbol: str, - freq: np.ndarray, + freq: np.ndarray, damping: np.ndarray, freq_tol: float=-1.0, show_mode_number: bool=False, # jcolor, color, linestyle2, symbol2, text @@ -2294,7 +2295,7 @@ def _increment_jcolor(mode: int, assert isinstance(freq, np.ndarray), freq assert isinstance(freq_tol, float_types), freq_tol is_filtered = False - if freq.max() - freq.min() <= freq_tol: + if freq.max() - freq.min() <= freq_tol and damping.max() < 0.: color = 'gray' is_filtered = True jcolor -= 1 @@ -2551,7 +2552,6 @@ def _add_vertical_lines(axes_list: list[Axes], axes.axvline( x=velocity, color=vcolor, linestyle=linestyle, linewidth=linewidth) - legend_elements.append(legend_element) return legend_elements diff --git a/pyNastran/utils/dev.py b/pyNastran/utils/dev.py index 053700679..5ff9245a3 100644 --- a/pyNastran/utils/dev.py +++ b/pyNastran/utils/dev.py @@ -25,7 +25,8 @@ def get_files_of_type(dirname: str, extension: str='.txt', extension : str; default='.txt' list of filetypes to get max_size : float; default=100.0 - size in MB for max file size + >0.0: size in MB for max file size + <=0.0: no limit limit_file : str; default=no_dig.txt the presence of this file indicates no folder digging should be done on this folder @@ -65,6 +66,10 @@ def get_files_of_type(dirname: str, extension: str='.txt', #assert len(filenames2) > 0, dirnamei else: print('no digging in filename=%s; dirname=%s' % (filename, dirname)) + elif (os.path.isfile(filename) and + os.path.splitext(filenamei)[1].endswith(extension) and + max_size <= 0.0): + filenames2.append(filename) elif (os.path.isfile(filename) and os.path.splitext(filenamei)[1].endswith(extension) and os.path.getsize(filename) / 1048576. <= max_size): diff --git a/pyNastran/utils/nastran_utils.py b/pyNastran/utils/nastran_utils.py index a2e8c342f..206c4d699 100644 --- a/pyNastran/utils/nastran_utils.py +++ b/pyNastran/utils/nastran_utils.py @@ -24,7 +24,7 @@ def run_nastran(bdf_filename: PathLike, 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 - remove the *.log, *.f04, and *.plt files + remove the *.asg, *asm, *.log, *.f04, and *.plt files Returns ------- @@ -67,6 +67,8 @@ def run_nastran(bdf_filename: PathLike, if run and cleanup: base = os.path.basename(bdf_filename)[0] fnames = [ + base + '.asg', + base + '.asm', base + '.f04', base + '.log', base + '.plt',