def generate_finite_diff_mesh(mesh_size, num_free_nodes): """ Generates a finite-difference mesh with the given size and number of free nodes. :param mesh_size: the mesh size :param num_free_nodes: the number of free nodes :return: the A and b matrices defining the mesh equation (Ax = b) """ A = Matrix.empty(num_free_nodes, num_free_nodes) b = Matrix.empty(num_free_nodes, 1) for row in range(mesh_size - 3): for col in range(mesh_size - 1): node = row * (mesh_size - 1) + col A[node][node] = -4 if row != 0: A[node][node - mesh_size + 1] = 1 if 12 <= node <= 14: b[node][0] = -15 else: A[node][node + mesh_size - 1] = 1 # Right Neumann boundary if col == mesh_size - 2: A[node][node - 1] = 2 else: if col != 0: A[node][node - 1] = 1 A[node][node + 1] = 1 # Special nodes A[15][10] = 1 A[15][15] = -4 A[15][16] = 1 A[15][17] = 1 A[16][11] = 1 A[16][15] = 1 A[16][16] = -4 A[16][18] = 1 b[16][0] = -15 A[17][15] = 2 A[17][17] = -4 A[17][18] = 1 A[18][16] = 2 A[18][17] = 1 A[18][18] = -4 b[18][0] = -15 return A, b
def conjugate_gradient_solve(A, b, residual_vectors=None): """ Solves the Ax = b matrix equation given by the given A and b matrices :param A: the A matrix :param b: the b matrix :param residual_vectors: the list to store the residual vectors in :return: the solved x vector """ n = len(A) x = Matrix.empty(n, 1) r = b - A * x p = deepcopy(r) if residual_vectors is not None: residual_vectors.append(r) for _ in range(n): denom = p.transpose() * A * p alpha = (p.transpose() * r) / denom x = x + p * alpha.item() r = b - A * x beta = -(p.transpose() * A * r) / denom p = r + p * beta.item() if residual_vectors is not None: residual_vectors.append(r) return x
def construct_phi(self): phi = Matrix.empty(self.mesh.num_rows, self.mesh.num_cols) for i in range(self.mesh.num_rows): y = self.mesh.get_y(i) for j in range(self.mesh.num_cols): x = self.mesh.get_x(j) boundary_pt = False for boundary in self.boundaries: if boundary.contains_point(x, y): boundary_pt = True phi[i][j] = boundary.potential() if not boundary_pt: phi[i][j] = self.guesser.guess(x, y) return phi
def find_disjoint_s_matrix(S1, S2): """ Finds the disjoint S matrix given by the two provided local S matrices. :param S1: the first local S matrix :param S2: the second local S matrix :return: the disjoint S matrix """ n = len(S1) S_dis = Matrix.empty(2 * n, 2 * n) for row in range(n): for col in range(n): S_dis[row][col] = S1[row][col] S_dis[row + n][col + n] = S2[row][col] return S_dis
def choleski_solve(A, b, half_bandwidth=None): """ Solves an Ax = b matrix equation by Choleski decomposition. :param A: the A matrix :param b: the b matrix :param half_bandwidth: the half-bandwidth of the A matrix :return: the solved x vector """ n = len(A[0]) if half_bandwidth is None: elimination(A, b) else: elimination_banded(A, b, half_bandwidth) x = Matrix.empty(n, 1) back_substitution(A, x, b) return x
def find_local_s_matrix(triangle): """ Finds the local S matrix for a finite-difference triangle. :param triangle: the finite-difference triangle :return: the local S matrix """ x = triangle.x y = triangle.y S = Matrix.empty(3, 3) for i in range(3): for j in range(3): S[i][j] = ((y[(i + 1) % 3] - y[(i + 2) % 3]) * (y[(j + 1) % 3] - y[(j + 2) % 3]) + (x[(i + 1) % 3] - x[(i + 2) % 3]) * (x[(j + 1) % 3] - x[(j + 2) % 3])) / (4 * triangle.area) return S
def compute_half_energy(S, mesh, mesh_size): """ Computes the half-energy needed to compute the capacitance of the mesh. :param S: the S matrix :param mesh: the mesh :param mesh_size: the mesh size :return: the half-energy """ U_con = Matrix.empty(4, 1) half_energy = 0 for row in range(mesh_size - 1): for col in range(mesh_size - 1): node = row * mesh_size + (col + 1) # 1-based if node < 28: U_con[0][0] = mesh[node + mesh_size] U_con[1][0] = mesh[node] U_con[2][0] = mesh[node + 1] U_con[3][0] = mesh[node + mesh_size + 1] half_energy_contribution = U_con.transpose() * S * U_con half_energy += half_energy_contribution[0][0] return half_energy
def create_incidence_matrix_mesh(cols, num_branches, num_horizontal_branches, num_nodes, num_vertical_branches): """ Create the incidence matrix given by the resistive mesh with the given number of columns, number of branches, number of horizontal branches, number of nodes, and number of vertical branches. :param cols: the number of columns in the mesh :param num_branches: the number of branches in the mesh :param num_horizontal_branches: the number of horizontal branches in the mesh :param num_nodes: the number of nodes in the mesh :param num_vertical_branches: the number of vertical branches in the mesh :return: the incidence matrix (A) """ A = Matrix.empty(num_nodes, num_branches) node_offset = -1 for branch in range(num_horizontal_branches): if branch == num_horizontal_branches - cols + 1: A[branch + node_offset + 1][branch] = 1 else: if branch % (cols - 1) == 0: node_offset += 1 node_number = branch + node_offset A[node_number][branch] = -1 A[node_number + 1][branch] = 1 branch_offset = num_horizontal_branches node_offset = cols for branch in range(num_vertical_branches): if branch == num_vertical_branches - cols: node_offset -= 1 A[branch][branch + branch_offset] = 1 else: A[branch][branch + branch_offset] = 1 A[branch + node_offset][branch + branch_offset] = -1 if num_branches == 2: A[0][1] = -1 else: A[cols - 1][num_branches - 1] = -1 return A