Source code for pyNastran.bdf.mesh_utils.export_caero_mesh

"""
defines:
 - export_caero_mesh(model, caero_bdf_filename='caero.bdf', is_subpanel_model=True)

"""
from __future__ import annotations
import math
from typing import TYPE_CHECKING
import numpy as np

if TYPE_CHECKING:  # pragma: no cover
    from pyNastran.bdf.bdf import BDF
from pyNastran.bdf.field_writer_8 import print_card_8

[docs] def export_caero_mesh(model: BDF, caero_bdf_filename: str='caero.bdf', is_subpanel_model: bool=True, pid_method: str='aesurf', write_panel_xyz: bool=True) -> None: """ Write the CAERO cards as CQUAD4s that can be visualized model: BDF a valid geometry caero_bdf_filename : str the file to write is_subpanel_model : bool; default=True True : write the subpanels as CQUAD4s False : write the macro elements as CQUAD4s pid_method : str; default='aesurf' 'aesurf' : write the referenced AESURF as the property ID main structure will be pid=1 'caero' : write the CAERO1 as the property id 'paero' : write the PAERO1 as the property id write_panel_xyz : bool; default=True write the following table... $$ CAEROID EID XLE YLE ZLE CHORD SPAN XLE+C/4 $$ 1 1 0.0000 0.2500 0.0000 0.0988 0.5000 0.0247 $$ 1 2 0.0988 0.2500 0.0000 0.0988 0.5000 0.1234 """ if not pid_method in {'aesurf', 'caero', 'paero'}: raise RuntimeError(f'pid_method={pid_method!r} is not [aesurf, caero, paero]') inid = 1 mid = 1 model.log.debug(f'---starting export_caero_model of {caero_bdf_filename}---') #all_points = [] aero_eid_map = {} #if is_subpanel_model: isubpanel_ieid = 0 for caero_eid, caero in sorted(model.caeros.items()): if caero.type == 'CAERO2': model.log.warning('CAERO2 will probably cause issues...put it at the max id') continue points, elements = caero.panel_points_elements() for isubpanel_eid in range(len(elements)): aero_eid_map[isubpanel_ieid] = caero_eid + isubpanel_eid isubpanel_ieid += 1 subcases, loads = _write_subcases_loads(model, aero_eid_map, is_subpanel_model) with open(caero_bdf_filename, 'w') as bdf_file: #bdf_file.write('$ pyNastran: punch=True\n') bdf_file.write('SOL 101\n') bdf_file.write('CEND\n') bdf_file.write(subcases) bdf_file.write('BEGIN BULK\n') bdf_file.write(loads) _write_properties(model, bdf_file, pid_method=pid_method) for caero_eid, caero in sorted(model.caeros.items()): #assert caero_eid != 1, 'CAERO eid=1 is reserved for non-flaps' scaero = str(caero).rstrip().split('\n') if is_subpanel_model: if caero.type == 'CAERO2': continue bdf_file.write('$ ' + '\n$ '.join(scaero) + '\n') #bdf_file.write("$ CAEROID ID XLE YLE ZLE CHORD SPAN\n") points, elements = caero.panel_points_elements() if write_panel_xyz: _write_subpanel_strips(bdf_file, model, caero_eid, points, elements) npoints = points.shape[0] #nelements = elements.shape[0] for ipoint, point in enumerate(points): x, y, z = point bdf_file.write(print_card_8(['GRID', inid+ipoint, None, x, y, z])) #pid = caero_eid #mid = caero_eid jeid = 0 for elem in elements + inid: p1, p2, p3, p4 = elem eid2 = jeid + caero_eid pidi = _get_subpanel_property( model, caero_eid, eid2, pid_method=pid_method) fields = ['CQUAD4', eid2, pidi, p1, p2, p3, p4] bdf_file.write(print_card_8(fields)) jeid += 1 else: # macro model if caero.type == 'CAERO2': continue bdf_file.write('$ ' + '\n$ '.join(scaero) + '\n') points = caero.get_points() npoints = 4 for ipoint, point in enumerate(points): x, y, z = point bdf_file.write(print_card_8(['GRID', inid+ipoint, None, x, y, z])) pid = _get_subpanel_property( model, caero_eid, caero_eid, pid_method=pid_method) p1 = inid p2 = inid + 1 p3 = inid + 2 p4 = inid + 3 bdf_file.write(print_card_8(['CQUAD4', caero_eid, pid, p1, p2, p3, p4])) inid += npoints # aluminum E = 350e9 # 350 GPa #G = None nu = 0.3 rho = 2700. # 2700 kg/m^3 bdf_file.write(f'MAT1,{mid},{E},,{nu},{rho}\n') bdf_file.write('ENDDATA\n')
[docs] def _write_subcases_loads(model: BDF, aero_eid_map: dict[int, int], is_subpanel_model: bool) -> tuple[str, str]: subcases = '' loads = '' if len(model.dmi) == 0: return subcases, loads isubcase = 1 for name, dmi in model.dmi.items(): data, rows, cols = dmi.get_matrix(is_sparse=False, apply_symmetry=True) if name == 'WTFACT': # square matrix of (neids,neids) that has values on only? the diagonal subcases += ( f'SUBCASE {isubcase}\n' f' SUBTITLE = DMI {name} - FORCE\n' f' LOAD = {isubcase}\n') # diagonal matrix diag = data.diagonal() data = data.copy() np.fill_diagonal(data, 0.) assert np.abs(data).max() == 0.0, 'WTFACT has cross terms' loads += f'$ {name} - FORCE\n' loads += '$ PLOAD2 SID P EID1\n' for ieid, value in enumerate(diag[::2]): eid = aero_eid_map[ieid] loads += f'PLOAD2,{isubcase},{value},{eid}\n' isubcase += 1 subcases += ( f'SUBCASE {isubcase}\n' f' SUBTITLE = DMI {name} - MOMENT\n' f' LOAD = {isubcase}\n') loads += f'$ {name} - MOMENT\n' loads += '$ PLOAD2 SID P EID1\n' for ieid, value in enumerate(diag[1::2]): eid = aero_eid_map[ieid] loads += f'PLOAD2,{isubcase},{value},{eid}\n' elif name in {'W2GJ', 'FA2J'}: # column matrix of (neids,1) assert data.shape[1] == 1, data.shape # (56,1) subcases += ( f'SUBCASE {isubcase}\n' f' SUBTITLE = DMI {name}\n' f' LOAD = {isubcase}\n') # (56,1) if name == 'W2GJ': data *= 180 / np.pi loads += f'$ {name} (degrees)\n' else: loads += f'$ {name}\n' loads += '$ PLOAD2 SID P EID1\n' for ieid, value in enumerate(data.ravel()): eid = aero_eid_map[ieid] loads += f'PLOAD2,{isubcase},{value},{eid}\n' elif name == 'WKK': # column matrix of (neids*2,1) assert data.shape[1] == 1, data.shape # (112,1) subcases += ( f'SUBCASE {isubcase}\n' f' SUBTITLE = DMI {name} - FORCE\n' f' LOAD = {isubcase}\n') nrows = data.shape[0] // 2 data = data.reshape(nrows, 2) # TODO: assume first column is forces & second column is moments...verify loads += f'$ {name} - FORCE\n' loads += '$ PLOAD2 SID P EID1\n' for ieid, value in enumerate(data[:, 0].ravel()): eid = aero_eid_map[ieid] loads += f'PLOAD2,{isubcase},{value},{eid}\n' isubcase += 1 subcases += ( f'SUBCASE {isubcase}\n' f' SUBTITLE = DMI {name} - MOMENT\n' f' LOAD = {isubcase}\n') loads += f'$ {name} - MOMENT\n' loads += '$ PLOAD2 SID P EID1\n' for ieid, value in enumerate(data[:, 1].ravel()): eid = aero_eid_map[ieid] loads += f'PLOAD2,{isubcase},{value},{eid}\n' else: # pragma: no cover msg = f'{name}:\n' msg += str(data) raise NotImplementedError(msg) isubcase += 1 if not is_subpanel_model: # we put this here to test model.log.warning('cannot export "loads" because not a subpanel model') subcases = '' loads = '' return subcases, loads return subcases, loads
[docs] def _write_subpanel_strips(bdf_file, model, caero_eid, points, elements): """writes the strips for the subpanels""" #bdf_file.write("$ CAEROID ID XLE YLE ZLE CHORD SPAN\n") bdf_file.write('$$\n$$ XYZ_LE is taken at the center of the leading edge; (p1+p4)/2\n$$\n') bdf_file.write('$$ %8s %8s %9s %9s %9s %9s %9s %9s %9s\n' % ( 'CAEROID', 'EID', 'XLE', 'YLE', 'ZLE', 'CHORD', 'SPAN', 'XLE+C/4', 'XLE+C/2')) for i in range(elements.shape[0]): # The point numbers here are consistent with the CAERO1 p1 = points[elements[i, 0], :] p4 = points[elements[i, 1], :] p2 = points[elements[i, 2], :] p3 = points[elements[i, 3], :] le = (p1 + p4)*0.5 te = (p2 + p3)*0.5 dy = (p4 - p1)[1] dz = (p4 - p1)[2] span = math.sqrt(dy**2 + dz**2) chord = te[0] - le[0] xqc = le[0] + chord / 4. xmid = le[0] + chord / 2. bdf_file.write("$$ %8d %8d %9.4f %9.4f %9.4f %9.4f %9.4f %9.4f %9.4f\n" % ( caero_eid, caero_eid+i, le[0], le[1], le[2], chord, span, xqc, xmid))
[docs] def _get_subpanel_property(model: BDF, caero_id: int, eid: int, pid_method: str='aesurf') -> int: """gets the property id for the subpanel""" pid = None if pid_method == 'aesurf': for aesurf_id, aesurf in model.aesurf.items(): aelist_id = aesurf.Aelist_id1() aelist = model.aelists[aelist_id] if eid in aelist.elements: pid = aesurf_id break elif pid_method == 'caero': pid = caero_id elif pid_method == 'paero': caero = model.caeros[caero_id] pid = caero.pid else: # pragma: no cover raise RuntimeError(f'pid_method={pid_method!r} is not [aesurf, caero, paero]') if pid is None: pid = 1 return pid
def _write_properties(model: BDF, bdf_file, pid_method: str='aesurf') -> None: if pid_method == 'aesurf': _write_aesurf_properties(model, bdf_file) elif pid_method == 'paero': for paero_id, paero in sorted(model.paeros.items()): spaero = str(paero).rstrip().split('\n') bdf_file.write('$ ' + '\n$ '.join(spaero) + '\n') bdf_file.write('PSHELL,%s,%s,0.1\n' % (paero_id, 1)) elif pid_method == 'caero': for caero_eid, caero in sorted(model.caeros.items()): scaero = str(caero).rstrip().split('\n') bdf_file.write('$ ' + '\n$ '.join(scaero) + '\n') bdf_file.write('PSHELL,%s,%s,0.1\n' % (caero_eid, 1)) else: # pragma: no cover raise RuntimeError(f'pid_method={repr(pid_method)} is not [aesurf, caero, paero]')
[docs] def _write_aesurf_properties(model: BDF, bdf_file): aesurf_mid = 1 for aesurf_id, aesurf in model.aesurf.items(): #cid = aesurf.cid1 #aesurf_mid = aesurf_id saesurf = str(aesurf).rstrip().split('\n') bdf_file.write('$ ' + '\n$ '.join(saesurf) + '\n') bdf_file.write('PSHELL,%s,%s,0.1\n' % (aesurf_id, aesurf_mid)) #print(cid) #ax, ay, az = cid.i #bx, by, bz = cid.j #cx, cy, cz = cid.k #bdf_file.write('CORD2R,%s,,%s,%s,%s,%s,%s,%s\n' % ( #cid, ax, ay, az, bx, by, bz)) #bdf_file.write(',%s,%s,%s\n' % (cx, cy, cz)) #print(cid) #aesurf.elements # dummy property bdf_file.write('PSHELL,%s,%s,0.1\n' % (1, 1)) return