def jump_constraints(jump, negative): n_dofs_per_side = jump.shape[0] cs = [] coeff_2 = 1.0 if negative else -1.0 for i in range(n_dofs_per_side): dof_1 = i dof_2 = i + n_dofs_per_side ts = [] ts.append(Term(1.0, dof_1)) ts.append(Term(coeff_2, dof_2)) cs.append(ConstraintEQ(ts, jump[i])) return cs
def fault_surf_intersection_traction_constraints(m): surf_tris = m.get_tris('surf') unscaled_ns = tct.util.geometry.unscaled_normals(m.pts[surf_tris]) ns = tct.util.geometry.normalize(unscaled_ns) pt_ns = np.zeros((m.pts.shape[0], 3)) for i in range(m.n_tris('surf')): for d in range(3): pt_ns[m.tris[i,d]] += ns[i] n_ns = np.ones((m.pts.shape[0])) unique, counts = np.unique(surf_tris, return_counts=True) n_ns[unique] = counts pt_ns /= n_ns[:,np.newaxis] cs = [] for i in m.get_tri_idxs('fault'): for d in range(3): n = pt_ns[m.tris[i,d]] if np.all(n == 0): continue assert(np.where(surf_tris == m.tris[i,d])[0].shape[0] > 0) ts = [] for d2 in range(3): if n[d2] == 0.0: continue fault_dof = i * 9 + d * 3 + d2 ts.append(Term(n[d2], fault_dof)) cs.append(ConstraintEQ(ts, 0)) return cs
def constant_stress_constraint(tri_data1, tri_data2): tri1, tri_idx1, corner_idx1 = tri_data1 dof_start1 = tri_idx1 * 9 + corner_idx1 * 3 n1 = np.cross(tri1[1] - tri1[0], tri1[2] - tri1[0]) n1 /= np.linalg.norm(n1) tri2, tri_idx2, corner_idx2 = tri_data2 dof_start2 = tri_idx2 * 9 + corner_idx2 * 3 n2 = np.cross(tri2[1] - tri2[0], tri2[2] - tri2[0]) n2 /= np.linalg.norm(n2) terms = [] for d in range(3): terms.append(Term(n1[d], dof_start2 + d)) terms.append(Term(-n2[d], dof_start1 + d)) return ConstraintEQ(terms, 0.0)
def equilibrium_constraint(tri_data): tri, tri_idx, corner_idx = tri_data x_to_xp = rot_mat(tri) # map from x to rotated frame x_to_xhat = inv_jacobian(tri) # map from triangle reference coords to x xp_to_xhat = x_to_xhat.dot(x_to_xp.T) vals = np.zeros((3, 3)) for b in range(3): for i in range(3): for Ik in range(2): for Ip in range(3): vals[b, i] += (basis_gradient[Ik, b] * xp_to_xhat[Ik, Ip] * x_to_xp[Ip, i]) terms = [] for b in range(3): for d in range(3): terms.append(Term(vals[b, d], tri_idx * 9 + b * 3 + d)) return ConstraintEQ(terms, 0.0)
def traction_continuity_constraints(pts, surface_tris, fault_tris, tensor_dim=3): from tectosaur.constraints import ConstraintEQ, Term from tectosaur.constraint_builders import find_touching_pts from tectosaur.util.geometry import unscaled_normals n_surf_tris = surface_tris.shape[0] n_fault_tris = fault_tris.shape[0] touching_pt = find_touching_pts(surface_tris) if fault_tris.shape[0] > 0: fault_touching_pt = find_touching_pts(fault_tris) else: fault_touching_pt = [] fault_touching_pt.extend( [[] for i in range(len(touching_pt) - len(fault_touching_pt))]) constraints = [] normals = unscaled_normals(pts[surface_tris]) normals /= np.linalg.norm(normals, axis=1)[:, np.newaxis] jj = 0 for i, tpt in enumerate(touching_pt): if len(tpt) == 0: continue for independent_idx in range(len(tpt)): independent = tpt[independent_idx] independent_tri_idx = independent[0] independent_tri = surface_tris[independent_tri_idx] for dependent_idx in range(independent_idx + 1, len(tpt)): dependent = tpt[dependent_idx] dependent_tri_idx = dependent[0] dependent_tri = surface_tris[dependent_tri_idx] n1 = normals[independent_tri_idx] n2 = normals[dependent_tri_idx] same_plane = np.all(np.abs(n1 - n2) < 1e-6) if not same_plane: jj += 1 print('not same plane', jj, n1, n2) continue # Check for anything that touches across the fault. crosses = (fault_tris.shape[0] > 0 and check_if_crosses_fault( independent_tri, dependent_tri, fault_touching_pt, fault_tris)) if crosses: continue for d in range(tensor_dim): independent_dof = (independent_tri_idx * 3 + independent[1]) * tensor_dim + d dependent_dof = (dependent_tri_idx * 3 + dependent[1]) * tensor_dim + d if dependent_dof <= independent_dof: continue diff = 0.0 constraints.append( ConstraintEQ([ Term(1.0, dependent_dof), Term(-1.0, independent_dof) ], diff)) return constraints
def make_constraint(lhs, rhs): terms = [] for k in range(3): terms.append(Term(lhs[1 + k], dof_start1 + k)) terms.append(Term(-rhs[1 + k], dof_start2 + k)) out.append(ConstraintEQ(terms, -lhs[0] + rhs[0]))
def continuity_constraints(pts, tris, fault_start_idx, tensor_dim=3): surface_tris = tris[:fault_start_idx] fault_tris = tris[fault_start_idx:] touching_pt = find_touching_pts(surface_tris) side = get_side_of_fault(pts, tris, fault_start_idx) constraints = [] for i, tpt in enumerate(touching_pt): if len(tpt) == 0: continue for independent_idx in range(len(tpt)): independent = tpt[independent_idx] independent_tri_idx = independent[0] independent_corner_idx = independent[1] independent_tri = surface_tris[independent_tri_idx] for dependent_idx in range(independent_idx + 1, len(tpt)): dependent = tpt[dependent_idx] dependent_tri_idx = dependent[0] dependent_corner_idx = dependent[1] dependent_tri = surface_tris[dependent_tri_idx] # Check for anything that touches across the fault. side1 = side[independent_tri_idx] side2 = side[dependent_tri_idx] crosses = (side1 != side2) and (side1 != 0) and (side2 != 0) fault_tri_idx = None if crosses: fault_tri_idxs, fault_corner_idxs = np.where( fault_tris == dependent_tri[dependent_corner_idx]) if fault_tri_idxs.shape[0] != 0: fault_tri_idx = fault_tri_idxs[0] fault_corner_idx = fault_corner_idxs[0] # plt_pts = np.vstack(( # pts[independent_tri], # pts[dependent_tri], # pts[fault_tris[fault_tri_idx]] # )) # import matplotlib.pyplot as plt # plt.tripcolor(pts[:,0], pts[:,1], tris[:surface_tris.shape[0]], side[:surface_tris.shape[0]]) # plt.triplot(plt_pts[:,0], plt_pts[:,1], np.array([[0,1,2]]), 'b-') # plt.triplot(plt_pts[:,0], plt_pts[:,1], np.array([[3,4,5]]), 'k-') # plt.triplot(pts[:,0], pts[:,1], tris[fault_start_idx:], 'r-') # plt.show() for d in range(tensor_dim): independent_dof = (independent_tri_idx * 3 + independent_corner_idx) * tensor_dim + d dependent_dof = (dependent_tri_idx * 3 + dependent_corner_idx) * tensor_dim + d if dependent_dof <= independent_dof: continue diff = 0.0 terms = [ Term(1.0, dependent_dof), Term(-1.0, independent_dof) ] if fault_tri_idx is not None: fault_dof = (fault_start_idx * 9 + fault_tri_idx * 9 + fault_corner_idx * 3 + d) if side1 < side2: terms.append(Term(-1.0, fault_dof)) else: terms.append(Term(1.0, fault_dof)) constraints.append(ConstraintEQ(terms, 0.0)) return constraints
def traction_admissibility_constraints(pts, tris, fault_start_idx): # At each vertex, there should be three remaining degrees of freedom. # Initially, there are n_tris*3 degrees of freedom. # So, we need (n_tris-1)*3 constraints. touching_pt = find_touching_pts(tris) ns = normalize(unscaled_normals(pts[tris])) side = get_side_of_fault(pts, tris, fault_start_idx) continuity_cs = [] admissibility_cs = [] for tpt in touching_pt: if len(tpt) == 0: continue # Separate the triangles touching at the vertex into a groups # by the normal vectors for each triangle. normal_groups = [] for i in range(len(tpt)): tri_idx = tpt[i][0] n = ns[tri_idx] joined = False for j in range(len(normal_groups)): if np.allclose(normal_groups[j][0], n): tri_idx2 = tpt[normal_groups[j][1][0]][0] side1 = side[tri_idx] side2 = side[tri_idx2] crosses = (side1 != side2) and (side1 != 0) and (side2 != 0) fault_tri_idx = None # if crosses: # continue normal_groups[j][1].append(i) joined = True break if not joined: normal_groups.append((n, [i])) # Continuity within normal group for i in range(len(normal_groups)): group = normal_groups[i][1] independent_idx = group[0] independent = tpt[independent_idx] independent_tri_idx = independent[0] independent_corner_idx = independent[1] independent_dof_start = independent_tri_idx * 9 + independent_corner_idx * 3 for j in range(1, len(group)): dependent_idx = group[j] dependent = tpt[dependent_idx] dependent_tri_idx = dependent[0] dependent_corner_idx = dependent[1] dependent_dof_start = dependent_tri_idx * 9 + dependent_corner_idx * 3 for d in range(3): terms = [ Term(1.0, dependent_dof_start + d), Term(-1.0, independent_dof_start + d) ] continuity_cs.append(ConstraintEQ(terms, 0.0)) if len(normal_groups) == 1: # Only continuity needed! continue # assert(len(normal_groups) == 2) # Add constant stress constraints for i in range(len(normal_groups)): tpt_idx1 = normal_groups[i][1][0] tri_idx1 = tpt[tpt_idx1][0] corner_idx1 = tpt[tpt_idx1][1] tri1 = pts[tris[tri_idx1]] tri_data1 = (tri1, tri_idx1, corner_idx1) for j in range(i + 1, len(normal_groups)): tpt_idx2 = normal_groups[j][1][0] tri_idx2 = tpt[tpt_idx2][0] # print(tri_idx1, tri_idx2) corner_idx2 = tpt[tpt_idx2][1] tri2 = pts[tris[tri_idx2]] tri_data2 = (tri2, tri_idx2, corner_idx2) # for c in new_cs: # print(', '.join(['(' + str(t.val) + ',' + str(t.dof) + ')' for t in c.terms]) + ' rhs: ' + str(c.rhs)) admissibility_cs.append( constant_stress_constraint(tri_data1, tri_data2)) admissibility_cs.append(equilibrium_constraint(tri_data1)) admissibility_cs.append(equilibrium_constraint(tri_data2)) return continuity_cs, admissibility_cs