339 the shape of this should be:
340 unknowns_per_cell_node * ( self.poly_degree + 1 ) ** self.dimensions rows (= 4 for 2D and 1st order)
341 solutions_per_face_node * ( self.poly_degree + 1 ) ** (self.dimensions-1) * 2 *dimensions cols (= 16 for 2D and 1st order)
343 solutions_per_face_node counts u and q
344 multiply further by number of nodes per face
345 multiply further by number of faces
354 A_CF_q_left = self.
block_matrix._A_CF[(0,2)][
"left"][(0,1)]
355 A_CF_q_bottom = self.
block_matrix._A_CF[(0,2)][
"bottom"][(1,0)]
356 A_CF_q_right = self.
block_matrix._A_CF[(0,2)][
"right"][(0,1)]
357 A_CF_q_top = self.
block_matrix._A_CF[(0,2)][
"top"][(1,0)]
363 A_CF_u_left = self.
block_matrix._A_CF[(0,5)][
"left"][(0,0)]
364 A_CF_u_bottom = self.
block_matrix._A_CF[(0,5)][
"bottom"][(0,0)]
365 A_CF_u_right = self.
block_matrix._A_CF[(0,5)][
"right"][(0,0)]
366 A_CF_u_top = self.
block_matrix._A_CF[(0,5)][
"top"][(0,0)]
373 u_indices = [ index
for index
in range(f_unknowns)
if index
not in q_indices]
376 matrix_0 = np.zeros((cell_unknowns, f_unknowns))
377 matrix_1 = np.zeros((cell_unknowns, f_unknowns))
378 matrix_0[ :, u_indices ] = np.concatenate((A_CF_u_left, A_CF_u_bottom, A_CF_u_right, A_CF_u_top), axis=1)
379 matrix_1[ :, q_indices ] = np.concatenate((A_CF_q_left, A_CF_q_bottom, A_CF_q_right, A_CF_q_top), axis=1)
381 output_matrices = [ matrix_0, matrix_1 ]
382 output_scalings = [ 0, 1 ]
384 assert( len(output_matrices) == len(output_scalings) )
386 for matrix
in output_matrices:
387 assert matrix.shape == output_matrices[0].shape
389 return output_matrices, output_scalings
401 Compute projection matrix from the FC and FF matrices provided in BlockMatrix interface
403 Return face-from-cell projection matrices with and without zero rows corresponding to the redundant projections.
404 Matrix with redundant projections is needed for get_face_from_cell_matrix(self).
405 Matrix without redundant projections is convenient for computing the Schur-complement matrix.
407 The output matrix with redundant projections should have: extra factor to account for all the faces
408 2 * self._projections_per_face_node * ( self.poly_degree + 1 ) ** (self.dimensions-1) * 2 *self.dimensions rows (= 32 in 2D 1st order)
409 self.unknowns_per_cell_node * ( self.poly_degree + 1 ) ** self.dimensions cols (= 4 in 2D 1st order)
411 The output matrix without redundant projections should have half the number of row as the previous one:
412 rows (= 16 in 2D 1st order)
413 cols (= 4 in 2D 1st order)
426 A_qp_u_left = self.
block_matrix._A_FC[(0,0)][
"left"][(0,0)]
427 A_qp_u_bottom = self.
block_matrix._A_FC[(0,0)][
"bottom"][(0,0)]
428 A_qm_u_right = self.
block_matrix._A_FC[(1,0)][
"right"][(0,0)]
429 A_qm_u_top = self.
block_matrix._A_FC[(1,0)][
"top"][(0,0)]
431 M_qp_qp_left = self.
block_matrix._A_FF[(0,0)][
"left"][(0,1)]
432 M_qp_qp_bottom = self.
block_matrix._A_FF[(0,0)][
"bottom"][(1,0)]
433 M_qm_qm_right = self.
block_matrix._A_FF[(1,1)][
"right"][(0,1)]
434 M_qm_qm_top = self.
block_matrix._A_FF[(1,1)][
"top"][(1,0)]
437 P_qp_u_left = -np.dot(np.linalg.inv(M_qp_qp_left), A_qp_u_left)
438 P_qp_u_bottom = -np.dot(np.linalg.inv(M_qp_qp_bottom), A_qp_u_bottom)
439 P_qm_u_right = -np.dot(np.linalg.inv(M_qm_qm_right), A_qm_u_right)
440 P_qm_u_top = -np.dot(np.linalg.inv(M_qm_qm_top), A_qm_u_top)
443 A_up_u_left = self.
block_matrix._A_FC[(3,0)][
"left"][(0,1)]
444 A_up_u_bottom = self.
block_matrix._A_FC[(3,0)][
"bottom"][(1,0)]
445 A_um_u_right = self.
block_matrix._A_FC[(4,0)][
"right"][(0,1)]
446 A_um_u_top = self.
block_matrix._A_FC[(4,0)][
"top"][(1,0)]
448 M_up_up_left = self.
block_matrix._A_FF[(3,3)][
"left"][(0,1)]
449 M_up_up_bottom = self.
block_matrix._A_FF[(3,3)][
"bottom"][(1,0)]
450 M_um_um_right = self.
block_matrix._A_FF[(4,4)][
"right"][(0,1)]
451 M_um_um_top = self.
block_matrix._A_FF[(4,4)][
"top"][(1,0)]
454 P_up_u_left = -np.dot(np.linalg.inv(M_up_up_left), A_up_u_left)
455 P_up_u_bottom = -np.dot(np.linalg.inv(M_up_up_bottom), A_up_u_bottom)
456 P_um_u_right = -np.dot(np.linalg.inv(M_um_um_right), A_um_u_right)
457 P_um_u_top = -np.dot(np.linalg.inv(M_um_um_top), A_um_u_top)
462 matrix_0_redundant = np.zeros((proj_unknowns, cell_unknowns))
463 matrix_0 = np.zeros((proj_unknowns // 2, cell_unknowns))
465 matrix_1_redundant = np.zeros((proj_unknowns, cell_unknowns))
466 matrix_1 = np.zeros((proj_unknowns // 2, cell_unknowns))
475 u_indices = [ index
for index
in range(f_unknowns)
if index
not in q_indices]
478 matrix_0[ q_indices, : ] = np.concatenate((P_qp_u_left,
483 matrix_1[ u_indices, : ] = np.concatenate((P_up_u_left,
493 q_m_indices = [ index
for index
in range(proj_unknowns)
if (index % proj_unknowns_per_face) < nodes_per_face ]
494 u_m_indices = [ index
for index
in range(proj_unknowns)
if nodes_per_face <= (index % proj_unknowns_per_face) < 2 * nodes_per_face ]
495 q_p_indices = [ index
for index
in range(proj_unknowns)
if 2 * nodes_per_face <= (index % proj_unknowns_per_face) < 3 * nodes_per_face ]
496 u_p_indices = [ index
for index
in range(proj_unknowns)
if 3 * nodes_per_face <= (index % proj_unknowns_per_face) < 4 * nodes_per_face ]
499 Zeros = np.zeros_like(P_qp_u_left)
502 matrix_0_redundant[ q_p_indices, : ] = np.concatenate((P_qp_u_left,
506 matrix_0_redundant[ q_m_indices, : ] = np.concatenate((Zeros,
511 matrix_1_redundant[ u_p_indices, : ] = np.concatenate((P_up_u_left,
515 matrix_1_redundant[ u_m_indices, : ] = np.concatenate((Zeros,
522 output_matrices = [ matrix_0, matrix_1 ]
523 output_matrices_redundant = [ matrix_0_redundant, matrix_1_redundant ]
524 output_scalings = [ -1, 0 ]
526 assert( len(output_matrices_redundant) == len(output_scalings) )
528 for matrix
in output_matrices_redundant:
529 assert matrix.shape == output_matrices_redundant[0].shape
531 return output_matrices, output_matrices_redundant, output_scalings