Source code for pyNastran.bdf.mesh_utils.collapse_bad_quads

"""
defines:
 - convert_bad_quads_to_tris(model, eids_to_check=None, xyz_cid0=None,
                             min_edge_length=0.0)

"""
from __future__ import annotations
from typing import Optional, TYPE_CHECKING
import numpy as np
from pyNastran.bdf.cards.elements.shell import CTRIA3
if TYPE_CHECKING:  # pragma: no cover
    from pyNastran.bdf.bdf import BDF


[docs] def convert_bad_quads_to_tris(model: BDF, eids_to_check: list[int]=None, xyz_cid0: Optional[np.ndarray]=None, min_edge_length: float=0.0) -> None: """ A standard quad is a nice rectangle. If an edge is collapsed, it's a triangle. Change the element type with an inplace operation. Deletes any line elements created. Parameters ---------- model : BDF() a BDF model that has not had it's properties/load xref'd, but is valid such that it could eids : list; (default=None -> all CQUAD4s) the subset of element ids to check xyz_cid0 : (n, 3) ndarray nodes in cid=0 min_edge_length : float; default=0.0 what is classified as "short" .. warning:: Don't cross reference properties/loads .. todo:: check for bad xref """ log = model.log out = model.get_card_ids_by_card_types('CQUAD4') cquad4s = out['CQUAD4'] if eids_to_check is None: cquad4s_to_check = cquad4s else: cquad4s_to_check = list(set(eids_to_check).intersection(set(cquad4s))) elements = model.elements unused_eids_to_remove = [] if xyz_cid0 is None: xyz_cid0 = model.get_xyz_in_coord(cid=0) nid_cd = np.array([[nid, node.Cd()] for nid, node in sorted(model.nodes.items())]) all_nids = nid_cd[:, 0] if len(cquad4s_to_check) == 0: log.warning('no quads in the model...') return for eid in sorted(cquad4s_to_check): elem = elements[eid] nids = elem.node_ids nids_long = nids + nids nids_to_remove = [] for inode in range(4): nid0 = nids_long[inode] nid1 = nids_long[inode + 1] edge = [nid0, nid1] i = np.searchsorted(all_nids, edge) xyz = xyz_cid0[i, :] edge_length = np.linalg.norm(xyz[0, :] - xyz[1, :]) if edge_length <= min_edge_length: nids_to_remove.append(nid1) nnodes_to_remove = len(nids_to_remove) if len(set(nids)) < 4 and nnodes_to_remove == 0: log.warning(f"removing eid={eid} because its a degenerate element, but it's not a triangle...\n{elem}") etype = elem.type model.card_count[etype] -= 1 del model.elements[eid] if nnodes_to_remove == 0: continue for nid in nids_to_remove: nids.remove(nid) if len(nids) < 3: log.debug('found eid=%s is a line/point...removing' % eid) etype = elem.type model.card_count[etype] -= 1 del model.elements[eid] continue nids_to_remove = [] nids_long = nids + nids for inode in range(3): nid0 = nids_long[inode] nid1 = nids_long[inode + 1] edge = [nid0, nid1] i = np.searchsorted(all_nids, edge) xyz = xyz_cid0[i, :] edge_length = np.linalg.norm(xyz[0, :] - xyz[1, :]) if edge_length <= min_edge_length: nids_to_remove.append(nid1) for nid in nids_to_remove: nids.remove(nid) if len(nids) < 3: log.debug('found eid=%s is a line/point...removing' % eid) etype = elem.type model.card_count[etype] -= 1 del model.elements[eid] continue log.debug('found eid=%s is a triangle...replacing CQUAD4 with CTRIA3' % eid) #print(' nids2=%s' % (nids)) assert elem.tflag == 0, elem assert elem.T1 is None, elem.T1 assert elem.T2 is None, elem.T2 assert elem.T3 is None, elem.T3 assert elem.T4 is None, elem.T4 elem2 = CTRIA3(eid, elem.Pid(), nids, elem.zoffset, theta_mcid=elem.theta_mcid, tflag=0, T1=None, T2=None, T3=None, comment='$ was a CQUAD4\n') model.increase_card_count('CTRIA3') del model.elements[eid] model.elements[eid] = elem2
[docs] def delete_bad_tris(model: BDF, eids_to_check: list[int]=None, xyz_cid0: Optional[np.ndarray]=None, min_edge_length: float=0.0) -> None: """ If an triangular edge is collapsed, it's a line and should be deleted. Parameters ---------- model : BDF() a BDF model that has not had it's properties/load xref'd, but is valid such that it could eids : list; (default=None -> all CTRIA3s) the subset of element ids to check xyz_cid0 : (n, 3) ndarray nodes in cid=0 min_edge_length : float; default=0.0 what is classified as "short" .. warning:: Don't cross reference properties/loads .. todo:: check for bad xref """ out = model.get_card_ids_by_card_types('CTRIA3') ctria3s = out['CTRIA3'] if eids_to_check is None: ctria3s_to_check = ctria3s else: ctria3s_to_check = list(set(eids_to_check).intersection(set(ctria3s))) elements = model.elements unused_eids_to_remove = [] if xyz_cid0 is None: xyz_cid0 = model.get_xyz_in_coord(cid=0) nid_cd = np.array([[nid, node.Cd()] for nid, node in sorted(model.nodes.items())]) all_nids = nid_cd[:, 0] if len(ctria3s_to_check) == 0: model.log.warning('no tris in the model...') return for eid in sorted(ctria3s_to_check): elem = elements[eid] nids = elem.node_ids nids_long = nids + nids nids_to_remove = [] for inode in range(3): nid0 = nids_long[inode] nid1 = nids_long[inode + 1] edge = [nid0, nid1] i = np.searchsorted(all_nids, edge) xyz = xyz_cid0[i, :] edge_length = np.linalg.norm(xyz[0, :] - xyz[1, :]) if edge_length <= min_edge_length: nids_to_remove.append(nid1) if len(nids_to_remove) == 0: continue for nid in nids_to_remove: nids.remove(nid) if len(nids) < 3: model.log.debug('found eid=%s is a line/point...removing' % eid) etype = elem.type model.card_count[etype] -= 1 del model.elements[eid] continue