from __future__ import annotations
from typing import Any, Union, TYPE_CHECKING
if TYPE_CHECKING: # pragma: no cover
from pyNastran.bdf.bdf import BDF
[docs]
def get_bdf_stats(model: BDF, return_type: str='string',
word: str='') -> Union[str, list[str]]:
"""
Print statistics for the BDF
Parameters
----------
return_type : str (default='string')
the output type ('list', 'string')
'list' : list of strings
'string' : single, joined string
word : str; default=''
model flag
Returns
-------
return_data : str, optional
the output data
.. note:: if a card is not supported and not added to the proper
lists, this method will fail
.. todo:: RBE3s from OP2s can show up as ???s
"""
card_dict_groups = [
'params', 'nodes', 'spoints', 'epoints', 'points', 'gridb',
'coords',
'elements', 'ao_element_flags', 'normals', 'rigid_elements', 'plotels',
'properties', 'pbusht', 'pdampt', 'pelast',
'properties_mass', 'masses',
'materials', 'creep_materials', 'hyperelastic_materials',
'MATT1', 'MATT2', 'MATT3', 'MATT4', 'MATT5', 'MATT8', 'MATT9',
'MATS1', 'MATS3', 'MATS8', 'MATT8',
'MATCID', 'MATDMG',
# axisysmmetric
'ringaxs', 'ringfl',
# dynamic cards
'dareas', 'delays', 'dphases', 'nlparms', 'nlpcis',
'tsteps', 'tstepnls',
'rotors',
# direct matrix input - DMIG - dict
'dmi', 'dmig', 'dmij', 'dmiji', 'dmik', 'dmiax',
'dequations',
'transfer_functions',
'tics',
# frequencies - dict[list[FREQ]]
'frequencies', 'acplnw', 'amlreg', 'micpnt',
# optimization - dict
'dconadds', 'dconstrs', 'desvars', 'topvar', 'ddvals', 'dlinks', 'dresps',
'dvcrels', 'dvmrels', 'dvprels', 'dvgrids',
# nx optimization - dict
'dmncon', 'dvtrels', 'group',
# SESETx - dict
'suport1',
# tables
'tables', 'tables_d', 'tables_m', 'random_tables', 'tables_sdamping',
# methods
'methods', 'cMethods',
# aero
'caeros', 'paeros', 'aecomps', 'aefacts', 'aelinks',
'aelists', 'aeparams', 'aesurf', 'aesurfs', 'aestats', 'gusts', 'flfacts',
'flutters', 'splines', 'trims', 'divergs', 'csschds',
# thermal
'bcs', 'thermal_materials', 'phbdys', 'views', 'view3ds',
'convection_properties',
# contact
'bsurf', 'bsurfs', 'blseg', 'bfric',
'bconp', 'bcrparas', 'bctadds', 'bctparas', 'bctsets',
'bgadds', 'bgsets', 'bctparms',
'bcbodys', 'bcparas',
# sets
'sets', 'usets',
# superelements
'csuper', 'csupext',
'sebulk', 'sebndry', 'seconct', 'seelt', 'seexcld',
'selabel', 'seloc', 'seload', 'sempln', 'senqset',
'setree',
'se_sets', 'se_usets', 'release',
# ???
'dscreen', 'dti', 'nxstrats', 'radcavs', 'radmtx',
'tempds', 'spcoffs',
'mpcs',
# cyclic
'cyjoin',
# parametric
'feedge', 'feface', 'gmcurv', 'gmsurf', 'pset', 'pval',
]
scalar_attrs = [
'app',
'aero', 'aeros', 'grdset', # handled below
'axic', 'axif', 'cyax', 'modtrak',
'mdlprm',
# not handled
'acmodl',
'baror', 'beamor', 'doptprm', 'dtable',
'zona',
]
list_attrs = [
'asets', 'bsets', 'csets', 'omits', 'qsets',
'se_bsets', 'se_csets', 'se_qsets',
'suport', 'se_suport',
'monitor_points',
]
skip_attrs = [
'model_groups',
'active_filename', 'active_filenames', 'debug', # 'log',
'reject_lines',
'is_nx', 'is_msc', 'is_optistruct', 'is_zona', 'is_mystran', 'is_nasa95',
'is_bdf_vectorized', 'dumplines', 'values_to_skip',
'system_command_lines', 'executive_control_lines', 'case_control_lines',
'case_control_deck',
'is_superelements', 'special_cards', 'units',
'sol', 'sol_iline', 'sol_method', 'cards_to_read', 'card_count',
'superelement_models', 'wtmass', 'echo', 'force_echo_off',
'read_includes', 'reject_cards', 'reject_count', 'punch',
'include_dir', 'include_filenames', 'save_file_structure',
'rsolmap_to_str', 'nastran_format', 'nid_map', 'bdf_filename',
'initial_superelement_models',
'type_slot_str', 'dict_of_vars', 'code_block',
# handled below
'mpcadds', 'mpcs', 'spcadds', 'spcs',
'loads', 'load_combinations',
'dloads', 'dload_entries',
'aero', 'aeros', 'mkaeros',
'nsmadds', 'nsms',
'seqgp',
# unhandled
'radset',
'dmigs', 'dmijis', 'dmijs', 'dmiks', 'dmis',
# vector
'cbar', 'cbeam', 'cbush',
'conm2',
'cshear', 'cvisc',
'conrod', 'ctube', 'crod',
'cdamp1', 'cdamp2', 'cdamp3', 'cdamp4',
'celas1', 'celas2', 'celas3', 'celas4',
'chexa20', 'chexa8', 'cpenta15', 'cpenta6',
'cpyram13', 'cpyram5', 'ctetra10', 'ctetra4',
'cquad', 'cquad4', 'cquad8', 'cquadr',
'ctria3', 'ctria6', 'ctriar',
'plotel',
'dampers', 'elements2',
'bars', 'beams', 'bushes', 'rods', 'shears',
'shells', 'solids', 'springs',
'force', 'force1', 'force2', 'grav', 'grid', 'lseq', 'masses2',
'moment', 'moment1', 'moment2', 'pload', 'pload1', 'pload2', 'pload4',
'sload', 'spcd',
'temp', 'tempd',
# ----
#new
'bolt', 'boltld', 'boltfor', 'boltseq', 'boltfrc',
'use_new_deck_parser',
] + list_attrs + card_dict_groups + scalar_attrs
missed_attrs = []
for attr in model.object_attributes(filter_properties=True,
keys_to_skip=skip_attrs):
#if attr in skip_attrs:
#continue
missed_attrs.append(attr)
if model.__class__.__name__ == 'BDF':
assert missed_attrs == [], missed_attrs
# These are ignored because they're lists
#ignored_types = set([
#'spoints', 'spointi', # singleton
#'grdset', # singleton
#'spcs',
#'suport', 'se_suport', # suport, suport1 - list
#'doptprm', # singleton
## SETx - list
#'sets', 'asets', 'bsets', 'csets', 'qsets',
#'se_bsets', 'se_csets', 'se_qsets',
#])
## TODO: why are some of these ignored?
#ignored_types2 = set([
#'case_control_deck', 'caseControlDeck',
## done
#'sol', 'loads', 'mkaeros',
#'reject_lines', 'reject_cards',
## not cards
#'debug', 'executive_control_lines',
#'case_control_lines', 'cards_to_read', 'card_count',
#'is_structured', 'uniqueBulkDataCards',
#'model_type', 'include_dir',
#'sol_method', 'log',
#'sol_iline',
#'reject_count', '_relpath',
#'special_cards',])
#unsupported_types = ignored_types.union(ignored_types2)
#all_params = object_attributes(model, keys_to_skip=unsupported_types)
msg = ['---BDF Statistics%s---' % word]
# sol
if 'Superelement' not in word:
msg.append('SOL %s\n' % model.sol)
msg.extend(_get_bdf_stats_loads(model))
# load_combinations / loads: handled below
#handled_explicitly = [
#'dloads', 'dload_entries',
#'spcadds', 'spcs',
#'mpcadds', 'mpcs',
#'nsmadds', 'nsms',
#'aero', 'aeros', 'mkaeros', 'radset',
#'seqgp',
#]
# dloads
for (lid, loads) in sorted(model.dloads.items()):
groups_dict = {} # type: dict[str, Any]
for loadi in loads:
groups_dict[loadi.type] = groups_dict.get(loadi.type, 0) + 1
added_messge = _get_added_message_from_dict(groups_dict)
msg.append(f'bdf.dloads[{lid}]{added_messge}')
for name, count_name in sorted(groups_dict.items()):
msg.append(' %-8s %s' % (name + ':', count_name))
msg.append('')
for (lid, loads) in sorted(model.dload_entries.items()):
groups_dict = {}
for loadi in loads:
groups_dict[loadi.type] = groups_dict.get(loadi.type, 0) + 1
added_messge = _get_added_message_from_dict(groups_dict)
msg.append(f'bdf.dload_entries[{lid}]{added_messge}')
for name, count_name in sorted(groups_dict.items()):
msg.append(' %-8s %s' % (name + ':', count_name))
msg.append('')
_constraint_stats(model, msg)
_nsm_stats(model, msg)
_cyclic_stats(model, msg)
_aero_stats(model, msg)
# radset
if model.radset:
msg.append('bdf:radset')
msg.append(' %-8s 1' % ('RADSET:'))
#seqgp
if model.seqgp:
msg.append('bdf:seqgp')
msg.append(' %-8s 1' % ('SEQGP:'))
for card_group_name in card_dict_groups:
try:
card_group = getattr(model, card_group_name)
except AttributeError:
msgi = 'cant find card_group_name=%r' % card_group_name
raise AttributeError(msgi)
groups = set() # type: set[str]
if not isinstance(card_group, dict):
msgi = '%s is a %s; not dictionary, which is required by get_bdf_stats()' % (
card_group_name, type(card_group))
model.log.error(msgi)
continue
#raise RuntimeError(msg)
for card in card_group.values():
if isinstance(card, list):
for card2 in card:
groups.add(card2.type)
else:
groups.add(card.type)
group_msg = []
ncards_total = 0
for card_name in sorted(groups):
try:
ncards = model.card_count[card_name]
group_msg.append(' %-8s : %s' % (card_name, ncards))
ncards_total += ncards
except KeyError:
# we get in here because we used add_grid or similar method, which
# doesn't increase the card_count, so instead we'll use _type_to_id_map
counter = '???'
if card_name in model._type_to_id_map:
counter = len(model._type_to_id_map[card_name])
if card_name == 'CORD2R' and counter == '???':
# there is always 1 CORD2R that isn't added to card_count/_type_to_id_map
continue
group_msg.append(' %-8s : %s' % (card_name, counter))
#assert card_name == 'CORD2R', model.card_count
if group_msg:
added_msg = _get_added_message(group_msg, ncards_total)
msg.append(f'bdf.{card_group_name}{added_msg}')
msg.append('\n'.join(group_msg))
msg.append('')
if model.reject_lines: # list[card]; card = list[str]
msg.append('Rejected Cards')
for name, counter in sorted(model.card_count.items()):
if name not in model.cards_to_read:
msg.append(' %-8s %s' % (name + ':', counter))
msg.append('')
for superelement_tuple, superelement in model.superelement_models.items():
if isinstance(superelement_tuple, int):
wordi = f' (Superelement {superelement_tuple:d})'
else:
word, value, label = superelement_tuple
if label:
wordi = f'BEGIN {word}={value:d} LABEL={label}\n'
else:
wordi = f'BEGIN {word}={value:d}\n'
msg += get_bdf_stats(superelement, return_type='list', word=wordi)
if return_type == 'string':
return '\n'.join(msg)
return msg
[docs]
def _get_added_message_from_dict(groups_dict: dict[str, int]) -> str:
ncards_total = sum(groups_dict.values())
msg = _get_added_message(groups_dict, ncards_total)
return msg
[docs]
def _get_added_message(group_msg: list[str], ncards_total) -> str:
#added_msg = ''
#if len(group_msg) > 1:
added_msg = f': {ncards_total}'
return added_msg
[docs]
def _constraint_stats(model: BDF, msg: list[str]) -> None:
"""helper for ``get_bdf_stats(...)``"""
# spcs
for (spc_id, spcadds) in sorted(model.spcadds.items()):
groups_dict = {}
for spcadd in spcadds:
groups_dict[spcadd.type] = groups_dict.get(spcadd.type, 0) + 1
added_messge = _get_added_message_from_dict(groups_dict)
msg.append(f'bdf.spcadds[{spc_id}]{added_messge}')
for name, count_name in sorted(groups_dict.items()):
msg.append(' %-8s %s' % (name + ':', count_name))
msg.append('')
for (spc_id, spcs) in sorted(model.spcs.items()):
groups_dict = {}
for spc in spcs:
groups_dict[spc.type] = groups_dict.get(spc.type, 0) + 1
added_messge = _get_added_message_from_dict(groups_dict)
msg.append(f'bdf.spcs[{spc_id}]{added_messge}')
for name, count_name in sorted(groups_dict.items()):
msg.append(' %-8s %s' % (name + ':', count_name))
msg.append('')
# mpcs
for (mpc_id, mpcadds) in sorted(model.mpcadds.items()):
groups_dict = {}
for mpcadd in mpcadds:
groups_dict[mpcadd.type] = groups_dict.get(mpcadd.type, 0) + 1
added_messge = _get_added_message_from_dict(groups_dict)
msg.append(f'bdf.mpcadds[{mpc_id}]{added_messge}')
for name, count_name in sorted(groups_dict.items()):
msg.append(' %-8s %s' % (name + ':', count_name))
msg.append('')
for (mpc_id, mpcs) in sorted(model.mpcs.items()):
groups_dict = {}
for mpc in mpcs:
groups_dict[mpc.type] = groups_dict.get(mpc.type, 0) + 1
added_messge = _get_added_message_from_dict(groups_dict)
msg.append(f'bdf.mpcs[{mpc_id}]{added_messge}')
for name, count_name in sorted(groups_dict.items()):
msg.append(' %-8s %s' % (name + ':', count_name))
msg.append('')
[docs]
def _cyclic_stats(model: BDF, msg: list[str]) -> None:
"""helper for ``get_bdf_stats(...)``"""
if model.cyax:
msg.append('bdf.cyax')
msg.append(' %-8s 1' % ('CYAX:'))
if model.cyjoin:
msg.append('bdf:cyjoin')
msg.append(' %-8s %s' % ('CYJOIN:', len(model.cyjoin)))
[docs]
def _aero_stats(model: BDF, msg: list[str]) -> None:
"""helper for ``get_bdf_stats(...)``"""
if model.aero:
msg.append('bdf.aero')
msg.append(' %-8s 1' % ('AERO:'))
# aeros
if model.aeros:
msg.append('bdf:aeros')
msg.append(' %-8s 1' % ('AEROS:'))
#mkaeros
if model.mkaeros:
msg.append('bdf:mkaeros')
msg.append(' %-8s %s' % ('MKAERO:', len(model.mkaeros)))
[docs]
def _nsm_stats(model: BDF, msg: list[str]) -> None:
"""helper for ``get_bdf_stats(...)``"""
# nsms
for (nsm_id, nsmadds) in sorted(model.nsmadds.items()):
msg.append('bdf.nsmadds[%s]' % nsm_id)
groups_dict = {}
for nsmadd in nsmadds:
groups_dict[nsmadd.type] = groups_dict.get(nsmadd.type, 0) + 1
for name, count_name in sorted(groups_dict.items()):
msg.append(' %-8s %s' % (name + ':', count_name))
msg.append('')
for (mpc_id, nsms) in sorted(model.nsms.items()):
msg.append('bdf.nsms[%s]' % mpc_id)
groups_dict = {}
for nsm in nsms:
groups_dict[nsm.type] = groups_dict.get(nsm.type, 0) + 1
for name, count_name in sorted(groups_dict.items()):
msg.append(' %-8s %s' % (name + ':', count_name))
msg.append('')
def _get_bdf_stats_loads(model: BDF) -> list[str]:
"""helper for ``get_bdf_stats(...)``"""
# loads
msg = []
if model.is_bdf_vectorized:
## kind of hackish
for (lid, load_combination) in sorted(model.load_combinations.items()):
msg.append('bdf.load_combinations[%s]' % lid)
msg.append('')
if len(model.loads):
msg.append('bdf.loads[%s] : ???')
else:
for (lid, load_combinations) in sorted(model.load_combinations.items()):
groups_dict: dict[str, int] = {}
for load_combination in load_combinations:
groups_dict[load_combination.type] = groups_dict.get(load_combination.type, 0) + 1
added_messge = _get_added_message_from_dict(groups_dict)
msg.append(f'bdf.load_combinations[{lid}]{added_messge}')
for name, count_name in sorted(groups_dict.items()):
msg.append(' %-8s %s' % (name + ':', count_name))
msg.append('')
for (lid, loads) in sorted(model.loads.items()):
groups_dict = {}
for loadi in loads:
groups_dict[loadi.type] = groups_dict.get(loadi.type, 0) + 1
added_messge = _get_added_message_from_dict(groups_dict)
msg.append(f'bdf.loads[{lid}]{added_messge}')
for name, count_name in sorted(groups_dict.items()):
msg.append(' %-8s %s' % (name + ':', count_name))
msg.append('')
return msg