def create_boundary_problem_matrix(self, omega): # full dof number self.map_dofs() num_dof = len(self.dof_mapping) # number of nodes node_no = len(self.nodes) # number of internal dofs internal_dof_no = np.sum(e.num_degrees_of_freedom() for e in self.elements) # number of terminals terminal_no = np.sum(e.num_terminals() for e in self.elements) # dynamic equations reflect the element's IV characteristic dynamic_equation_no = terminal_no + internal_dof_no # kinetic equations are Kirchhof's law that the sum of nodal currents is zero kinetic_equation_no = node_no num_equations = dynamic_equation_no + kinetic_equation_no boundary_condition_matrix = np.zeros((num_equations, num_dof), dtype=complex) # filling dynamic equations equation_id = 0 current_offset = 0 internal_dof_offset = 0 for e_id, e in enumerate(self.elements): equations = e.boundary_condition(omega) for element_equation_id in range(equations.shape[0]): equation = equations[element_equation_id, :] for terminal_id, terminal_node in enumerate( self.terminal_node_mapping[e_id]): node_id = self.nodes.index(terminal_node) boundary_condition_matrix[equation_id, node_id] = equation[ terminal_id] # nodal voltages boundary_condition_matrix[ equation_id, node_no + current_offset + terminal_id] = equation[ terminal_id + e.num_terminals()] # nodal current for internal_dof_id in range(e.num_degrees_of_freedom()): boundary_condition_matrix[ equation_id, node_no + terminal_no + internal_dof_offset + internal_dof_id] = equation[2 * e.num_terminals() + internal_dof_id] equation_id += 1 internal_dof_offset += e.num_degrees_of_freedom() current_offset += e.num_terminals() full_terminal_id = 0 # filling kinetic equations for e_id, e in enumerate(self.elements): for terminal_id, node in enumerate( self.terminal_node_mapping[e_id]): boundary_condition_matrix[dynamic_equation_no + self.nodes.index(node), node_no + full_terminal_id] = 1 full_terminal_id += 1 return boundary_condition_matrix
def get_element_dynamic_equations(self, element: TLSystemElement): e_id = self.elements.index(element) offset = np.sum(e.num_terminals() + e.num_degrees_of_freedom() for e in self.elements[:e_id]) return np.arange( offset, offset + element.num_terminals() + element.num_degrees_of_freedom())
def get_element_energies_from_dynamic(self, state): # number of nodes node_no = len(self.nodes) # number of internal dofs internal_dof_no = np.sum(e.num_degrees_of_freedom_dynamic() for e in self.elements) # number of terminals terminal_no = np.sum(e.num_terminals() for e in self.elements) dynamic_equation_no = terminal_no + internal_dof_no # kinetic equations are Kirchhof's law that the sum of nodal currents is zero kinetic_equation_no = node_no num_equations = dynamic_equation_no + kinetic_equation_no # todo: build energy and dissipation rate matrices e = np.zeros((num_equations, num_equations)) p = np.zeros((num_equations, num_equations)) current_offset = 0 internal_dof_offset = 0 energies = [] for e_id, e in enumerate(self.elements): element_state_size = 2 * e.num_terminals( ) + e.num_degrees_of_freedom_dynamic() element_state = np.zeros((element_state_size, ), dtype=complex) for terminal_id, terminal_node in enumerate( self.terminal_node_mapping[e_id]): node_id = self.nodes.index(terminal_node) element_state[terminal_id] = state[node_id] element_state[terminal_id + e.num_terminals()] = state[node_no + current_offset + terminal_id] for internal_dof_id in range(e.num_degrees_of_freedom_dynamic()): element_state[2 * e.num_terminals() + internal_dof_id] = state[node_no + terminal_no + internal_dof_offset + internal_dof_id] internal_dof_offset += e.num_degrees_of_freedom_dynamic() current_offset += e.num_terminals() energies.append(e.energy(element_state)) return energies
def map_dofs(self): # count nodes self.dof_mapping = [n for n in self.nodes] # nodal voltages self.dof_mapping.extend([(e_id, p_id) for e_id, e in enumerate(self.elements) for p_id in range(e.num_terminals())]) # currents incident into each terminal self.dof_mapping.extend([ (e_id, int_dof_id) for e_id, e in enumerate(self.elements) for int_dof_id in range(e.num_degrees_of_freedom()) ])
def get_element_dofs(self, element: TLSystemElement): self.map_dofs() e_id = self.elements.index(element) voltages = [ self.dof_mapping[:len(self.nodes)].index(t) for t in self.terminal_node_mapping[e_id] ] terminal_no = np.sum(e.num_terminals() for e in self.elements) current_variables = self.dof_mapping[len(self.nodes):len(self.nodes) + terminal_no] internal_dof_variables = self.dof_mapping[len(self.nodes) + terminal_no:] currents = [ current_variables.index((e_id, p_id)) + len(self.nodes) for p_id in range(element.num_terminals()) ] degrees_of_freedom = [internal_dof_variables.index((e_id, dof_id)) + len(self.nodes) + terminal_no \ for dof_id in range(element.num_degrees_of_freedom())] return voltages, currents, degrees_of_freedom
def create_dynamic_equation_matrices(self): # number of nodes node_no = len(self.nodes) # number of internal dofs internal_dof_no = np.sum(e.num_degrees_of_freedom_dynamic() for e in self.elements) # number of terminals terminal_no = np.sum(e.num_terminals() for e in self.elements) # dynamic equations reflect the element's IV characteristic dynamic_equation_no = terminal_no + internal_dof_no # kinetic equations are Kirchhof's law that the sum of nodal currents is zero kinetic_equation_no = node_no num_equations = dynamic_equation_no + kinetic_equation_no dynamic_equation_matrix_a = np.zeros((num_equations, num_equations), dtype=float) dynamic_equation_matrix_b = np.zeros((num_equations, num_equations), dtype=float) # filling dynamic equations equation_id = 0 current_offset = 0 internal_dof_offset = 0 for e_id, e in enumerate(self.elements): equations_a, equations_b = e.dynamic_equations() for element_equation_id in range(equations_b.shape[0]): equation_b = equations_b[element_equation_id, :] equation_a = equations_a[element_equation_id, :] for terminal_id, terminal_node in enumerate( self.terminal_node_mapping[e_id]): node_id = self.nodes.index(terminal_node) dynamic_equation_matrix_a[ equation_id, node_id] = equation_a[terminal_id] # nodal voltages dynamic_equation_matrix_a[ equation_id, node_no + current_offset + terminal_id] = equation_a[ terminal_id + e.num_terminals()] # nodal current dynamic_equation_matrix_b[ equation_id, node_id] = equation_b[terminal_id] # nodal voltages dynamic_equation_matrix_b[ equation_id, node_no + current_offset + terminal_id] = equation_b[ terminal_id + e.num_terminals()] # nodal current for internal_dof_id in range( e.num_degrees_of_freedom_dynamic()): dynamic_equation_matrix_a[ equation_id, node_no + terminal_no + internal_dof_offset + internal_dof_id] = equation_a[2 * e.num_terminals() + internal_dof_id] dynamic_equation_matrix_b[ equation_id, node_no + terminal_no + internal_dof_offset + internal_dof_id] = equation_b[2 * e.num_terminals() + internal_dof_id] equation_id += 1 internal_dof_offset += e.num_degrees_of_freedom_dynamic() current_offset += e.num_terminals() full_terminal_id = 0 # filling kinetic equations for e_id, e in enumerate(self.elements): for terminal_id, node in enumerate( self.terminal_node_mapping[e_id]): dynamic_equation_matrix_a[dynamic_equation_no + self.nodes.index(node), node_no + full_terminal_id] = 1 full_terminal_id += 1 return dynamic_equation_matrix_a, dynamic_equation_matrix_b