try: from drbutil import * except ImportError: drbutilFound = False else: drbutilFound = True try: import polyscope as ps except ImportError: polyscopeFound = False else: polyscopeFound = True ps.set_up_dir('z_up') ps.init() try: import igl except ImportError: iglFound = False else: iglFound = True try: import cupy as cp except ImportError: cupyFound = False else: cupyFound = True try: import trimesh except ImportError: trimeshFound = False else: trimeshFound = True try: from embreex import rtcore as rtc from embreex import rtcore_scene as rtcs from embreex.mesh_construction import TriangleMesh except ImportError: try: from pyembree import rtcore as rtc from pyembree import rtcore_scene as rtcs from pyembree.mesh_construction import TriangleMesh except ImportError: embreeFound = False else: embreeFound = True else: embreeFound = True try: import scipy from scipy.interpolate import griddata except ImportError: scipyFound = False else: scipyFound = True try: import scipy.sparse as sparse import scipy.sparse.linalg as spla except ImportError: sciSparseFound = False else: sciSparseFound = True try: import skfem as fem from skfem.models.elasticity import linear_elasticity, lame_parameters, linear_stress from skfem.helpers import dot, ddot, sym_grad, jump, mul from skfem.assembly import BilinearForm except ImportError: skfemFound = False else: skfemFound = True # fast sparse factorization and solving try: import pypardiso except ImportError: pypardisoFound = False else: pypardisoFound = True print('using pypardiso') def solver_pypardiso(A, b, **kwargs): # mkl stores factorized (huge) matrix in undeletable memory return pypardiso.spsolve(A, b, factorize = len(b) < 1000000, **kwargs) def dg1Project(dg1, Csg): M, f = dg1._projection(Csg) if dg1.tind is not None: return fem.solve(*fem.condense(M, f, I=dg1.get_dofs(elements=dg1.tind)), solver = solver_pypardiso) return fem.solve(M, f, solver = solver_pypardiso) if sys.platform != 'win32' or '-mp' in sys.argv: print('mpCPU:', cpuCount) def linear_elasticity(Lambda=1., Mu=1.): """Weak form of the linear elasticity operator.""" C = linear_stress(Lambda, Mu) @BilinearForm(nthreads=cpuCount) def weakform(u, v, w): return ddot(C(sym_grad(u)), sym_grad(v)) return weakform """ def sparseCond(M): ew1, ev = sparse.linalg.eigsh(M, which='LM') ew2, ev = sparse.linalg.eigsh(M, sigma=eps) return np.abs(ew1).max() / np.abs(ew2).min() """ def lassoShrink(x, k): return np.maximum(x - k, 0.0) - np.maximum(-x - k, 0.0) def sampleStar(cIdx, nCells, vts): sPts = [] for nCell in nCells: qes = faceToEdges(nCell) if len(nCell) == 4 else hexaToEdges(nCell) nIdxs = np.unique(ringNeighborElements(qes, [cIdx]).ravel()) if vts.shape[1] == 2: sPts.append(generateTriGridSamples(vts[nIdxs], 13, False)) else: sPts.append(generateTetGridSamples(vts[nIdxs], 8, False)) return np.vstack(sPts) def computeOptiPoint(cIdx, cells, vts, useBB = False): nCells = ringNeighborElements(cells, [cIdx]) if useBB: pts = vts[nCells.ravel()] bbMin = pts.min(axis=0) bbMax = pts.max(axis=0) if pts.shape[1] < 3: bbVerts = quadVerts * (bbMax-bbMin)/2 + (bbMax+bbMin)/2 sPts = generateQuadGridSamples(bbVerts, 8, False) else: bbVerts = cubeVerts * (bbMax-bbMin)/2 + (bbMax+bbMin)/2 sPts = generateHexGridSamples(bbVerts, 8, False) else: sPts = sampleStar(cIdx, nCells, vts) tVts = vts.copy() mSJs = [] for sPt in sPts: tVts[cIdx] = sPt mSJs.append(computeJacobians(tVts[nCells], True).min()) if useBB: x,y,z = sPts.T s = z*0+1 scals = normZeroToOne(mSJs)**0.5 sPlot = mlab.quiver3d(x, y, z, s, s, s, scalars = scals, scale_factor = 0.0025, mode='sphere') sPlot.glyph.color_mode = 'color_by_scalar' sPlot.glyph.glyph_source.glyph_position = 'center' sys.exit() return sPts[np.argmax(mSJs)] alphaMin = 0.05 tauMinMax = {'tri': [(1 - np.sqrt(1 - alphaMin)) / 3, 1/3], 'quad': [(1 - np.sqrt(1 - alphaMin)) / 2, 1/2], 'tet': [0.01, 1/5], 'tetw': [0.01, 1/5], 'hex': [np.arccos(1-2*alphaMin)/(2*np.pi), 1/2], 'hexw': [(1 - (1-alphaMin)**(1/3))/2, 1/2]} def bandedTau(p, tau, mType, walled = False): tMin, tMax = tauMinMax[mType + 'w' * walled] return (tau * (1-abs(p)) + max(p,0)) * (tMax-tMin) + tMin def materializeFaceVerts(p, tau, verts, edges, faces, mType, averagedTaus = False): taus = np.ones(len(verts)) * tau if p is None else bandedTau(p, tau, mType) eVerts = [] for edge in edges: t0,t1 = [taus[edge].mean()]*2 if averagedTaus else taus[edge] eWs = [[1-t0, t0], [t1, 1-t1]] eVerts.append(np.dot(eWs, verts[edge])) fVerts = [] for face in faces: if mType == 'tri': t0,t1,t2 = [taus[face].mean()]*3 if averagedTaus else taus[face] fWs = [[1-2*t0,t0,t0], [t1,1-2*t1,t1], [t2,t2,1-2*t2]] elif mType == 'quad': a = lambda t: t**2-2*t+1 b = lambda t: t-t**2 c = lambda t: t**2 t0,t1,t2,t3 = [taus[face].mean()]*4 if averagedTaus else taus[face] fWs = [[a(t0), b(t0), c(t0), b(t0)], [b(t1), a(t1), b(t1), c(t1)], [c(t2), b(t2), a(t2), b(t2)], [b(t3), c(t3), b(t3), a(t3)]] else: t = taus[face].mean() fW = [t, 1-2*t, t] + [0] * (len(face)-3) fWs = [np.roll(fW, s-1) for s in range(len(face))] fVerts.append(np.dot(fWs, verts[face])) return np.vstack([verts, np.vstack(eVerts), np.vstack(fVerts)]) def materializeTetVerts(p, tau, verts, edges, tris, tets, mType, walled, averagedTaus = True): taus = np.ones(len(verts)) * tau if p is None else bandedTau(p, tau, mType, walled) eVerts = [] for edge in edges: t0,t1 = [taus[edge].mean()]*2 if averagedTaus else taus[edge] eWs = [[1-t0, t0], [t1, 1-t1]] if walled: eWs += [[(1-t0+t1)/2,(1-t1+t0)/2]] eVerts.append(np.dot(eWs, verts[edge])) fVerts = [] for tri in tris: t0,t1,t2 = [taus[tri].mean()]*3 if averagedTaus else taus[tri] fWs = [[1-2*t0,t0,t0], [t1,1-2*t1,t1], [t2,t2,1-2*t2]] if walled: fWs += [[(t1+t2)/2,(t2+1-2*t1)/2,(t1+1-2*t2)/2], [(t2+1-2*t0)/2,(t2+t0)/2,(t0+1-2*t2)/2], [(t1+1-2*t0)/2,(t0+1-2*t1)/2,(t0+t1)/2], [(1-2*t0+t1+t2)/3,(t0+1-2*t1+t2)/3,(t0+t1+1-2*t2)/3]] fVerts.append(np.dot(fWs, verts[tri])) cVerts = [] for tet in tets: t0,t1,t2,t3 = [taus[tet].mean()]*4 if averagedTaus else taus[tet] cWs = [[1-3*t0,t0,t0,t0], [t1,1-3*t1,t1,t1], [t2,t2,1-3*t2,t2], [t3,t3,t3,1-3*t3]] if walled: cWs += [[(1-3*t0+t1)/2,(1-3*t1+t0)/2,(t0+t1)/2,(t0+t1)/2], [(t1+t2)/2,(1-3*t1+t2)/2,(1-3*t2+t1)/2,(t1+t2)/2], [(1-3*t0+t2)/2,(t0+t2)/2,(1-3*t2+t0)/2,(t0+t2)/2], [(1-3*t0+t3)/2,(t0+t3)/2,(t0+t3)/2,(1-3*t3+t0)/2], [(t1+t3)/2,(1-3*t1+t3)/2,(t1+t3)/2,(1-3*t3+t1)/2], [(t2+t3)/2,(t2+t3)/2,(1-3*t2+t3)/2,(1-3*t3+t2)/2], [(t1+t2+t3)/3,(1-3*t1+t2+t3)/3,(t1+1-3*t2+t3)/3,(t1+t2+1-3*t3)/3], [(1-3*t0+t2+t3)/3,(t0+t2+t3)/3,(t0+1-3*t2+t3)/3,(t0+t2+1-3*t3)/3], [(1-3*t0+t1+t3)/3,(t0+1-3*t1+t3)/3,(t0+t1+t3)/3,(t0+t1+1-3*t3)/3], [(1-3*t0+t1+t2)/3,(t0+1-3*t1+t2)/3,(t0+t1+1-3*t2)/3,(t0+t1+t2)/3]] cVerts.append(np.dot(cWs, verts[tet])) return np.vstack([verts, np.vstack(eVerts), np.vstack(fVerts), np.vstack(cVerts)]) def materializeHexVerts(p, tau, verts, edges, quads, hexas, mType, walled, averagedTaus = True): taus = np.ones(len(verts)) * tau if p is None else bandedTau(p, tau, mType, walled) eVerts = [] for edge in edges: t0,t1 = [taus[edge].mean()]*2 if averagedTaus else taus[edge] eWs = [[1-t0, t0], [t1, 1-t1]] eVerts.append(np.dot(eWs, verts[edge])) fVerts = [] for quad in quads: a = lambda t: t**2-2*t+1 b = lambda t: t-t**2 c = lambda t: t**2 t0,t1,t2,t3 = [taus[quad].mean()]*4 if averagedTaus else taus[quad] fWs = [[a(t0), b(t0), c(t0), b(t0)], [b(t1), a(t1), b(t1), c(t1)], [c(t2), b(t2), a(t2), b(t2)], [b(t3), c(t3), b(t3), a(t3)]] fVerts.append(np.dot(fWs, verts[quad])) cVerts = [] for hexa in hexas: t0,t1,t2,t3,t4,t5,t6,t7 = [taus[hexa].mean()]*8 if averagedTaus else taus[hexa] a = lambda t: -(t-1)**3 b = lambda t: (t-1)**2 * t c = lambda t: t**2-t**3 d = lambda t: t**3 cWs = [[a(t0),b(t0),b(t0),b(t0),c(t0),c(t0),c(t0),d(t0)], [b(t1),a(t1),c(t1),c(t1),b(t1),b(t1),d(t1),c(t1)], [b(t2),c(t2),a(t2),c(t2),b(t2),d(t2),b(t2),c(t2)], [b(t3),c(t3),c(t3),a(t3),d(t3),b(t3),b(t3),c(t3)], [c(t4),b(t4),b(t4),d(t4),a(t4),c(t4),c(t4),b(t4)], [c(t5),b(t5),d(t5),b(t5),c(t5),a(t5),c(t5),b(t5)], [c(t6),d(t6),b(t6),b(t6),c(t6),c(t6),a(t6),b(t6)], [d(t7),c(t7),c(t7),c(t7),b(t7),b(t7),b(t7),a(t7)]] cVerts.append(np.dot(cWs, verts[hexa])) return np.vstack([verts, np.vstack(eVerts), np.vstack(fVerts), np.vstack(cVerts)]) def optimizeTaus(vTarget, taus, verts, cellsInit, cellsMat, mType, walled = False): pInit = 0 def showProgress(vTarget, vCurr, vDelta, p): print('vt: %0.6f, vc: %0.6f, vd: %0.6f, p: %0.6f'%(vTarget, vCurr, vDelta, p)) if mType in ['tri', 'quad']: edges, faces = cellsInit quadTris = facesToTris(cellsMat) def f(p, *args): vTarget, taus, verts = args[0] verts = materializeFaceVerts(p, taus, verts, edges, faces, mType) area = computeTriangleAreas(verts[quadTris], False).sum() err = abs(vTarget - area)**2 showProgress(vTarget, area, err, p[0]) return err elif mType == 'tet': edges, faces, cells = cellsInit hexaTets = np.vstack([hexaToTetras(hexa) for hexa in hexOrderDg2BT(cellsMat)]) def f(p, *args): vTarget, taus, verts = args[0] verts = materializeTetVerts(p, taus, verts, edges, faces, cells, mType, walled) vol = computeTetraVolumes(verts[hexaTets]).sum() err = abs(vTarget - vol)**2 showProgress(vTarget, vol, err, p[0]) return err elif mType == 'hex': edges, faces, cells = cellsInit hexaTets = np.vstack([hexaToTetras(hexa) for hexa in hexOrderDg2BT(cellsMat)]) def f(p, *args): vTarget, taus, verts = args[0] verts = materializeHexVerts(p, taus, verts, edges, faces, cells, mType, walled) vol = computeTetraVolumes(verts[hexaTets]).sum() err = abs(vTarget - vol)**2 showProgress(vTarget, vol, err, p[0]) return err # use L-BFGS-B or Nelder-Mead print('optimizing tau*') p = scipy.optimize.minimize(f, pInit, method="L-BFGS-B", bounds = [(-1,1)], args = [vTarget, taus, verts], options={"maxiter": 100}).x return p def computeBarycentricWeights(verts, tVerts, nDim, innerOnly = False, hullTolerance = False): if iglFound and not cupyFound: tVertsT = [np.float32(tVerts[:,i,:]) for i in range(nDim+1)] tVertsT = list(map(pad2Dto3D, tVertsT)) if nDim == 2 else tVertsT if nDim == 2: tVols = computeTriangleAreas(tVerts) subTVerts = tVerts[:,[[1,2],[2,0],[0,1],]].reshape(-1, nDim, nDim) else: if iglFound and innerOnly and not hullTolerance: hullVerts, hullTris = innerOnly windingNumbers = np.abs(igl.fast_winding_number_for_meshes(np.array(hullVerts, np.float64, order='F'), hullTris, verts)) tVols = computeTetraVolumes(tVerts) subTVerts = tVerts[:,[[1,2,3],[0,3,2],[0,1,3],[0,2,1]]].reshape(-1, nDim, nDim) if cupyFound: # cupy to much overhead for 2D ? tVols = cp.array(tVols) subTVerts = cp.array(subTVerts) verts = cp.array(verts) tVerts = cp.array(tVerts) tIdxs = np.zeros(len(verts), np.int32)-1 bWeights = np.zeros((len(verts), nDim+1), np.float32) for vIdx, vert in tqdm(enumerate(verts), total = len(verts), ascii=True, desc='baryWeights'): if nDim == 2: if not iglFound or cupyFound: subTriVols = simpleDets2x2(subTVerts - vert.reshape(1,2)) / 2.0 bCoords = subTriVols.reshape(-1,3) / tVols.reshape(-1,1) else: ps = pad2Dto3D(np.float32(np.repeat([vert], len(tVerts), axis=0))) bCoords = igl.barycentric_coordinates_tri(ps, tVertsT[0], tVertsT[1], tVertsT[2]) else: if iglFound and innerOnly and not hullTolerance and windingNumbers[vIdx] < 0.5: continue if not iglFound or cupyFound: subTetVols = simpleDets3x3(subTVerts - vert.reshape(1,3)) / 6.0 bCoords = subTetVols.reshape(-1,4) / tVols.reshape(-1,1) else: ps = np.float32(np.repeat([vert], len(tVerts), axis=0)) bCoords = igl.barycentric_coordinates_tet(ps, tVertsT[0], tVertsT[1], tVertsT[2], tVertsT[3]) if innerOnly or innerOnly is None: if hullTolerance: bCoordsNorm = cp.linalg.norm(bCoords, axis=1) if cupyFound else norm(bCoords) validMask = bCoordsNorm < 2 else: validMask = (bCoords >= 0).all(axis=1) if cupyFound: validMask = validMask.get() if validMask.any(): if hullTolerance: tIdx = bCoordsNorm.argmin() bWs = cp.clip(bCoords[tIdx], 0, 1) if cupyFound else np.clip(bCoords[tIdx], 0, 1) pVert = cp.dot(bWs / bWs.sum(), tVerts[tIdx]) if cupyFound else np.dot(bWs / bWs.sum(), tVerts[tIdx]) if (cp.linalg.norm(vert - pVert) if cupyFound else norm(vert - pVert)) > hullTolerance: continue else: tIdx = np.where(validMask)[0][0] tIdxs[vIdx] = tIdx bWeights[vIdx] = bCoords[tIdx].get() if cupyFound else bCoords[tIdx] else: tIdx = (cp.linalg.norm(bCoords, axis=1) if cupyFound else norm(bCoords)).argmin() tIdxs[vIdx] = tIdx bWeights[vIdx] = bCoords[tIdx].get() if cupyFound else bCoords[tIdx] return bWeights, tIdxs