// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced // at the Lawrence Livermore National Laboratory. All Rights reserved. See files // LICENSE and NOTICE for details. LLNL-CODE-806117. // // This file is part of the MFEM library. For more information and source code // availability visit https://mfem.org. // // MFEM is free software; you can redistribute it and/or modify it under the // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details. #include "mesquite.hpp" #ifdef MFEM_USE_MESQUITE #include "../fem/fem.hpp" #include namespace mfem { using namespace std; MesquiteMesh::MeshTags::TagData::~TagData() { if (elementData) { free(elementData); } if (vertexData) { free(vertexData); } if (defaultValue) { free(defaultValue); } } void MesquiteMesh::MeshTags::clear() { for (std::vector::iterator iter = tagList.begin(); iter != tagList.end(); ++iter) if (*iter) { delete *iter; } tagList.clear(); } size_t MesquiteMesh::MeshTags::size_from_tag_type( Mesh::TagType type ) { switch ( type ) { case Mesh::BYTE: return 1; case Mesh::BOOL: return sizeof(bool); case Mesh::DOUBLE: return sizeof(double); case Mesh::INT: return sizeof(int); case Mesh::HANDLE: return sizeof(void*); case Mesh::LONG_LONG: return sizeof(long long); default: assert(0); return 0; } } size_t MesquiteMesh::MeshTags::create( const std::string& name, Mesh::TagType type, unsigned length, const void* defval, MsqError& err ) { size_t h = handle( name, err ); if (h) { MSQ_SETERR(err)(name, MsqError::TAG_ALREADY_EXISTS); return 0; } if (length == 0 || size_from_tag_type(type) == 0) { MSQ_SETERR(err)(MsqError::INVALID_ARG); return 0; } TagData* tag = new TagData( name, type, length ); h = tagList.size(); tagList.push_back(tag); if (defval) { tag->defaultValue = malloc( tag->desc.size ); memcpy( tag->defaultValue, defval, tag->desc.size ); } return h+1; } size_t MesquiteMesh::MeshTags::create( const MfemTagDescription& desc, const void* defval, MsqError& err ) { size_t h = handle( desc.name.c_str(), err ); if (h) { MSQ_SETERR(err)(desc.name.c_str(), MsqError::TAG_ALREADY_EXISTS); return 0; } err.clear(); if (desc.size == 0 || (desc.size % size_from_tag_type(desc.type)) != 0) { MSQ_SETERR(err)(MsqError::INVALID_ARG); return 0; } TagData* tag = new TagData( desc ); h = tagList.size(); tagList.push_back(tag); if (defval) { tag->defaultValue = malloc( tag->desc.size ); memcpy( tag->defaultValue, defval, tag->desc.size ); } return h+1; } void MesquiteMesh::MeshTags::destroy( size_t tag_index, MsqError& err ) { --tag_index; if (tag_index >= tagList.size() || 0 == tagList[tag_index]) { MSQ_SETERR(err)(MsqError::TAG_NOT_FOUND); return ; } delete tagList[tag_index]; tagList[tag_index] = 0; } size_t MesquiteMesh::MeshTags::handle( const std::string& name, MsqError& err ) const { for (size_t i = 0; i < tagList.size(); ++i) if (tagList[i] && tagList[i]->desc.name == name) { return i+1; } return 0; } const MesquiteMesh::MfemTagDescription& MesquiteMesh::MeshTags::properties( size_t tag_index, MsqError& err ) const { static MfemTagDescription dummy_desc; --tag_index; if (tag_index >= tagList.size() || !tagList[tag_index]) { MSQ_SETERR(err)("Invalid tag handle", MsqError::INVALID_ARG); return dummy_desc; } return tagList[tag_index]->desc; } void MesquiteMesh::MeshTags::set_element_data( size_t tag_index, size_t num_indices, const size_t* index_array, const void* values, MsqError& err ) { size_t i; char* data; --tag_index; if (tag_index >= tagList.size() || !tagList[tag_index]) { MSQ_SETERR(err)("Invalid tag handle", MsqError::INVALID_ARG); return; } TagData* tag = tagList[tag_index]; // Get highest element index size_t total = tag->elementCount; for (i = 0; i < num_indices; ++i) if (index_array[i] >= total) { total = index_array[i] + 1; } // If need more space if (total > tag->elementCount) { // allocate more space tag->elementData = realloc( tag->elementData, tag->desc.size * total ); // if a default value, initialize new space with it if (tag->defaultValue) { data = ((char*)tag->elementData) + tag->elementCount * tag->desc.size; for (i = tag->elementCount; i < total; ++i) { memcpy( data, tag->defaultValue, tag->desc.size ); data += tag->desc.size; } } else { memset( (char*)tag->elementData + tag->elementCount * tag->desc.size, 0, (total - tag->elementCount) * tag->desc.size ); } tag->elementCount = total; } // Store passed tag values data = (char*)tag->elementData; const char* iter = (const char*)values; for (i = 0; i < num_indices; ++i) { memcpy( data + index_array[i]*tag->desc.size, iter, tag->desc.size ); iter += tag->desc.size; } } void MesquiteMesh::MeshTags::get_element_data( size_t tag_index, size_t num_indices, const size_t* index_array, void* values, MsqError& err ) const { --tag_index; if (tag_index >= tagList.size() || !tagList[tag_index]) { MSQ_SETERR(err)("Invalid tag handle", MsqError::INVALID_ARG); return; } TagData* tag = tagList[tag_index]; char* iter = (char*)values; const char* data = (const char*)tag->elementData; for (size_t i = 0; i < num_indices; ++i) { const void* ptr; size_t index = index_array[i]; if (index >= tag->elementCount) { ptr = tag->defaultValue; if (!ptr) { MSQ_SETERR(err)(MsqError::TAG_NOT_FOUND); return; } } else { ptr = data + index * tag->desc.size; } memcpy( iter, ptr, tag->desc.size ); iter += tag->desc.size; } } void MesquiteMesh::MeshTags::set_vertex_data( size_t tag_index, size_t num_indices, const size_t* index_array, const void* values, MsqError& err ) { size_t i; char* data; --tag_index; if (tag_index >= tagList.size() || !tagList[tag_index]) { MSQ_SETERR(err)("Invalid tag handle", MsqError::INVALID_ARG); return; } TagData* tag = tagList[tag_index]; // Get highest element index size_t total = tag->vertexCount; for (i = 0; i < num_indices; ++i) if (index_array[i] >= total) { total = index_array[i] + 1; } // If need more space if (total > tag->vertexCount) { // allocate more space tag->vertexData = realloc( tag->vertexData, tag->desc.size * total ); // if a default value, initialize new space with it if (tag->defaultValue) { data = ((char*)tag->vertexData) + tag->vertexCount * tag->desc.size; for (i = tag->vertexCount; i < total; ++i) { memcpy( data, tag->defaultValue, tag->desc.size ); data += tag->desc.size; } } else { memset( (char*)tag->vertexData + tag->vertexCount * tag->desc.size, 0, (total - tag->vertexCount) * tag->desc.size ); } tag->vertexCount = total; } // Store passed tag values data = (char*)tag->vertexData; const char* iter = (const char*)values; for (i = 0; i < num_indices; ++i) { memcpy( data + index_array[i]*tag->desc.size, iter, tag->desc.size ); iter += tag->desc.size; } } void MesquiteMesh::MeshTags::get_vertex_data( size_t tag_index, size_t num_indices, const size_t* index_array, void* values, MsqError& err ) const { --tag_index; if (tag_index >= tagList.size() || !tagList[tag_index]) { MSQ_SETERR(err)("Invalid tag handle", MsqError::INVALID_ARG); return; } TagData* tag = tagList[tag_index]; char* iter = (char*)values; const char* data = (const char*)tag->vertexData; for (size_t i = 0; i < num_indices; ++i) { const void* ptr; size_t index = index_array[i]; if (index >= tag->vertexCount) { ptr = tag->defaultValue; if (!ptr) { MSQ_SETERR(err)(MsqError::TAG_NOT_FOUND); return; } } else { ptr = data + index * tag->desc.size; } memcpy( iter, ptr, tag->desc.size ); iter += tag->desc.size; } } bool MesquiteMesh::MeshTags::tag_has_vertex_data( size_t tag_index, MsqError& err ) { --tag_index; if (tag_index >= tagList.size() || !tagList[tag_index]) { MSQ_SETERR(err)("Invalid tag handle", MsqError::INVALID_ARG); return false; } TagData* tag = tagList[tag_index]; return 0 != tag->vertexData || tag->defaultValue; } bool MesquiteMesh::MeshTags::tag_has_element_data( size_t tag_index, MsqError& err ) { --tag_index; if (tag_index >= tagList.size() || !tagList[tag_index]) { MSQ_SETERR(err)("Invalid tag handle", MsqError::INVALID_ARG); return false; } TagData* tag = tagList[tag_index]; return 0 != tag->elementData || tag->defaultValue; } MesquiteMesh::MeshTags::TagIterator MesquiteMesh::MeshTags::tag_begin() { size_t index = 0; while (index < tagList.size() && tagList[index] == NULL) { ++index; } return TagIterator( this, index ); } MesquiteMesh::MeshTags::TagIterator MesquiteMesh::MeshTags::TagIterator::operator++() { ++index; while (index < tags->tagList.size() && NULL == tags->tagList[index]) { ++index; } return TagIterator( tags, index ); } MesquiteMesh::MeshTags::TagIterator MesquiteMesh::MeshTags::TagIterator::operator--() { --index; while (index < tags->tagList.size() && NULL == tags->tagList[index]) { --index; } return TagIterator( tags, index ); } MesquiteMesh::MeshTags::TagIterator MesquiteMesh::MeshTags::TagIterator::operator++(int) { size_t old = index; ++index; while (index < tags->tagList.size() && NULL == tags->tagList[index]) { ++index; } return TagIterator( tags, old ); } MesquiteMesh::MeshTags::TagIterator MesquiteMesh::MeshTags::TagIterator::operator--(int) { size_t old = index; --index; while (index < tags->tagList.size() && NULL == tags->tagList[index]) { --index; } return TagIterator( tags, old ); } // // MesquiteMesh implementation follows // MesquiteMesh::MesquiteMesh(mfem::Mesh *mfem_mesh) : myTags( new MeshTags ) { mesh = mfem_mesh; nelems = mesh->GetNE(); nodes = mesh->GetNodes(); if (nodes) { fes = nodes->FESpace(); ndofs = fes->GetNDofs(); fes->BuildElementToDofTable(); const Table *elem_dof = &fes->GetElementToDofTable(); dof_elem = new Table; Transpose(*elem_dof, *dof_elem, ndofs); } else { ndofs = mesh->GetNV(); dof_elem = mesh->GetVertexToElementTable(); } mByte = vector(ndofs); mFixed = vector(ndofs, false); // By default, flag all boundary nodes as fixed Array bdofs; for (int i = 0; i < mesh->GetNBE(); i++) { if (nodes) { fes->GetBdrElementDofs(i, bdofs); } else { mesh->GetBdrElementVertices(i, bdofs); } for (int j = 0; j < bdofs.Size(); j++) { mFixed[bdofs[j]] = true; } } } int MesquiteMesh::get_geometric_dimension(Mesquite::MsqError &err) { return mesh->Dimension(); } void MesquiteMesh::get_all_elements(std::vector& elements, Mesquite::MsqError& err) { elements.resize(nelems); for (int i = 0; i < nelems; i++) { elements[i] = (ElementHandle) i; } } void MesquiteMesh::get_all_vertices(std::vector& vertices, Mesquite::MsqError& err) { vertices.resize(ndofs); for (int i = 0; i < ndofs; i++) { vertices[i] = (VertexHandle) i; } } void MesquiteMesh::vertices_get_coordinates(const VertexHandle vert_array[], MsqVertex* coordinates, size_t num_vtx, MsqError &err) { const size_t *indices = (const size_t*) vert_array; double coords[3]; for (int i = 0; i < num_vtx; i++) { mesh->GetNode(indices[i], coords); coordinates[i].x(coords[0]); coordinates[i].y(coords[1]); if (mesh->Dimension() == 3 ) { coordinates[i].z(coords[2]); } else { coordinates[i].z(0.0); } } } void MesquiteMesh::vertex_set_coordinates(VertexHandle vertex, const Vector3D &coordinates, MsqError &err) { double coords[3]; coords[0] = coordinates.x(); coords[1] = coordinates.y(); coords[2] = coordinates.z(); mesh->SetNode((size_t) vertex, coords); } void MesquiteMesh::vertex_set_byte(VertexHandle vertex, unsigned char byte, MsqError &err) { size_t index = (size_t) vertex; mByte[index] = byte; } void MesquiteMesh::vertices_set_byte(const VertexHandle *vert_array, const unsigned char *byte_array, size_t array_size, MsqError &err) { const size_t* indices = (const size_t*) vert_array; for (int i = 0; i < array_size; i++) { mByte[indices[i]] = byte_array[i]; } } void MesquiteMesh::vertex_get_byte(const VertexHandle vertex, unsigned char *byte, MsqError &err ) { *byte = mByte[(const size_t) vertex]; } void MesquiteMesh::vertices_get_byte(const VertexHandle *vertex, unsigned char *byte_array, size_t array_size, MsqError &err ) { const size_t* indices = (const size_t*) vertex; for (int i = 0; i < array_size; i++) { byte_array[i] = mByte[indices[i]]; } } void MesquiteMesh::vertices_get_fixed_flag(const VertexHandle vert_array[], std::vector& fixed_flag_array, size_t num_vtx, MsqError &err ) { fixed_flag_array.resize(num_vtx + 1); const size_t* indices = (const size_t*) vert_array; for (int i = 0; i < num_vtx; i++) { fixed_flag_array[i] = mFixed[indices[i]]; } } void MesquiteMesh::vertices_set_fixed_flag(const VertexHandle vert_array[], const std::vector< bool > &fixed_flag_array, size_t num_vtx, MsqError &err ) { const size_t* indices = (const size_t*) vert_array; for (int i = 0; i < num_vtx; i++) { mFixed[indices[i]] = fixed_flag_array[i]; } } void MesquiteMesh::elements_get_attached_vertices(const ElementHandle *elem_handles, size_t num_elems, std::vector& vert_handles, std::vector& offsets, MsqError &err) { const size_t* indices = (const size_t*) elem_handles; vert_handles.clear(); offsets.resize(num_elems + 1); Array elem_dofs; for (int i = 0; i < num_elems; i++) { offsets[i] = vert_handles.size(); elem = mesh->GetElement(indices[i]); if (nodes) { fes->GetElementDofs(indices[i],elem_dofs); } else { elem->GetVertices(elem_dofs); } for (int j = 0; j < elem_dofs.Size(); j++) { // Ordering of this matters!!! // We are good for triangles, quads and hexes. What about tets? vert_handles.push_back( (VertexHandle) elem_dofs[j] ); } } offsets[num_elems] = vert_handles.size(); } void MesquiteMesh::vertices_get_attached_elements(const VertexHandle* vertex_array, size_t num_vertex, std::vector& elements, std::vector& offsets, MsqError& err) { const size_t* indices = (const size_t*) vertex_array; elements.clear(); offsets.resize(num_vertex + 1); for (int i = 0; i < num_vertex; i++) { offsets[i] = elements.size(); int* vertex_elems = dof_elem->GetRow(indices[i]); for (int j = 0; j < dof_elem->RowSize(indices[i]); j++) { elements.push_back( (ElementHandle) vertex_elems[j] ); } } offsets[num_vertex] = elements.size(); } void MesquiteMesh::elements_get_topologies(const ElementHandle *element_handle_array, EntityTopology *element_topologies, size_t num_elements, MsqError &err) { // In MESQUITE: // TRIANGLE = 8 // QUADRILATERAL = 9 // TETRAHEDRON = 11 // HEXAHEDRON = 12 // In MFEM: // POINT = 0 // SEGMENT = 1 // TRIANGLE = 2 // QUADRILATERAL = 3 // TETRAHEDRON = 4 // HEXAHEDRON = 5 // BISECTED = 6 // QUADRISECTED = 7 // OCTASECTED = 8 int mfem_to_mesquite[9] = {0,0,8,9,11,12,0,0,0}; const size_t* indices = (const size_t*) element_handle_array; for (int i = 0; i < num_elements; i++) { element_topologies[i] = (EntityTopology) mfem_to_mesquite[mesh->GetElementType( indices[i])]; } } MesquiteMesh::~MesquiteMesh() { MsqPrintError err(mfem::err); delete myTags; delete dof_elem; } void MesquiteMesh::tag_attributes() { MsqError err; // create a tag for a single integer value TagHandle attributeTagHandle = tag_create( "material", Mesh::INT, 1, 0, err ); int *materialValues = new int[nelems]; for (int i=0; iGetAttribute(i); } // // now put these values into an element tag // std::vector elements; get_all_elements(elements, err ); tag_set_element_data( attributeTagHandle, nelems, arrptr(elements), (const void*)(materialValues), err ); delete[] materialValues; } TagHandle MesquiteMesh::tag_create( const std::string& name, TagType type, unsigned length, const void* defval, MsqError& err ) { size_t size = MeshTags::size_from_tag_type( type ); MfemTagDescription desc( name, type, length*size ); size_t index = myTags->create( desc, defval, err ); MSQ_ERRZERO(err); return (TagHandle)index; } void MesquiteMesh::tag_delete( TagHandle handle, MsqError& err ) { myTags->destroy( (size_t)handle, err ); MSQ_CHKERR(err); } TagHandle MesquiteMesh::tag_get( const std::string& name, MsqError& err ) { size_t index = myTags->handle( name, err ); MSQ_ERRZERO(err); if (!index) { MSQ_SETERR(err)( MsqError::TAG_NOT_FOUND, "could not find tag \"%s\"", name.c_str() ); } return (TagHandle)index; } void MesquiteMesh::tag_properties( TagHandle handle, std::string& name, TagType& type, unsigned& length, MsqError& err ) { const MfemTagDescription& desc = myTags->properties( (size_t)handle, err ); MSQ_ERRRTN(err); name = desc.name; type = desc.type; length = (unsigned)(desc.size / MeshTags::size_from_tag_type( desc.type )); } void MesquiteMesh::tag_set_element_data( TagHandle handle, size_t num_elems, const ElementHandle* elem_array, const void* values, MsqError& err ) { myTags->set_element_data( (size_t)handle, num_elems, (const size_t*)elem_array, values, err ); MSQ_CHKERR(err); } void MesquiteMesh::tag_get_element_data( TagHandle handle, size_t num_elems, const ElementHandle* elem_array, void* values, MsqError& err ) { myTags->get_element_data( (size_t)handle, num_elems, (const size_t*)elem_array, values, err ); MSQ_CHKERR(err); } void MesquiteMesh::tag_set_vertex_data( TagHandle handle, size_t num_elems, const VertexHandle* elem_array, const void* values, MsqError& err ) { myTags->set_vertex_data( (size_t)handle, num_elems, (const size_t*)elem_array, values, err ); MSQ_CHKERR(err); } void MesquiteMesh::tag_get_vertex_data( TagHandle handle, size_t num_elems, const VertexHandle* elem_array, void* values, MsqError& err ) { myTags->get_vertex_data( (size_t)handle, num_elems, (const size_t*)elem_array, values, err ); MSQ_CHKERR(err); } static void BoundaryPreservingOptimization(mfem::MesquiteMesh &mesh) { MsqDebug::enable(1); MsqPrintError err(mfem::err); int pOrder = 2; int mNumInterfaceSmoothIters = 5; bool mFixBndryInInterfaceSmooth = true; //this fixes exterior surface nodes bool project_gradient= false; double cos_crease_angle=0.2; // get all vertices std::vector vertices; mesh.get_all_vertices(vertices, err); if (MSQ_CHKERR(err)) {mfem::out << err << std::endl; exit(EXIT_FAILURE);} int num_vertices = vertices.size(); // get application fixed vertices std::vector app_fixed(num_vertices); mesh.vertices_get_fixed_flag(&(vertices[0]), app_fixed, num_vertices, err); if (MSQ_CHKERR(err)) {mfem::out << err << std::endl; exit(EXIT_FAILURE);} int num_app_fixed = 0; for (int i = 0; i < num_vertices; i++) { if (app_fixed[i]) { num_app_fixed++; } } mfem::out << "mesh has " << num_vertices << " vertices and " << num_app_fixed << " are app fixed. "; // create planar domain for interior and assessor queues Mesquite::PlanarDomain geom( PlanarDomain::XY ); // tag the underlying mesh material attributes. mesh.tag_attributes(); // create boundary domain and find mesh boundary Mesquite::MeshBoundaryDomain2D* mesh_domain = new Mesquite::MeshBoundaryDomain2D( MeshBoundaryDomain2D::XY, 0.0, project_gradient, Mesquite::MeshBoundaryDomain2D::QUADRATIC); mesh_domain->skin_area_mesh(&mesh,cos_crease_angle,"material"); std::vector theBoundaryVertices; mesh_domain->get_boundary_vertices( theBoundaryVertices ); int num_boundary_vertices = theBoundaryVertices.size(); std::vector theBoundaryEdges; mesh_domain->get_boundary_edges( theBoundaryEdges ); // int num_boundary_edges = theBoundaryEdges.size(); std::vector fixed_flags_boundary(num_boundary_vertices); // get application fixed boundary vertices std::vector app_fixed_boundary(num_boundary_vertices); mesh.vertices_get_fixed_flag(&(theBoundaryVertices[0]),app_fixed_boundary, num_boundary_vertices, err); int num_app_fixed_boundary = 0; for (int i = 0; i < num_boundary_vertices; i++) { if (app_fixed_boundary[i]) { num_app_fixed_boundary++; } } mfem::out << "mesh has " << num_boundary_vertices << " boundary vertices and " << num_app_fixed_boundary << " are app fixed" << std::endl; // only fix boundary vertices along corners int num_fixed_boundary_flags = 0; std::vector theCornerVertices; mesh_domain->get_corner_vertices( theCornerVertices ); // fix only vertices that are classified as corners for (int i = 0; i < num_boundary_vertices; i++) { if (!mFixBndryInInterfaceSmooth) { fixed_flags_boundary[i] = false; } else { fixed_flags_boundary[i] = app_fixed_boundary[i]; } for (int j = 0; j < theCornerVertices.size(); j++) { // printf("theCornerVertices[%d]=%lu\n",j, (size_t)(theCornerVertices[j])); if (theCornerVertices[j] == theBoundaryVertices[i]) { fixed_flags_boundary[i] = true; num_fixed_boundary_flags++; break; } } } printf("fixed %d of %d boundary vertices (those classified corner)\n", num_fixed_boundary_flags, num_boundary_vertices); // creates three intruction queues Mesquite::InstructionQueue boundary_queue; Mesquite::InstructionQueue interior_queue; boundary_queue.set_slaved_ho_node_mode(Settings::SLAVE_ALL); interior_queue.set_slaved_ho_node_mode(Settings::SLAVE_ALL); TShapeB1 targetMetric; IdealShapeTarget tc; TQualityMetric metric( &tc, &targetMetric ); Mesquite::LPtoPTemplate* obj_func = new Mesquite::LPtoPTemplate(&metric, pOrder, err); if (MSQ_CHKERR(err)) {mfem::out << err << std::endl; exit(EXIT_FAILURE);} Mesquite::QuasiNewton* boundary_alg = new Mesquite::QuasiNewton( obj_func); boundary_alg->use_element_on_vertex_patch(); Mesquite::QuasiNewton* interior_alg = new Mesquite::QuasiNewton( obj_func ); interior_alg->use_global_patch(); // **************Set stopping criterion************** double grad_norm = 1e-5; double successiveEps = 1e-5; int boundary_outer = 5; int boundary_inner = 3; // for boundary Mesquite::TerminationCriterion* boundaryTermInner = new Mesquite::TerminationCriterion(); Mesquite::TerminationCriterion* boundaryTermOuter = new Mesquite::TerminationCriterion(); // boundaryTermInner->add_absolute_gradient_L2_norm(grad_norm); // boundaryTermInner->add_relative_successive_improvement(successiveEps); boundaryTermOuter->add_relative_successive_improvement(successiveEps); boundaryTermOuter->add_iteration_limit(boundary_outer); boundaryTermInner->add_iteration_limit(boundary_inner); ostringstream bndryStream; bndryStream<<"boundary_"<write_mesh_steps(bndryStream.str().c_str()); boundary_alg->set_outer_termination_criterion(boundaryTermOuter); boundary_alg->set_inner_termination_criterion(boundaryTermInner); // for interior Mesquite::TerminationCriterion* interiorTermInner = new Mesquite::TerminationCriterion(); Mesquite::TerminationCriterion* interiorTermOuter = new Mesquite::TerminationCriterion(); interiorTermInner->add_absolute_gradient_L2_norm(grad_norm); interiorTermInner->add_relative_successive_improvement(successiveEps); // interiorTermInner->add_iteration_limit(3); // for element_on_vertex_patch mode interiorTermInner->add_iteration_limit(100); // for global_patch mode ostringstream interiorStream; interiorStream<<"interior_"<write_mesh_steps(interiorStream.str().c_str()); interiorTermOuter->add_iteration_limit(1); interior_alg->set_outer_termination_criterion(interiorTermOuter); interior_alg->set_inner_termination_criterion(interiorTermInner); // ConditionNumberQualityMetric qm_metric; // QualityAssessor boundary_assessor,interior_assessor; // boundary_assessor.add_quality_assessment( &metric, 10 ); // boundary_assessor.add_quality_assessment( &qm_metric ); // interior_assessor.add_quality_assessment( &metric, 10 ); // interior_assessor.add_quality_assessment( &qm_metric ); // set the boundary instruction queue // boundary_queue.add_quality_assessor( &boundary_assessor, err ); boundary_queue.set_master_quality_improver(boundary_alg, err); // boundary_queue.add_quality_assessor( &boundary_assessor, err ); if (MSQ_CHKERR(err)) {mfem::out << err << std::endl; exit(EXIT_FAILURE);} // set the interior instruction queue // interior_queue.add_quality_assessor( &interior_assessor, err ); interior_queue.set_master_quality_improver(interior_alg, err); // interior_queue.add_quality_assessor( &interior_assessor, err ); if (MSQ_CHKERR(err)) {mfem::out << err << std::endl; exit(EXIT_FAILURE);} err.clear(); std::vector fixed_flags(num_vertices); for (int j=0; jget_iteration_count() <<" outer and "<get_iteration_count() <<" inner iterations."<get_iteration_count() <<" outer and "<get_iteration_count() <<" inner iterations."<set_slaved_ho_node_mode(Settings::SLAVE_NONE); if ( this->Dimension() == 3 ) { method->run_instructions(&msq_mesh, err); } else { Vector3D normal(0,0,1); Vector3D point(0,0,0); PlanarDomain mesh_plane(normal, point); method->run_instructions(&msq_mesh, &mesh_plane, err); } } else { // boundary perserving smoothing doesn't have a wrapper yet. BoundaryPreservingOptimization( msq_mesh ); } } } #endif