MsqMatrix<3,2> LVQDTargetTest::target( const double* L, const MsqMatrix<3,2>* V, const MsqMatrix<2,2>* Q, const MsqMatrix<2,2>* D ) { ConstantTarget W_size ( L ? *L : 1.0, true ); ConstantTarget W_orient( V ? *V : I32 ); ConstantTarget W_skew ( Q ? *Q : I22 ); ConstantTarget W_aspect( D ? *D : I22 ); LVQDTargetCalculator LVQD( L ? &W_size : NULL, V ? &W_orient : NULL, Q ? &W_skew : NULL, D ? &W_aspect : NULL ); MsqError err; MsqMatrix<3,2> W; bool v; if (!V) { MsqMatrix<2,2> W_2D; v = LVQD.get_2D_target( pd2D, 0, Sample(0,0), W_2D, err ); W.set_row( 0, W_2D.row(0) ); W.set_row( 1, W_2D.row(1) ); W.set_row( 2, MsqMatrix<1,2>(0.0) ); } else { v = LVQD.get_surface_target( pd2D, 0, Sample(0,0), W, err ); } ASSERT_NO_ERROR(err); CPPUNIT_ASSERT(v); return W; }
bool get_surface_target( PatchData& pd, size_t element, Sample sample, MsqMatrix<3,2>& W_out, MsqError& err ) { MsqMatrix<2,2> W; bool rval = get_2D_target( pd, element, sample, W, err ); W_out.set_row( 0, W.row(0) ); W_out.set_row( 1, W.row(1) ); W_out.set_row( 2, MsqMatrix<1,2>(0.0) ); return rval; }
void MappingFunction3D::ideal( Sample location, MsqMatrix<3,3>& J, MsqError& err ) const { const Vector3D* coords = unit_element( element_topology(), true ); if (!coords) { MSQ_SETERR(err)(MsqError::UNSUPPORTED_ELEMENT); return; } const unsigned MAX_VERTS = 8; MsqVector<3> d_coeff_d_xi[MAX_VERTS]; size_t indices[MAX_VERTS], num_vtx = 0; derivatives( location, NodeSet(), indices, d_coeff_d_xi, num_vtx, err ); MSQ_ERRRTN(err); assert(num_vtx > 0 && num_vtx <= MAX_VERTS); J.zero(); for (size_t r = 0; r < num_vtx; ++r) { MsqMatrix<3,1> c( coords[indices[r]].to_array() ); J += c * transpose(d_coeff_d_xi[r]); } double size = Mesquite::cbrt(fabs(det(J))); assert(size > -1e-15); // no negative jacobians for ideal elements! divide( 1.0, size, size ); J *= size; }
/* Do transform M_hat = S_a M_{3x2}, M_{2x2} Theta^-1 M_hat * where the plane into which we are projecting is orthogonal * to the passed u vector. */ static inline bool project_to_perp_plane( MsqMatrix<3,2> J, const MsqVector<3>& u, const MsqVector<3>& u_perp, MsqMatrix<2,2>& A, MsqMatrix<3,2>& S_a_transpose_Theta ) { MsqVector<3> n_a = J.column(0) * J.column(1); double sc, len = length(n_a); if (!divide(1.0, len, sc)) return false; n_a *= sc; double ndot = n_a % u; double sigma = (ndot < 0.0) ? -1 : 1; double cosphi = sigma * ndot; MsqVector<3> cross = n_a * u; double sinphi = length(cross); MsqMatrix<3,2> Theta; Theta.set_column(0, u_perp); Theta.set_column(1, u*u_perp); // If columns of J are not in plane orthogonal to u, then // rotate J such that they are. if (sinphi > 1e-12) { MsqVector<3> m = sigma * cross; MsqVector<3> n = (1/sinphi) * m; MsqVector<3> p = (1-cosphi) * n; double s_a[] = { p[0]*n[0] + cosphi, p[0]*n[1] - m[2], p[0]*n[2] + m[1], p[1]*n[0] + m[2], p[1]*n[1] + cosphi, p[1]*n[2] - m[0], p[2]*n[0] - m[1], p[2]*n[1] + m[0], p[2]*n[2] + cosphi }; MsqMatrix<3,3> S_a(s_a); J = S_a * J; S_a_transpose_Theta = transpose(S_a) * Theta; } else { S_a_transpose_Theta = Theta; // J *= sigma; } // Project to get 2x2 A from A_hat (which might be equal to J) A = transpose(Theta) * J; return true; }
template <class QMType> inline void TMPQualityMetricTest<QMType>::test_evaluate_3D() { MsqPrintError err(cout); PatchData pd; bool rval; double value; QMType m( &ideal, &faux_two ); // test with aligned elements faux_two.count = 0; tester.get_ideal_element( HEXAHEDRON, true, pd ); rval = m.evaluate( pd, 0, value, err ); CPPUNIT_ASSERT(!MSQ_CHKERR(err)); CPPUNIT_ASSERT(rval); CPPUNIT_ASSERT_DOUBLES_EQUAL( faux_two.value, value, DBL_EPSILON ); CPPUNIT_ASSERT_EQUAL( 1, faux_two.count ); // test that columns are orthogonal for ideal hex element MsqMatrix<3,3> A = faux_two.last_A_3D; CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.0, A.column(0) % A.column(1), 1e-6 ); CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.0, A.column(0) % A.column(2), 1e-6 ); CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.0, A.column(1) % A.column(2), 1e-6 ); // test with rotated element faux_two.count = 0; tester.get_ideal_element( TETRAHEDRON, true, pd ); // rotate by 90-degrees about X axis for (size_t i = 0; i < pd.num_nodes(); ++i) { Vector3D orig = pd.vertex_by_index(i); Vector3D newp( orig[0], -orig[2], orig[1] ); pd.set_vertex_coordinates( newp, i, err ); } rval = m.evaluate( pd, 0, value, err ); CPPUNIT_ASSERT(!MSQ_CHKERR(err)); CPPUNIT_ASSERT(rval); CPPUNIT_ASSERT_DOUBLES_EQUAL( faux_two.value, value, DBL_EPSILON ); CPPUNIT_ASSERT_EQUAL( 1, faux_two.count ); }
void test_3d_eval_ortho_hex() { MsqPrintError err(cout); PatchData pd; bool rval; double value; QMType m( &ideal, &faux_zero ); faux_zero.count = 0; tester.get_ideal_element( HEXAHEDRON, true, pd ); rval = m.evaluate( pd, 0, value, err ); CPPUNIT_ASSERT(!MSQ_CHKERR(err)); CPPUNIT_ASSERT(rval); CPPUNIT_ASSERT_EQUAL( 1, faux_zero.count ); // test that columns are orthogonal for ideal hex element MsqMatrix<3,3> A = faux_zero.last_A_3D; CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.0, A.column(0) % A.column(1), 1e-6 ); CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.0, A.column(0) % A.column(2), 1e-6 ); CPPUNIT_ASSERT_DOUBLES_EQUAL( 0.0, A.column(1) % A.column(2), 1e-6 ); }
void JacobianCalculator::get_Jacobian_3D( const MappingFunction3D* mf, NodeSet ho_bits, Sample location, const Vector3D* verts, size_t num_type_vert, MsqMatrix<3,3>& J_out, MsqError& err ) { size_t num_vtx = 0; mf->derivatives( location, ho_bits, mIndices, mDerivs3D, num_vtx, err ); MSQ_ERRRTN(err); mf->convert_connectivity_indices( num_type_vert, mIndices, num_vtx, err ); MSQ_ERRRTN(err); const MsqVector<3>* d = mDerivs3D; const size_t* const e = mIndices + num_vtx; Vector3D c[3] = {Vector3D(0,0,0), Vector3D(0,0,0), Vector3D(0,0,0)}; for (const size_t* i = mIndices; i != e; ++i, ++d) { c[0] += (*d)[0] * verts[*i];; c[1] += (*d)[1] * verts[*i];; c[2] += (*d)[2] * verts[*i];; } J_out.set_column( 0, MsqMatrix<3,1>(c[0].to_array()) ); J_out.set_column( 1, MsqMatrix<3,1>(c[1].to_array()) ); J_out.set_column( 2, MsqMatrix<3,1>(c[2].to_array()) ); }
bool LambdaTarget::get_surface_target( PatchData& pd, size_t element, Sample sample, MsqMatrix<3,2>& W_out, MsqError& err ) { bool valid = lambdaSource->get_surface_target( pd, element, sample, W_out, err ); if (MSQ_CHKERR(err) && !valid) return false; MsqVector<3> cross = W_out.column(0) * W_out.column(1); double det1 = cross % cross; // length squared valid = compositeSource->get_surface_target( pd, element, sample, W_out, err ); if (MSQ_CHKERR(err) && !valid) return false; cross = W_out.column(0) * W_out.column(1); double det2 = cross % cross; // length squared if (det2 < 1e-15) return false; W_out *= sqrt( sqrt( det1/det2 ) ); return true; }
bool AffineMapMetric::evaluate( PatchData& pd, size_t handle, double& value, MsqError& err ) { Sample s = ElemSampleQM::sample( handle ); size_t e = ElemSampleQM:: elem( handle ); MsqMeshEntity& elem = pd.element_by_index( e ); EntityTopology type = elem.get_element_type(); unsigned edim = TopologyInfo::dimension( type ); const size_t* conn = elem.get_vertex_index_array(); // This metric only supports sampling at corners, except for simplices. // If element is a simpex, then the Jacobian is constant over a linear // element. In this case, always evaluate at any vertex. //unsigned corner = s.number; if (s.dimension != 0) { if (type == TRIANGLE || type == TETRAHEDRON) /*corner = 0*/; else { MSQ_SETERR(err)("Invalid sample point for AffineMapMetric", MsqError::UNSUPPORTED_ELEMENT ); return false; } } bool rval; if (edim == 3) { // 3x3 or 3x2 targets ? Vector3D c[3] = { Vector3D(0,0,0), Vector3D(0,0,0), Vector3D(0,0,0) }; unsigned n; const unsigned* adj = TopologyInfo::adjacent_vertices( type, s.number, n ); c[0] = pd.vertex_by_index( conn[adj[0]] ) - pd.vertex_by_index( conn[s.number] ); c[1] = pd.vertex_by_index( conn[adj[1]] ) - pd.vertex_by_index( conn[s.number] ); c[2] = pd.vertex_by_index( conn[adj[2]] ) - pd.vertex_by_index( conn[s.number] ); MsqMatrix<3,3> A; A.set_column( 0, MsqMatrix<3,1>(c[0].to_array()) ); A.set_column( 1, MsqMatrix<3,1>(c[1].to_array()) ); A.set_column( 2, MsqMatrix<3,1>(c[2].to_array()) ); if (type == TETRAHEDRON) A = A * TET_XFORM; MsqMatrix<3,3> W; targetCalc->get_3D_target( pd, e, s, W, err ); MSQ_ERRZERO(err); rval = targetMetric->evaluate( A * inverse(W), value, err ); MSQ_ERRZERO(err); } else { Vector3D c[2] = { Vector3D(0,0,0), Vector3D(0,0,0) }; unsigned n; const unsigned* adj = TopologyInfo::adjacent_vertices( type, s.number, n ); c[0] = pd.vertex_by_index( conn[adj[0]] ) - pd.vertex_by_index( conn[s.number] ); c[1] = pd.vertex_by_index( conn[adj[1]] ) - pd.vertex_by_index( conn[s.number] ); MsqMatrix<3,2> App; App.set_column( 0, MsqMatrix<3,1>(c[0].to_array()) ); App.set_column( 1, MsqMatrix<3,1>(c[1].to_array()) ); MsqMatrix<3,2> Wp; targetCalc->get_surface_target( pd, e, s, Wp, err ); MSQ_ERRZERO(err); MsqMatrix<2,2> A, W; MsqMatrix<3,2> RZ; surface_to_2d( App, Wp, W, RZ ); A = transpose(RZ) * App; if (type == TRIANGLE) A = A * TRI_XFORM; rval = targetMetric->evaluate( A*inverse(W), value, err ); MSQ_ERRZERO(err); } // apply target weight to value if (weightCalc) { double ck = weightCalc->get_weight( pd, e, s, err ); MSQ_ERRZERO(err); value *= ck; } return rval; }
bool InverseMeanRatio2D::evaluate_with_hess( const MsqMatrix<2,2>& A, const MsqMatrix<2,2>& W, double& result, MsqMatrix<2,2>& dA, MsqMatrix<2,2> d2A[3], MsqError& err ) { const MsqMatrix<2,2> Winv = inverse(W); const MsqMatrix<2,2> T = A * Winv; const double d = det( T ); if (invalid_determinant(d)) { result = 0.0; dA = d2A[0] = d2A[1] = d2A[2] = MsqMatrix<2,2>(0.0); return false; } else { const double inv_det = 1.0/d; result = sqr_Frobenius(T) * 0.5 * inv_det; const MsqMatrix<2,2> AT = transpose_adj(T); dA = AT; dA *= -result; dA += T; dA *= inv_det; dA = dA * transpose(Winv); const double p3 = -result * inv_det; const double p1 = -2.0 * p3 * inv_det; const double p2 = -inv_det * inv_det; const MsqMatrix<2,2> AT_T_op_00 = outer( AT.row(0), T.row(0)); const MsqMatrix<2,2> AT_T_op_11 = outer( AT.row(1), T.row(1)); d2A[0] = p1 * outer( AT.row(0), AT.row(0)) + p2 * (AT_T_op_00 + transpose(AT_T_op_00)); d2A[1] = p1 * outer( AT.row(0), AT.row(1)) + p2 * (outer( AT.row(0), T.row(1)) + outer( T.row(0), AT.row(1) )); d2A[2] = p1 * outer( AT.row(1), AT.row(1)) + p2 * (AT_T_op_11 + transpose(AT_T_op_11)); d2A[0](0,0) += inv_det; d2A[0](1,1) += inv_det; d2A[1](0,1) += p3; d2A[1](1,0) -= p3; d2A[2](0,0) += inv_det; d2A[2](1,1) += inv_det; d2A[0] = Winv * d2A[0] * transpose(Winv); d2A[1] = Winv * d2A[1] * transpose(Winv); d2A[2] = Winv * d2A[2] * transpose(Winv); result -= 1.0; return true; } }
bool TQualityMetric::evaluate_with_Hessian_diagonal( PatchData& pd, size_t handle, double& value, std::vector<size_t>& indices, std::vector<Vector3D>& grad, std::vector<SymMatrix3D>& diagonal, MsqError& err ) { const Sample s = ElemSampleQM::sample( handle ); const size_t e = ElemSampleQM:: elem( handle ); MsqMeshEntity& elem = pd.element_by_index( e ); EntityTopology type = elem.get_element_type(); unsigned edim = TopologyInfo::dimension( type ); size_t num_idx = 0; const NodeSet bits = pd.non_slave_node_set( e ); bool rval; if (edim == 3) { // 3x3 or 3x2 targets ? const MappingFunction3D* mf = pd.get_mapping_function_3D( type ); if (!mf) { MSQ_SETERR(err)( "No mapping function for element type", MsqError::UNSUPPORTED_ELEMENT ); return false; } MsqMatrix<3,3> A, W, dmdT, d2mdT2[6]; mf->jacobian( pd, e, bits, s, mIndices, mDerivs3D, num_idx, A, err ); MSQ_ERRZERO(err); targetCalc->get_3D_target( pd, e, s, W, err ); MSQ_ERRZERO(err); const MsqMatrix<3,3> Winv = inverse(W); const MsqMatrix<3,3> T = A*Winv; rval = targetMetric->evaluate_with_hess( T, value, dmdT, d2mdT2, err ); MSQ_ERRZERO(err); gradient<3>( num_idx, mDerivs3D, dmdT * transpose(Winv), grad ); second_deriv_wrt_product_factor( d2mdT2, Winv ); diagonal.resize( num_idx ); hessian_diagonal<3>(num_idx, mDerivs3D, d2mdT2, arrptr(diagonal) ); #ifdef PRINT_INFO print_info<3>( e, s, A, W, A * inverse(W) ); #endif } else if (edim == 2) { #ifdef NUMERICAL_2D_HESSIAN // use finite diference approximation for now return QualityMetric::evaluate_with_Hessian_diagonal( pd, handle, value, indices, grad, diagonal, err ); #else MsqMatrix<2,2> W, A, dmdT, d2mdT2[3]; MsqMatrix<3,2> M; rval = evaluate_surface_common( pd, s, e, bits, mIndices, num_idx, mDerivs2D, W, A, M, err ); if (MSQ_CHKERR(err) || !rval) return false; const MsqMatrix<2,2> Winv = inverse(W); const MsqMatrix<2,2> T = A*Winv; rval = targetMetric->evaluate_with_hess( T, value, dmdT, d2mdT2, err ); MSQ_ERRZERO(err); gradient<2>( num_idx, mDerivs2D, M * dmdT * transpose(Winv), grad ); second_deriv_wrt_product_factor( d2mdT2, Winv ); diagonal.resize( num_idx ); for (size_t i = 0; i < num_idx; ++i) { MsqMatrix<2,2> block2d; block2d(0,0) = transpose(mDerivs2D[i]) * d2mdT2[0] * mDerivs2D[i]; block2d(0,1) = transpose(mDerivs2D[i]) * d2mdT2[1] * mDerivs2D[i]; block2d(1,0) = block2d(0,1); block2d(1,1) = transpose(mDerivs2D[i]) * d2mdT2[2] * mDerivs2D[i]; MsqMatrix<3,2> p = M * block2d; SymMatrix3D& H = diagonal[i]; H[0] = p.row(0) * transpose(M.row(0)); H[1] = p.row(0) * transpose(M.row(1)); H[2] = p.row(0) * transpose(M.row(2)); H[3] = p.row(1) * transpose(M.row(1)); H[4] = p.row(1) * transpose(M.row(2)); H[5] = p.row(2) * transpose(M.row(2)); } #ifdef PRINT_INFO print_info<2>( e, s, J, Wp, A * inverse(W) ); #endif #endif } else { assert(0); return false; } // pass back index list indices.resize( num_idx ); std::copy( mIndices, mIndices+num_idx, indices.begin() ); // apply target weight to value if (!num_idx) weight( pd, s, e, num_idx, value, 0, 0, 0, err ); else weight( pd, s, e, num_idx, value, arrptr(grad), arrptr(diagonal), 0, err ); MSQ_ERRZERO(err); return rval; }