Source code for pyNastran.bdf.cards.collpase_card

"""
defines:
 - collapse_thru_by(fields, get_packs=False)
 - collapse_thru_by_float(fields)
 - collapse_thru(fields, nthru=None)
 - collapse_thru_packs(fields)
 - collapse_colon_packs(fields, thru_split=3)
 - condense(value_list)
 - build_thru_packs(packs, max_dv=1, thru_split=3)
 - build_thru(packs, max_dv=None, nthru=None)
 - build_thru_float(packs, max_dv=None)

"""
from collections import Counter
from typing import Union, Optional
import numpy as np


[docs] def collapse_thru_by(fields: list[int], get_packs: bool=False) -> list[list[int]]: """ Parameters ---------- fields : list[int] the list of fields to collapse get_packs : bool; default=False get the list of packs so "special" formatting can be done fields packs [1, 2, 3...150] -> [1, 150, 1] [1, 3, 5...150] -> [1, 150, 2] """ _check_sort_fields(fields) packs = condense(fields) if get_packs: return packs fields2 = build_thru(packs) #assert fields == expand_thru_by(fields2) # why doesn't this work? return fields2
[docs] def collapse_thru_by_float(fields: list[float]) -> list[Union[int, str]]: _check_sort_fields(fields) packs = condense(fields) fields2 = build_thru_float(packs) #assert fields == expand_thru_by(fields2) # why doesn't this work? return fields2
[docs] def collapse_thru(fields, nthru: Optional[int]=None) -> list[tuple[int, int, int]]: """ Collapses fields into a set of packs Parameters ---------- fields : list[int, int, ...] the list of integers to compress nthru : int; default=1 currently unused Returns ------- packs = list[pack] pack = list[int first_val, int last_val, int_by] """ _check_sort_fields(fields) packs = condense(fields) fields2 = build_thru(packs, max_dv=1) # , nthru=nthru if nthru is not None and Counter(fields2)['THRU'] > 2: return fields #assert fields == expand_thru_by(fields2), fields2 # why doesn't this work? return fields2
[docs] def _check_sort_fields(fields): if isinstance(fields, np.ndarray): # if an int array, we don't need to test it beyond that if not isinstance(fields[0], (np.int32, np.int64)): # pragma: no cover raise NotImplementedError(f'fields={fields}; dtype={fields.dtype}') #'THRU' in fields #assert not np.any(''), fields elif isinstance(fields, (list, tuple)): assert 'THRU' not in fields, fields else: # pragma: no cover raise NotImplementedError(f'fields={fields}; type={type(fields)}') fields.sort()
[docs] def collapse_thru_packs(fields): assert isinstance(fields, list), fields assert 'THRU' not in fields, fields packs = condense(fields) singles, doubles = build_thru_packs(packs, max_dv=1) #assert fields == expand_thru_by(fields2), fields2 # why doesn't this work? return singles, doubles
[docs] def collapse_colon_packs(fields, thru_split=3): """ Applies colons (:) to packs to represent THRU and BY as is used by Patran. Parameters ---------- fields : list[int] the values to collapse thru_split : int; default=3 the length to not write THRU 3 : [10, 11, 12] will write as '10 THRU 12' 4 : [10, 11, 12] will write as '10 11 12' Returns ------- singles : list[int] the list of singles doubles : list[pack] pack : list[varies] [3, :, 13] [3, :, 13, :, 5] the double packs # invalid SET1,4000, 1, 3, :, 10, 20, :, 30 # valid SET1,4000, 1 SET1,4000, 3, :, 10 SET1,4000, 20, :, 30 # output singles = [1] doubles = [[3, ':', 10], [20, ':', 30]] """ packs = condense(fields) singles, doubles = build_thru_packs(packs, max_dv=None, thru_split=thru_split) doubles2 = [] for double in doubles: if len(double) == 3: double[1] = ':' elif len(double) == 5: double[1] = ':' double[3] = ':' else: raise RuntimeError(double) doubles2.append(double) return singles, doubles2
[docs] def condense(value_list): """ Builds a list of packs (list of 3 values representing the first, last, and delta values for condensing a SET card. Parameters ---------- value_list : list[int] list of values to pack Returns ------- packs : list[pack] pack : list[id_low, id_high, delta_id] a list representation of the min/max/delta id values .. seealso:: build_thru """ if len(value_list) == 0: return [] if len(value_list) == 1: return [[value_list[0], value_list[0], 1]] value_list.sort() packs = [] dv_old = None first_val = value_list[0] last_val = first_val for val in value_list[1:]: try: dv = val - last_val except TypeError: print("last_val=%r val=%r" % (last_val, val)) print("value_list=%r" % value_list) raise # sets up the first item of the pack if dv_old is None: dv_old = dv # fill up the pack if dv_old == dv: last_val = val else: packs.append([first_val, last_val, dv_old]) last_val = val dv_old = None first_val = val # fills the last pack if dv_old == dv: packs.append([first_val, val, dv]) else: packs.append([first_val, val, dv_old]) return packs
[docs] def build_thru_packs(packs, max_dv=1, thru_split=3): """ Applies THRU and BY to packs to shorten output as Nastran does on cards like the SPOINT Parameters ---------- packs : list[pack] pack : list[id_low, id_high, delta_id] a list representation of the min/max/delta id values max_dv : int; default=1 maximum allowed delta between two values thru_split : int; default=3 the length to not write THRU 3 : [10, 11, 12] will write as '10 THRU 12' 4 : [10, 11, 12] will write as '10 11 12' Returns ------- singles : list[int] the list of singles doubles : list[pack] pack : list[varies] [3, THRU, 13] [3, THRU, 13, BY, 5] the double packs # invalid SET1,4000, 1, 3, THRU, 10, 20, THRU, 30 # valid SET1,4000, 1 SET1,4000, 3, THRU, 10 SET1,4000, 20, THRU, 30 returns singles = [1] doubles = [[3, 'THRU', 10], [20, 'THRU', 30]] """ singles = [] doubles = [] for (first_val, last_val, by) in packs: if first_val == last_val: singles.append(first_val) else: if by == 1: if last_val - first_val < thru_split: # dont make extra THRU cards singlei = list(range(first_val, last_val + 1, 1)) singles += singlei else: double = [first_val, 'THRU', last_val] doubles.append(double) else: diff = last_val - first_val if max_dv == 1 or diff == by: singlei = list(range(first_val, last_val + by, by)) singles += singlei else: double = [first_val, 'THRU', last_val, 'BY', by] doubles.append(double) return singles, doubles
[docs] def build_thru(packs, max_dv: Optional[int]=None, nthru: Optional[int]=None): """ Takes a pack [1,7,2] and converts it into fields used by a SET card. The values correspond to the first value, last value, and delta in the list. This means that [1,1001,2] represents 500 values. [1,1001,1] represents 1001 values and will be written as [1,THRU,1001].. Parameters ---------- packs : list[pack] pack : list[int first, int last, int delta] the first, last, delta id values max_dv : int; default=None -> no limit defines the max allowable delta between two values nthru : int; default=None don't use this; it will crash Returns ------- value : varies the value of the field """ #singles = [] fields = [] if nthru is not None: raise NotImplementedError('nthru=%s' % nthru) #assert nthru == 1, nthru # others #assert nthru == 1, nthru #packs2 = [] #nvalues = [] #for (first_val, last_val, dv) in packs: #nvalue = (last_val - first_val + 1) // dv ##print('first=%s last=%s delta=%s dv=%s n=%s' % ( ##first_val, last_val, last_val-first_val, dv, nvalue)) #nvalues.append(nvalue) #i = nvalues.index(max(nvalues)) ##print('nvalues =', nvalues, i) #packs = [] for (first_val, last_val, dv) in packs: if first_val == last_val: fields.append(first_val) elif dv == 1: if last_val - first_val > 2: fields.extend([first_val, 'THRU', last_val]) elif last_val - first_val == 2: # no point in writing 'A THRU A+2' fields.extend([first_val, first_val + 1, last_val]) else: # no point in writing 'A THRU A+1' fields.append(first_val) fields.append(last_val) else: if max_dv is None: # no point in writing 'A THRU B BY C' if last_val - first_val > 4 * dv: fields.extend([first_val, 'THRU', last_val, 'BY', dv]) else: fields += list(range(first_val, last_val + dv, dv)) else: for v in range(first_val, last_val + dv, dv): fields.append(v) return fields
[docs] def build_thru_float(packs: list[list[int]], max_dv: Optional[int]=None) -> list[Union[int, str]]: """ Takes a pack [1,7,2] and converts it into fields used by a SET card. The values correspond to the first value, last value, and delta in the list. This means that [1,1001,2] represents 500 values. [1,1001,1] represents 1001 values and will be written as [1,THRU,1001]. Parameters ---------- packs : list[pack] pack : list[first, last, delta] first, last, delta are integers max_dv : int; default=None -> no limit integer defining the max allowable delta between two values (default=None; no limit) """ fields = [] for (first_val, last_val, dv) in packs: if last_val - first_val > 4 * dv: fields.extend([first_val, 'THRU', last_val, 'BY', dv]) else: nv = int(round((last_val - first_val) / dv)) + 1 for i in range(nv): v = first_val + i * dv fields.append(v) return fields