def element_matrices_index_table(self): """Return element matrices index table""" if self._element_matrices_index_table is None: with open(self.filename, 'rb') as f: f.seek(self.header['ptrIDX'] * 4) self._element_matrices_index_table = read_table(f) return self._element_matrices_index_table
def get_n_comp(filename): """Get the number of node and element components""" with open(filename, 'rb') as f: f.seek(103 * 4) for _ in range(200): tablesize = read_table(f, skip=True) # print(tablesize) if tablesize == 1240: break if tablesize == 1240: for _ in range(6): read_table(f, skip=True) return read_table(f, nread=2) raise Exception('Can not find node and element component length table')
def eeqv(self): """Element equivalence table. This table equates the number used for storage to the actual element number. """ if self._eeqv is None: with open(self.filename, 'rb') as f: f.seek(self.header['ptrELM'] * 4) self._eeqv = read_table(f) return self._eeqv
def neqv(self): """Nodal equivalence table. This table equates the number used for storage to the actual node number. """ if self._neqv is None: with open(self.filename, 'rb') as f: f.seek(self.header['ptrBAC'] * 4) self._neqv = read_table(f) return self._neqv
def element_equivalence_table(self): """Element equivalence table The ANSYS program stores all element data in the numerical order that the SOLUTION processor solves the elements. This table equates the order number used to the actual element number. """ if self._element_equivalence_table is None: with open(self.filename, 'rb') as f: f.seek(self.header['ptrIDX'] * 4) self._element_matrices_index_table = read_table(f) return self._element_equivalence_table
def read_element_matrix_header(self, f_index): """Read element matrix header Parameters ---------- f_indes : int Fortran index to the start of the element matrix header. Notes ----- stkey - stiffness matrix key 0 - matrix not present 1 - matrix present mkey - mass matrix key 0 - matirx not present 1 - matrix present dkey - damping matrix key 0 - matrix not present 1 - matrix present sskey - stress stiffening matrix key 0 - matrix not present 1 - matrix present akey - applied load vector key 0 - vector not used 1 - vector used nrkey - newton-raphson(restoring) load 0 - vector not used 1 - vector used ikey - imaginary load vector key (for complex analyses) 0 - vector not used 1 - vector used nmrow - numbers/columns in matrices. If the number is negative, the matrices will be written in lower triangular form. """ with open(self.filename, 'rb') as f: f.seek(4 * f_index) return parse_header(read_table(f), ELEMENT_HEADER_KEYS)
def read_db_header(filename): with open(filename, 'rb') as f: f.seek(103 * 4) return parse_header(read_table(f), DB_HEADER_KEYS)
def read_element(self, index, stress=True, mass=True, damping=True, stress_stiff=True, applied_force=True, newton_raphson=True, imaginary_load=True): """Read element by index Parameters ---------- index : int Element index. This is not the element number. Reference the element equivalency table for the actual element number. stress : bool, optional Return the stress matrix entries if available. mass : bool, optional Return the mass matrix entries if available. damping : bool, optional Return the damping matrix entries if available. stress_stiff : bool, optional Return the stress stiffening entries if available. newton_raphson : bool, optional Return the newton raphson load entries if available. applied_force : bool, optional Return the applied load vector if available. imaginary_load : bool, optional Return the imaginary load vector if available. Returns ------- dof_idx : np.ndarray DOF index table. This record specifies the DOF locations of this element matrix in relation to the global matrix. The index is calculated as (N-1)*NUMDOF+DOF, where N is the position number of the node in the nodal equivalence table and DOF is the DOF reference number given above element_data : dict Dictionary containing the following entries for each of the corresponding inputs when the item is True. - 'stress' : stress matrix entries - 'mass' : mass matrix entries - 'damping' : damping matrix entries - 'stress_stiff' : stress stiffening matrix entries - 'newton_raphson' : newton rapson load vector - 'applied_force' : applied force vector - 'imaginary_load' : imaginary load vector Notes ----- If the matrix is diagonal, the length of the records will be nmrow. If the matrix is unsymmetric, the length of the records will be nmrow*nmrow. If the matrix is symmetric, only the lower triangular terms are written and the length of the records will be ``(nmrow)*(nmrow+1)/2`` Records are written relative to the dof_idx. The index is calculated as (N-1)*NUMDOF+DOF, where N is the position number of the node in the nodal equivalence table and DOF is the DOF reference number given by ``dof_idx``. """ element_data = {} with open(self.filename, 'rb') as fobj: # seek to the position of the element table in the file fobj.seek(self.element_matrices_index_table[index] * 4) # matrix header element_header = parse_header(read_table(fobj), ELEMENT_HEADER_KEYS) nmrow = abs(element_header['nmrow']) lower_tri = element_header['nmrow'] < 0 if lower_tri: nread = nmrow * (nmrow + 1) // 2 else: nread = nmrow * nmrow # Read DOF index table. This record specifies the DOF locations # of this element matrix in relation to the global # matrix. The index is calculated as (N-1)*NUMDOF+DOF, # where N is the position number of the node in the nodal # equivalence table and DOF is the DOF reference number # given above dof_idx = read_table(fobj) - 1 # adj one based indexing # read stress matrix if element_header['stkey']: # if entry even exists if stress: stress_entries = read_table(fobj, 'd', nread) element_data['stress'] = stress_entries else: # skip fobj.seek(nread * 8 + 12, 1) # read mass matrix if element_header['mkey']: # if entry even exists if mass: mass_entries = read_table(fobj, 'd', nread) element_data['mass'] = mass_entries else: # skip fobj.seek(nread * 8 + 12, 1) if element_header['dkey']: if damping: damping_entries = read_table(fobj, 'd', nread) element_data['damping'] = damping_entries else: # skip fobj.seek(nread * 8 + 12, 1) if element_header['sskey']: if stress_stiff: stress_stiff_entries = read_table(fobj, 'd', nread) element_data['stress_stiff'] = stress_stiff_entries else: # skip fobj.seek(nread * 8 + 12, 1) if element_header['akey']: if applied_force: applied_force_vector = read_table(fobj, 'd', nmrow) element_data['applied_force'] = applied_force_vector else: # skip fobj.seek(nmrow * 8 + 12, 1) if element_header['nrkey']: if newton_raphson: newton_raphson_vector = read_table(fobj, 'd', nmrow) element_data['newton_raphson'] = newton_raphson_vector else: # skip fobj.seek(nmrow * 8 + 12, 1) if element_header['ikey']: if imaginary_load: imaginary_load_vector = read_table(fobj, 'd', nmrow) element_data['imaginary_load'] = imaginary_load_vector else: # skip fobj.seek(nmrow * 8 + 12, 1) return dof_idx, element_data
def read_header(self): """Read standard emat file header""" with open(self.filename, 'rb') as f: f.seek(103 * 4) self.header = parse_header(read_table(f), EMAT_HEADER_KEYS)
def load_km(self, as_sparse=True, sort=False): """Load and construct mass and stiffness matrices from an ANSYS full file. Parameters ---------- as_sparse : bool, optional Outputs the mass and stiffness matrices as scipy csc sparse arrays when True by default. sort : bool, optional Rearranges the k and m matrices such that the rows correspond to to the sorted rows and columns in dor_ref. Also sorts dor_ref. Returns ------- dof_ref : (n x 2) np.int32 array This array contains the node and degree corresponding to each row and column in the mass and stiffness matrices. In a 3 DOF analysis the dof integers will correspond to: 0 - x 1 - y 2 - z Sort these values by node number and DOF by enabling the sort parameter. k : (n x n) np.float or scipy.csc array Stiffness array m : (n x n) np.float or scipy.csc array Mass array Examples -------- >>> import pyansys >>> full = pyansys.read_binary('file.rst') >>> dof_ref, k, m = full.load_km() >>> print(k) (0, 0) 163408119.6581276 (0, 1) 0.0423270 (1, 1) 163408119.6581276 : : (342, 344) 6590544.8717949 (343, 344) -6590544.8717950 (344, 344) 20426014.9572689 Notes ----- Constrained entries are removed from the mass and stiffness matrices. Constrained DOF can be accessed from ``const``, which returns the node number and DOF constrained in ANSYS. """ if not os.path.isfile(self.filename): raise Exception('%s not found' % self.filename) if as_sparse: try: from scipy.sparse import csc_matrix, coo_matrix except ImportError: raise ImportError('Unable to load scipy, use ``load_km`` with ' '``as_sparse=False``') # Get header details neqn = self._header['neqn'] # Number of equations # number of terms in stiffness matrix ntermK = two_ints_to_long(self._header['ntermKl'], self._header['ntermKh']) ptrSTF = self._header['ptrSTF'] # Location of stiffness matrix ptrMAS = self._header['ptrMAS'] # Location in file to mass matrix # number of terms in mass matrix ntermM = two_ints_to_long(self._header['ntermMl'], self._header['ntermMh']) ptrDOF = self._header['ptrDOF'] # pointer to DOF info # DOF information with open(self.filename, 'rb') as f: read_table(f, skip=True) # standard header read_table(f, skip=True) # full header read_table(f, skip=True) # number of degrees of freedom # Nodal equivalence table neqv = read_table(f, cython=True) # read number of degrees of freedom for each node and constant tables f.seek(ptrDOF * 4) ndof = read_table(f, cython=True) const = read_table(f, cython=True) # degree of freedom reference and number of degress of freedom per node dof_ref = [ndof, neqv] self.ndof = ndof # Read k and m blocks (see help(ReadArray) for block description) if ntermK: krow, kcol, kdata = _binary_reader.read_array( self.filename, ptrSTF, ntermK, neqn, const) else: warnings.warn('Missing stiffness matrix') kdata = None if ntermM: mrow, mcol, mdata = _binary_reader.read_array( self.filename, ptrMAS, ntermM, neqn, const) else: warnings.warn('Missing mass matrix') mdata = None # remove constrained entries if np.any(const < 0): if kdata is not None: remove = np.nonzero(const < 0)[0] mask = ~np.logical_or(np.in1d(krow, remove), np.in1d(kcol, remove)) krow = krow[mask] kcol = kcol[mask] kdata = kdata[mask] if mdata is not None: mask = ~np.logical_or(np.in1d(mrow, remove), np.in1d(mcol, remove)) mrow = mrow[mask] mcol = mcol[mask] mdata = mdata[mask] # sort nodal equivalence dof_ref, index, nref, dref = _binary_reader.sort_nodal_eqlv( neqn, neqv, ndof) # store constrained dof information unsort_dof_ref = np.vstack((nref, dref)).T self._const = unsort_dof_ref[const < 0] if sort: # make sorting the same as ANSYS rdfull would output # resort to make in upper triangle krow = index[krow] kcol = index[kcol] krow, kcol = np.sort(np.vstack((krow, kcol)), 0) if mdata is not None: mrow = index[mrow] mcol = index[mcol] mrow, mcol = np.sort(np.vstack((mrow, mcol)), 0) else: dof_ref = unsort_dof_ref # store data for later reference if kdata is not None: self._krow = krow self._kcol = kcol self._kdata = kdata if mdata is not None: self._mrow = mrow self._mcol = mcol self._mdata = mdata # output as a sparse matrix if as_sparse: if kdata is not None: k = coo_matrix((neqn, ) * 2) k.data = kdata # data has to be set first k.row = krow k.col = kcol # convert to csc matrix (generally faster for sparse solvers) k = csc_matrix(k) else: k = None if mdata is not None: m = coo_matrix((neqn, ) * 2) m.data = mdata m.row = mrow m.col = mcol # convert to csc matrix (generally faster for sparse solvers) m = csc_matrix(m) else: m = None else: if kdata is not None: k = np.zeros((neqn, ) * 2) k[krow, kcol] = kdata else: k = None if mdata is not None: m = np.zeros((neqn, ) * 2) m[mrow, mcol] = mdata else: m = None return dof_ref, k, m
def load_km(self, as_sparse=True, sort=False): """Load and construct mass and stiffness matrices from an ANSYS full file. Parameters ---------- as_sparse : bool, optional Outputs the mass and stiffness matrices as scipy csc sparse arrays when True by default. sort : bool, optional Rearranges the k and m matrices such that the rows correspond to to the sorted rows and columns in dor_ref. Also sorts dor_ref. Returns ------- dof_ref : (n x 2) np.int32 array This array contains the node and degree corresponding to each row and column in the mass and stiffness matrices. In a 3 DOF analysis the dof intergers will correspond to: 0 - x 1 - y 2 - z Sort these values by node number and DOF by enabling the sort parameter. k : (n x n) np.float or scipy.csc array Stiffness array m : (n x n) np.float or scipy.csc array Mass array Notes ----- Constrained entries are removed from the mass and stiffness matrices. Constrained DOF can be accessed with self.const, which returns the node number and DOF constrained in ANSYS. """ if not os.path.isfile(self.filename): raise Exception('%s not found' % self.filename) # see if if as_sparse: try: from scipy.sparse import csc_matrix, coo_matrix except BaseException: raise Exception('Unable to load scipy, matricies will be full') as_sparse = False # Get header details neqn = self.header[2] # Number of equations ntermK = self.header[9] # number of terms in stiffness matrix ptrSTF = self.header[19] # Location of stiffness matrix ptrMAS = self.header[27] # Location in file to mass matrix ntermM = self.header[34] # number of terms in mass matrix ptrDOF = self.header[36] # pointer to DOF info # DOF information ptrDOF = self.header[36] # pointer to DOF info with open(self.filename, 'rb') as f: read_table(f, skip=True) # standard header read_table(f, skip=True) # full header read_table(f, skip=True) # number of degrees of freedom neqv = read_table(f) # Nodal equivalence table f.seek(ptrDOF * 4) ndof = read_table(f) const = read_table(f) # degree of freedom reference and number of degress of freedom per node dof_ref = [ndof, neqv] self.ndof = ndof # Read k and m blocks (see help(ReadArray) for block description) if ntermK: krow, kcol, kdata = _binary_reader.ReadArray( self.filename, ptrSTF, ntermK, neqn, const) else: warnings.warn('Missing stiffness matrix') kdata = None if ntermM: mrow, mcol, mdata = _binary_reader.ReadArray( self.filename, ptrMAS, ntermM, neqn, const) else: warnings.warn('Missing mass matrix') mdata = None # remove constrained entries if np.any(const < 0): if kdata is not None: remove = np.nonzero(const < 0)[0] mask = ~np.logical_or(np.in1d(krow, remove), np.in1d(kcol, remove)) krow = krow[mask] kcol = kcol[mask] kdata = kdata[mask] if mdata is not None: mask = ~np.logical_or(np.in1d(mrow, remove), np.in1d(mcol, remove)) mrow = mrow[mask] mcol = mcol[mask] mdata = mdata[mask] # sort nodal equivalence dof_ref, index, nref, dref = _binary_reader.SortNodalEqlv( neqn, neqv, ndof) # store constrained dof information unsort_dof_ref = np.vstack((nref, dref)).T self.const = unsort_dof_ref[const < 0] if sort: # make sorting the same as ANSYS rdfull would output # resort to make in upper triangle krow = index[krow] kcol = index[kcol] krow, kcol = np.sort(np.vstack((krow, kcol)), 0) if mdata is not None: mrow = index[mrow] mcol = index[mcol] mrow, mcol = np.sort(np.vstack((mrow, mcol)), 0) else: dof_ref = unsort_dof_ref # store data for later reference if kdata is not None: self.krow = krow self.kcol = kcol self.kdata = kdata if mdata is not None: self.mrow = mrow self.mcol = mcol self.mdata = mdata # output as a sparse matrix if as_sparse: if kdata is not None: k = coo_matrix((neqn, ) * 2) k.data = kdata # data has to be set first k.row = krow k.col = kcol # convert to csc matrix (generally faster for sparse solvers) k = csc_matrix(k) else: k = None if mdata is not None: m = coo_matrix((neqn, ) * 2) m.data = mdata m.row = mrow m.col = mcol # convert to csc matrix (generally faster for sparse solvers) m = csc_matrix(m) else: m = None else: if kdata is not None: k = np.zeros((neqn, ) * 2) k[krow, kcol] = kdata else: k = None if mdata is not None: m = np.zeros((neqn, ) * 2) m[mrow, mcol] = mdata else: m = None return dof_ref, k, m