Source code for pulse.processing.assembly_structural

from time import time
import numpy as np
from scipy.sparse import csr_matrix

from pulse.utils import timer, error
from pulse.preprocessing.node import DOF_PER_NODE_STRUCTURAL
from pulse.preprocessing.structural_element import ENTRIES_PER_ELEMENT, DOF_PER_ELEMENT

[docs]class AssemblyStructural: """ This class creates a structural assembly object from input data. Parameters ---------- mesh : Mesh object Acoustic finite element mesh. frequencies : array Frequencies of analysis. acoustic_solution : array, optional Solution of the acoustic FETM model. This solution is need to solve the coupled problem. Default is None. """ def __init__(self, mesh, frequencies, **kwargs): self.mesh = mesh self.frequencies = frequencies self.acoustic_solution = kwargs.get('acoustic_solution', None) self.no_table = True
[docs] def get_prescribed_indexes(self): """ This method returns all the indexes of the structural degrees of freedom with prescribed structural displacement or rotation boundary conditions. Returns ---------- array Indexes of the structural degrees of freedom with prescribed displacement or rotation boundary conditions. See also -------- get_prescribed_values : Vaslues of the structural degrees of freedom with prescribed displacement or rotation boundary conditions. get_unprescribed_indexes : Indexes of the structural free degrees of freedom. """ global_prescribed = [] for node in self.mesh.nodes.values(): if node.there_are_prescribed_dofs: starting_position = node.global_index * DOF_PER_NODE_STRUCTURAL dofs = np.array(node.get_prescribed_dofs_bc_indexes()) + starting_position global_prescribed.extend(dofs) return global_prescribed
[docs] def get_prescribed_values(self): """ This method returns all the values of the structural degrees of freedom with prescribed structural displacement or rotation boundary conditions. Returns ---------- array Values of the structural degrees of freedom with prescribed displacement or rotation boundary conditions. See also -------- get_prescribed_indexes : Indexes of the structural degrees of freedom with prescribed displacement or rotation boundary conditions. get_unprescribed_indexes : Indexes of the structural free degrees of freedom. """ global_prescribed = [] list_prescribed_dofs = [] if self.frequencies is None: cols = 1 else: cols = len(self.frequencies) for node in self.mesh.nodes.values(): if node.there_are_prescribed_dofs: global_prescribed.extend(node.get_prescribed_dofs_bc_values()) try: aux_ones = np.ones(cols, dtype=complex) for value in global_prescribed: if isinstance(value, complex): list_prescribed_dofs.append(aux_ones*value) elif isinstance(value, np.ndarray): list_prescribed_dofs.append(value) array_prescribed_values = np.array(list_prescribed_dofs) except Exception as e: error(str(e)) return global_prescribed, array_prescribed_values
[docs] def get_unprescribed_indexes(self): """ This method returns all the indexes of the structural free degrees of freedom. Returns ---------- array Indexes of the structural free degrees of freedom. See also -------- get_prescribed_indexes : Indexes of the structural degrees of freedom with prescribed displacement or rotation boundary conditions. get_prescribed_values : Values of the structural degrees of freedom with prescribed displacement or rotation boundary conditions. """ total_dof = DOF_PER_NODE_STRUCTURAL * len(self.mesh.nodes) all_indexes = np.arange(total_dof) prescribed_indexes = self.get_prescribed_indexes() unprescribed_indexes = np.delete(all_indexes, prescribed_indexes) return unprescribed_indexes
[docs] def get_global_matrices(self): """ This method perform the assembly process of the structural FEM matrices. Returns ---------- K : list List of stiffness matrices of the free degree of freedom. Each item of the list is a sparse csr_matrix. M : list List of mass matrices of the free degree of freedom. Each item of the list is a sparse csr_matrix. Kr : list List of stiffness matrices of the prescribed degree of freedom. Each item of the list is a sparse csr_matrix. Mr : list List of mass matrices of the prescribed degree of freedom. Each item of the list is a sparse csr_matrix. """ total_dof = DOF_PER_NODE_STRUCTURAL * len(self.mesh.nodes) number_elements = len(self.mesh.structural_elements) rows, cols = self.mesh.get_global_structural_indexes() mat_Ke = np.zeros((number_elements, DOF_PER_ELEMENT, DOF_PER_ELEMENT), dtype=float) mat_Me = np.zeros((number_elements, DOF_PER_ELEMENT, DOF_PER_ELEMENT), dtype=float) for index, element in enumerate(self.mesh.structural_elements.values()): mat_Ke[index,:,:], mat_Me[index,:,:] = element.matrices_gcs() full_K = csr_matrix((mat_Ke.flatten(), (rows, cols)), shape=[total_dof, total_dof]) full_M = csr_matrix((mat_Me.flatten(), (rows, cols)), shape=[total_dof, total_dof]) prescribed_indexes = self.get_prescribed_indexes() unprescribed_indexes = self.get_unprescribed_indexes() K = full_K[unprescribed_indexes, :][:, unprescribed_indexes] M = full_M[unprescribed_indexes, :][:, unprescribed_indexes] Kr = full_K[:, prescribed_indexes] Mr = full_M[:, prescribed_indexes] return K, M, Kr, Mr
[docs] def get_lumped_matrices(self): """ This method perform the assembly process of the structural FEM lumped matrices. Returns ---------- K_lump : list List of lumped stiffness matrices of the free degree of freedom. Each item of the list is a sparse csr_matrix that corresponds to one frequency of analysis. M_lump : list List of mass matrices of the free degree of freedom. Each item of the list is a sparse csr_matrix that corresponds to one frequency of analysis. C_lump : list List of lumped damping matrices of the free degree of freedom. Each item of the list is a sparse csr_matrix that corresponds to one frequency of analysis. Kr_lump : list List of lumped stiffness matrices of the prescribed degree of freedom. Each item of the list is a sparse csr_matrix that corresponds to one frequency of analysis. Mr_lump : list List of lumped mass matrices of the prescribed degree of freedom. Each item of the list is a sparse csr_matrix that corresponds to one frequency of analysis. Cr_lump : list List of lumped damping matrices of the prescribed degree of freedom. Each item of the list is a sparse csr_matrix that corresponds to one frequency of analysis. flag_Clump : boll This flag returns True if the damping matrices are non zero, and False otherwise. """ total_dof = DOF_PER_NODE_STRUCTURAL * len(self.mesh.nodes) if self.frequencies is None: cols = 1 else: cols = len(self.frequencies) list_Kdata = [] list_Mdata = [] list_Cdata = [] i_indexes_M, j_indexes_M = [], [] i_indexes_K, j_indexes_K = [], [] i_indexes_C, j_indexes_C = [], [] self.nodes_with_lumped_masses = [] self.nodes_connected_to_springs = [] self.nodes_connected_to_dampers = [] self.nodes_with_nodal_elastic_links = [] flag_Clump = False # processing external elements by node for node in self.mesh.nodes.values(): # processing mass added if node.there_are_lumped_stiffness: position = node.global_dof self.nodes_connected_to_springs.append(node) list_Kdata.append(self.get_bc_array_for_all_frequencies(node.loaded_table_for_lumped_stiffness, node.lumped_stiffness)) i_indexes_K.append(position) j_indexes_K.append(position) # processing mass added if node.there_are_lumped_masses: position = node.global_dof self.nodes_with_lumped_masses.append(node) list_Mdata.append(self.get_bc_array_for_all_frequencies(node.loaded_table_for_lumped_masses, node.lumped_masses)) i_indexes_M.append(position) j_indexes_M.append(position) # processing damper added if node.there_are_lumped_dampings: position = node.global_dof self.nodes_connected_to_dampers.append(node) list_Cdata.append(self.get_bc_array_for_all_frequencies(node.loaded_table_for_lumped_dampings, node.lumped_dampings)) i_indexes_C.append(position) j_indexes_C.append(position) flag_Clump = True for cluster_data in self.mesh.dict_nodes_with_elastic_link_stiffness.values(): for indexes_i, indexes_j, data, in cluster_data: for i in range(2): i_indexes_K.append(indexes_i[i]) j_indexes_K.append(indexes_j[i]) list_Kdata.append(self.get_bc_array_for_all_frequencies(node.loaded_table_for_elastic_link_stiffness, data[i])) for cluster_data in self.mesh.dict_nodes_with_elastic_link_damping.values(): for indexes_i, indexes_j, data, in cluster_data: for i in range(2): i_indexes_C.append(indexes_i[i]) j_indexes_C.append(indexes_j[i]) list_Cdata.append(self.get_bc_array_for_all_frequencies(node.loaded_table_for_elastic_link_damping, data[i])) data_Klump = np.array(list_Kdata).reshape(-1, cols) data_Mlump = np.array(list_Mdata).reshape(-1, cols) data_Clump = np.array(list_Cdata).reshape(-1, cols) i_indexes_K = np.array(i_indexes_K).flatten() i_indexes_M = np.array(i_indexes_M).flatten() i_indexes_C = np.array(i_indexes_C).flatten() j_indexes_K = np.array(j_indexes_K).flatten() j_indexes_M = np.array(j_indexes_M).flatten() j_indexes_C = np.array(j_indexes_C).flatten() full_K = [csr_matrix((data_Klump[:,j], (i_indexes_K, j_indexes_K)), shape=[total_dof, total_dof]) for j in range(cols)] full_M = [csr_matrix((data_Mlump[:,j], (i_indexes_M, j_indexes_M)), shape=[total_dof, total_dof]) for j in range(cols)] full_C = [csr_matrix((data_Clump[:,j], (i_indexes_C, j_indexes_C)), shape=[total_dof, total_dof]) for j in range(cols)] prescribed_indexes = self.get_prescribed_indexes() unprescribed_indexes = self.get_unprescribed_indexes() K_lump = [sparse_matrix[unprescribed_indexes, :][:, unprescribed_indexes] for sparse_matrix in full_K] M_lump = [sparse_matrix[unprescribed_indexes, :][:, unprescribed_indexes] for sparse_matrix in full_M] C_lump = [sparse_matrix[unprescribed_indexes, :][:, unprescribed_indexes] for sparse_matrix in full_C] Kr_lump = [sparse_matrix[:, prescribed_indexes] for sparse_matrix in full_K] Mr_lump = [sparse_matrix[:, prescribed_indexes] for sparse_matrix in full_M] Cr_lump = [sparse_matrix[:, prescribed_indexes] for sparse_matrix in full_C] return K_lump, M_lump, C_lump, Kr_lump, Mr_lump, Cr_lump, flag_Clump
[docs] def get_global_loads(self, pressure_external = 0, loads_matrix3D=False): """ This method perform the assembly process of the structural FEM force and moment loads. Parameters ---------- pressure_external : float, optional Static pressure difference between atmosphere and the fluid in the pipeline. Default is 0. loads_matrix3D : boll, optional Default is False. Returns ---------- array Loads vectors. Each column corresponds to a frequency of analysis. """ total_dof = DOF_PER_NODE_STRUCTURAL * len(self.mesh.nodes) cols = len(self.frequencies) loads = np.zeros((total_dof, cols), dtype=complex) pressure_loads = np.zeros((total_dof, cols), dtype=complex) # distributed loads for element in self.mesh.structural_elements.values(): if np.sum(element.loaded_forces) != 0: position = element.global_dof loads[position] += element.force_vector_gcs() # nodal loads for node in self.mesh.nodes.values(): if node.there_are_nodal_loads: position = node.global_dof if node.loaded_table_for_nodal_loads: temp_loads = [np.zeros_like(self.frequencies) if bc is None else bc for bc in node.nodal_loads] else: temp_loads = [np.zeros_like(self.frequencies) if bc is None else np.ones_like(self.frequencies)*bc for bc in node.nodal_loads] loads[position, :] += temp_loads unprescribed_indexes = self.get_unprescribed_indexes() loads = loads[unprescribed_indexes,:] if self.acoustic_solution is not None: for element in self.mesh.structural_elements.values(): pressure_first = self.acoustic_solution[element.first_node.global_index, :] pressure_last = self.acoustic_solution[element.last_node.global_index, :] pressure_avg = (pressure_first + pressure_last) / 2 position = element.global_dof pressure_loads[position, :] += element.force_vector_acoustic_gcs(self.frequencies, pressure_avg, pressure_external) pressure_loads = pressure_loads[unprescribed_indexes,:] if loads_matrix3D: loads = loads.T.reshape((len(self.frequencies), loads.shape[0], 1)) loads += pressure_loads.T.reshape((len(self.frequencies), pressure_loads.shape[0],1)) else: loads += pressure_loads return loads
[docs] def get_bc_array_for_all_frequencies(self, there_are_table, boundary_condition): """ This method perform the assembly process of the structural FEM force and moment loads. Parameters ---------- pressure_external : float, optional Static pressure difference between atmosphere and the fluid in the pipeline. Default is 0. loads_matrix3D : boll, optional Default is False. Returns ---------- array Loads vectors. Each column corresponds to a frequency of analysis. """ if there_are_table: list_arrays = [np.zeros_like(self.frequencies) if bc is None else bc for bc in boundary_condition] self.no_table = False else: if self.frequencies is None: frequencies = 1 else: frequencies = self.frequencies list_arrays = [np.zeros_like(frequencies) if bc is None else np.ones_like(frequencies)*bc for bc in boundary_condition] return list_arrays