예제 #1
0
void SmartLaplacianSmoother::optimize_vertex_positions( PatchData &pd, 
                                                        MsqError &err )
{
  assert(pd.num_free_vertices() == 1);
  const size_t center_vtx_index = 0;
  const size_t init_inverted = num_inverted( pd, err ); MSQ_ERRRTN(err);
  
  adjVtxList.clear();
  pd.get_adjacent_vertex_indices( center_vtx_index, adjVtxList, err );
  MSQ_ERRRTN(err);
  
  if (adjVtxList.empty())
    return;
  
  const MsqVertex* verts = pd.get_vertex_array(err);
  const size_t n = adjVtxList.size();
  
  const Vector3D orig_pos = verts[center_vtx_index];
  Vector3D new_pos = verts[ adjVtxList[0] ];
  for (size_t i = 1; i < n; ++i)
    new_pos += verts[ adjVtxList[i] ];
  new_pos *= 1.0/n;
  pd.set_vertex_coordinates( new_pos, center_vtx_index, err );
  pd.snap_vertex_to_domain( center_vtx_index, err );  MSQ_ERRRTN(err);
  const size_t new_inverted = num_inverted( pd, err ); MSQ_ERRRTN(err);
  if (new_inverted > init_inverted)
    pd.set_vertex_coordinates( orig_pos, center_vtx_index, err );
}
예제 #2
0
void BoundedCylinderDomain::create_curve( double distance,
                                          Mesh* mesh,
                                          double tolerance,
                                          MsqError& err )
{
  std::vector<Mesh::VertexHandle> handles;
  mesh->get_all_vertices( handles, err ); MSQ_ERRRTN(err);
  if (handles.empty()) {
    MSQ_SETERR(err)("No vertices in mesh.\n", MsqError::INVALID_ARG );
    return;
  }
  
  std::vector<MsqVertex> coords(handles.size());
  mesh->vertices_get_coordinates( arrptr(handles), arrptr(coords), handles.size(), err );
  MSQ_ERRRTN(err);
  
  std::vector<Mesh::EntityHandle> list;
  Vector3D close, normal;
  for (size_t i = 0; i < handles.size(); ++i)
  {
    evaluate( distance, coords[i], close, normal );
    if ((coords[i] - close).length() < tolerance)
      list.push_back( handles[i] );
  }
  
  if (list.empty())
  {
    MSQ_SETERR(err)("No vertices within specified tolerance.\n", MsqError::INVALID_ARG );
    return;
  }
  
  create_curve( distance, list );
}
void TerminationCriterion::accumulate_inner( PatchData& pd, 
                                             OFEvaluator& of_eval,
                                             MsqError& err )
{
  double of_value = 0;
  
  if (terminationCriterionFlag & GRAD_FLAGS)
  {
    mGrad.resize( pd.num_free_vertices() );
    bool b = of_eval.evaluate(pd, of_value, mGrad, err);
    MSQ_ERRRTN(err);
    if (!b) {
      MSQ_SETERR(err)("Initial patch is invalid for gradient compuation.",
                      MsqError::INVALID_MESH);
      return;
    }
  }
  else if (terminationCriterionFlag & OF_FLAGS)
  {
    bool b = of_eval.evaluate(pd, of_value, err); MSQ_ERRRTN(err);
    if (!b) {
      MSQ_SETERR(err)("Invalid patch passed to TerminationCriterion.",
                      MsqError::INVALID_MESH);
      return;
    }
  }

  accumulate_inner( pd, of_value, mGrad.empty() ? 0 : arrptr(mGrad), err );  MSQ_CHKERR(err);
}
예제 #4
0
void TagVertexMesh::copy_all_coordinates( MsqError& err )
{
  if (!haveTagHandle) {
    tagHandle = get_mesh()->tag_create( tagName, Mesh::DOUBLE, 3, 0, err ); 
    MSQ_ERRRTN(err);
    haveTagHandle = true;
  }

  std::vector<Mesh::VertexHandle> handles;
  get_all_vertices( handles, err ); 
  MSQ_ERRRTN(err);
  if (handles.empty())
    return;
  
  std::vector<MsqVertex> coords(handles.size());
  get_mesh()->vertices_get_coordinates( arrptr(handles), arrptr(coords), handles.size(), err );
  MSQ_ERRRTN(err);
  
  std::vector<double> data( 3*handles.size() );
  std::vector<double>::iterator j = data.begin();
  std::vector<MsqVertex>::const_iterator i = coords.begin();
  while (i != coords.end()) {
    i->get_coordinates( &*j );
    ++i;
    j += 3;
  }
  
  tag_set_vertex_data( tagHandle, handles.size(), arrptr(handles), arrptr(data), err );
  MSQ_ERRRTN(err);
}
void MappingFunction3D::jacobian( const PatchData& pd,
                                  size_t element_number,
                                  NodeSet nodeset,
                                  Sample location,
                                  size_t* vertex_patch_indices_out,
                                  MsqVector<3>* d_coeff_d_xi_out,
                                  size_t& num_vtx_out,
                                  MsqMatrix<3,3>& jacobian_out,
                                  MsqError& err ) const
{
  const MsqMeshEntity& elem = pd.element_by_index( element_number );
  const size_t* conn = elem.get_vertex_index_array();
  
  derivatives( location, nodeset, vertex_patch_indices_out,
               d_coeff_d_xi_out, num_vtx_out, err ); MSQ_ERRRTN(err);
 
  convert_connectivity_indices( elem.node_count(), vertex_patch_indices_out, 
                                num_vtx_out, err );  MSQ_ERRRTN(err);
 
  jacobian_out.zero();
  size_t w = 0;
  for (size_t r = 0; r < num_vtx_out; ++r) {
    size_t i = conn[vertex_patch_indices_out[r]];
    MsqMatrix<3,1> coords( pd.vertex_by_index( i ).to_array() );
    jacobian_out += coords * transpose(d_coeff_d_xi_out[r]);
    if (i < pd.num_free_vertices()) {
      vertex_patch_indices_out[w] = i;
      d_coeff_d_xi_out[w] = d_coeff_d_xi_out[r];
      ++w;
    }
  }
  num_vtx_out = w;
}
예제 #6
0
void CompositeOFAdd::initialize_queue( MeshDomainAssoc* mesh_and_domain,
                                       const Settings* settings,
                                       MsqError& err )
{
  objFunc1->initialize_queue( mesh_and_domain, settings, err ); MSQ_ERRRTN(err);
  objFunc2->initialize_queue( mesh_and_domain, settings, err ); MSQ_ERRRTN(err);
}
예제 #7
0
void EdgeIterator::get_adjacent_vertices( MsqError& err )
{
  adjList.clear();
  
    // Get all adjacent elements
  size_t num_elem;
  const size_t* elems = patchPtr->get_vertex_element_adjacencies( vertIdx, num_elem, err );
  MSQ_ERRRTN(err);
  
    // Get all adjacent vertices from elements
  std::vector<size_t> elem_verts;
  for (size_t e = 0; e < num_elem; ++e)
  {
    MsqMeshEntity& elem = patchPtr->element_by_index(elems[e]);
    EntityTopology type = elem.get_element_type();
    size_t num_edges = TopologyInfo::edges( type );
    
    bool mid_edge, mid_face, mid_vol;
    TopologyInfo::higher_order( type, elem.node_count(), mid_edge, mid_face, mid_vol, err );
    MSQ_ERRRTN(err);
    
      // For each edge
    for (size_t d = 0; d < num_edges; ++d)
    {
      const unsigned* edge = TopologyInfo::edge_vertices( type, d, err );
      MSQ_ERRRTN(err);
      size_t vert1 = elem.get_vertex_index( edge[0] );
      size_t vert2 = elem.get_vertex_index( edge[1] );

      size_t mid = ~(size_t)0;
      if (mid_edge) {
        int p = TopologyInfo::higher_order_from_side( type, elem.node_count(), 1, d, err );
        MSQ_ERRRTN(err);
        mid = elem.get_vertex_index_array()[p];
      }

        // If this edge contains the input vertex (vert_idx)
        // AND the input vertex index is less than the 
        // other vertex (avoids iterating over this edge twice)
        // add it to the list.
      if (vert1 > vert2)
      {
        if (vert2 == vertIdx)
          adjList.push_back( Edge(vert1,mid) );
      } 
      else 
      {
        if (vert1 == vertIdx)
          adjList.push_back( Edge(vert2,mid) );
      }
    }
  }
  
    // Remove duplicates
  std::sort( adjList.begin(), adjList.end() );
  adjIter = std::unique( adjList.begin(), adjList.end() );
  adjList.resize( adjIter - adjList.begin() );
  adjIter = adjList.begin();
}
예제 #8
0
void MeshImplData::reset_element( size_t index,
                                  const std::vector<size_t>& vertices,
                                  EntityTopology topology,
                                  MsqError& err )
{
  clear_element( index, err );                   MSQ_ERRRTN(err);
  set_element( index, vertices, topology, err ); MSQ_ERRRTN(err);
}
예제 #9
0
void VertexMover::initialize_queue( Mesh* mesh,
                                    MeshDomain* domain,
                                    Settings* settings,
                                    MsqError& err )
{
  QualityImprover::initialize_queue( mesh, domain, settings, err ); MSQ_ERRRTN(err);
  objFuncEval.initialize_queue( mesh, domain, settings, err ); MSQ_ERRRTN(err);
}
예제 #10
0
  // Create patch containing one ideal element, optionally higher-order.
  // For 2D elements, will attach appropriate planar domain.
  inline void create_ideal_element_patch( PatchData& pd,
                                          EntityTopology type, 
                                          size_t num_nodes,
                                          MsqError& err )
  {
     static PlanarDomain zplane(PlanarDomain::XY);
     static Settings settings;
     settings.set_slaved_ho_node_mode( Settings::SLAVE_NONE );
     pd.attach_settings( &settings );
     
  
       // build list of vertex coordinates
     const Vector3D* corners = unit_edge_element( type );
     std::vector<Vector3D> coords( corners, corners+TopologyInfo::corners(type) );
     bool mids[4] = {false};
     TopologyInfo::higher_order( type, num_nodes, mids[1], mids[2], mids[3], err );
     MSQ_ERRRTN(err);
     std::vector<size_t> conn(coords.size());
     for (unsigned i = 0; i < coords.size(); ++i)
       conn[i] = i;
 
     for (unsigned dim = 1; dim <= TopologyInfo::dimension(type); ++dim) {
       if (!mids[dim])
         continue;
       
       int num_side;
       if (dim == TopologyInfo::dimension(type))
         num_side = 1;
       else
         num_side = TopologyInfo::adjacent( type, dim );
       
       for (int s = 0; s < num_side; ++s) {
         unsigned idx = TopologyInfo::higher_order_from_side( type, num_nodes, dim, s, err );
         MSQ_ERRRTN(err);
         conn.push_back(idx);
       
         unsigned n;
         const unsigned* side = TopologyInfo::side_vertices( type, dim, s, n, err );
         MSQ_ERRRTN(err);
         Vector3D avg = coords[side[0]];
         for (unsigned v = 1; v < n; ++v)
           avg += coords[side[v]];
         avg *= 1.0/n;
         coords.push_back(avg);
       }
     }
     
     bool* fixed = new bool[coords.size()];
     std::fill( fixed, fixed+coords.size(), false );
     pd.fill( coords.size(), coords[0].to_array(), 1, &type, 
              &num_nodes, &conn[0], fixed, err );
     delete [] fixed;
     MSQ_ERRRTN(err);
     
     if (TopologyInfo::dimension(type) == 2)
       pd.set_domain( &zplane );
  }
void TMPQualityMetric::initialize_queue( MeshDomainAssoc* mesh_and_domain,
                                         const Settings* settings,
                                         MsqError& err )
{
  targetCalc->initialize_queue( mesh_and_domain, settings, err ); MSQ_ERRRTN(err);
  if (weightCalc) {
    weightCalc->initialize_queue( mesh_and_domain, settings, err ); 
    MSQ_ERRRTN(err);
  }
}
예제 #12
0
void AddQualityMetric::get_evaluations( PatchData& pd,
                                        std::vector<size_t>& handles,
                                        bool free_only,
                                        MsqError& err )
{
  metric1.get_evaluations( pd, handles, free_only, err );
  MSQ_ERRRTN(err);
  metric2.get_evaluations( pd, mHandles, free_only, err );
  MSQ_ERRRTN(err);
  if (handles != mHandles) {
    MSQ_SETERR(err)("Incompatible metrics", MsqError::INVALID_STATE);
  }
}
예제 #13
0
void TagVertexMesh::initialize( Mesh* mesh, std::string name, MsqError& err )
{
  MeshDecorator::set_mesh( mesh );
  tagName = name;

  tagHandle = get_mesh()->tag_get( tagName, err );
    // If tag isn't defined yet, we're done for now.
  if (err.error_code() == MsqError::TAG_NOT_FOUND) {
    err.clear();
    return;
  } 
  else if (MSQ_CHKERR(err))
    return;
  
    // If tag is already defined, make sure it is the correct type.
  std::string t_name;
  Mesh::TagType type;
  unsigned length;
  tag_properties( tagHandle, t_name, type, length, err ); 
  MSQ_ERRRTN(err);
  if (!(type == Mesh::DOUBLE && length == 3) &&
      !(type == Mesh::BYTE && length == 3*sizeof(double)))
    MSQ_SETERR(err)(MsqError::TAG_ALREADY_EXISTS,
                    "Tag \"%s\" has invalid type or size.",
                    tagName.c_str());
 
    // If tag is already defined and init was true, reset tag
    // values.
  haveTagHandle = true;
}
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;
}
void ObjectiveFunctionTemplate::initialize_queue( Mesh* mesh,
                                               MeshDomain* domain,
                                               const Settings* settings,
                                               MsqError& err )
{
  qualityMetric->initialize_queue( mesh, domain, settings, err ); MSQ_ERRRTN(err);
}
NonGradient::NonGradient(ObjectiveFunction* of, MsqError &err) 
  : VertexMover(of),
    PatchSetUser(true),
    projectGradient(false),
    mDimension(0),
    mThreshold(0.0),
    mTolerance(0.0),
    mMaxNumEval(0),
    mNonGradDebug(0),
    mUseExactPenaltyFunction(true),
    mScaleDiameter(0.1)
{
  set_debugging_level(2);
  //set the default inner termination criterion
  TerminationCriterion* default_crit=get_inner_termination_criterion();
  if(default_crit==NULL){
    MSQ_SETERR(err)("QualityImprover did not create a default inner "
                    "termination criterion.", MsqError::INVALID_STATE);
    return;
  }
  else{
    default_crit->add_iteration_limit( 5 );
    MSQ_ERRRTN(err);
  }
}  
예제 #17
0
void TagVertexMesh::vertex_set_coordinates( VertexHandle vertex,
                                            const Vector3D &coordinates,
                                            MsqError &err )
{
  if (!haveTagHandle)
  {
    tagHandle = get_mesh()->tag_create( tagName, Mesh::DOUBLE, 3, 0, err ); 
    MSQ_ERRRTN(err);
    haveTagHandle = true;
    copy_all_coordinates( err );
    MSQ_ERRRTN(err);  
  }
  
  get_mesh()->tag_set_vertex_data( tagHandle, 1, &vertex, coordinates.to_array(), err );
  MSQ_ERRRTN(err);
}
예제 #18
0
void TMPQualityMetric::weight( PatchData& pd,
                               Sample sample,
                               size_t elem,
                               int num_idx,
                               double& value,
                               Vector3D* grad,
                               SymMatrix3D* diag,
                               Matrix3D* hess,
                               MsqError& err )
{
    if (!weightCalc)
        return;

    double ck = weightCalc->get_weight( pd, elem, sample, err );
    MSQ_ERRRTN(err);
    value *= ck;
    if (grad) {
        for (int i = 0; i < num_idx; ++i)
            grad[i] *= ck;
    }
    if (diag) {
        for (int i = 0; i < num_idx; ++i)
            diag[i] *= ck;
    }
    if (hess) {
        const int n = num_idx * (num_idx+1) / 2;
        for (int i = 0; i < n; ++i)
            hess[i] *= ck;
    }
}
예제 #19
0
void TagVertexMesh::check_remove_tag( MsqError& err )
{
  if (cleanUpTag && haveTagHandle) {
    tag_delete( tagHandle, err );
    MSQ_ERRRTN(err);
  }
  haveTagHandle = false;
}
/*!
 */
  void I_DFT_NoBarrierSmoother::optimize_vertex_positions(PatchData &pd, 
                                                          MsqError &err)
  {
    MSQ_FUNCTION_TIMER( "I_DFT_NoBarrierSmoother::optimize_vertex_positions" );
    
      // does the I_DFT_NoBarrier smoothing
    MsqFreeVertexIndexIterator free_iter(pd, err);  MSQ_ERRRTN(err);
    free_iter.reset();
    free_iter.next();
      //m is the free vertex.
    size_t m=free_iter.value();
      //move vertex m
    i_dft_no_barrier_smooth_mesh(pd, m, err); MSQ_ERRRTN(err);
      //snap vertex m to domain
    pd.snap_vertex_to_domain(m,err);
  
  }
예제 #21
0
void PaverMinEdgeLengthWrapper::run_wrapper( Mesh* mesh,
                                             ParallelMesh* pmesh,
                                             MeshDomain* domain,
                                             Settings* settings,
                                             QualityAssessor* qa,
                                             MsqError& err )
{
  InstructionQueue q;
 
    // calculate average lambda for mesh
  ReferenceMesh ref_mesh( mesh );
  RefMeshTargetCalculator W_0( &ref_mesh );
  SimpleStats lambda_stats;
  MeshUtil tool(mesh, settings);
  tool.lambda_distribution( lambda_stats, err ); MSQ_ERRRTN(err);
  double lambda = lambda_stats.average();
  
    // create objective function
  IdealShapeTarget W_i;
  LambdaConstant W( lambda, &W_i );
  TShapeSizeB1 tm;
  TQualityMetric mu_0( &W, &tm );
  ElementPMeanP mu( 1.0, &mu_0 );
  PMeanPTemplate of( 1.0, &mu );
  
    // create quality assessor
  EdgeLengthMetric len(0.0);
  qa->add_quality_assessment( &mu );
  qa->add_quality_assessment( &len );
  q.add_quality_assessor( qa, err );
  
    // create solver
  TrustRegion solver( &of );
  TerminationCriterion tc, ptc;
  tc.add_absolute_vertex_movement( maxVtxMovement );
  tc.add_iteration_limit( iterationLimit );
  ptc.add_iteration_limit( pmesh ? parallelIterations : 1 );
  solver.set_inner_termination_criterion( &tc );
  solver.set_outer_termination_criterion( &ptc );
  q.set_master_quality_improver( &solver, err ); MSQ_ERRRTN(err);
  q.add_quality_assessor( qa, err );

  // Optimize mesh
  q.run_common( mesh, pmesh, domain, settings, err ); MSQ_CHKERR(err);  
}
예제 #22
0
 /*! \brief increment the coordinates of the index-th vertex in the raw array
 */
 inline void PatchData::move_vertex( const Vector3D &delta,
                                     size_t index,
                                     MsqError &err) 
 {
   if (index >= vertexArray.size()) {
     MSQ_SETERR(err)( "Index bigger than numVertices.", MsqError::INVALID_ARG );
     return;
   }
   
   vertexArray[index] += delta;
   
   if (numSlaveVertices) {
     size_t num_elem;
     const size_t *indices;
     indices = get_vertex_element_adjacencies( index, num_elem, err ); MSQ_ERRRTN(err);
     update_slave_node_coordinates( indices, num_elem, err ); MSQ_ERRRTN(err);
   }
 }
void TerminationCriterion::reset_patch(PatchData &pd, MsqError &err)
{
  const unsigned long totalFlag = terminationCriterionFlag | cullingMethodFlag;
  if (totalFlag & MOVEMENT_FLAGS)
  {
    if (previousVerticesMemento)
      pd.recreate_vertices_memento(previousVerticesMemento,err); 
    else
      previousVerticesMemento = pd.create_vertices_memento(err);
    MSQ_ERRRTN(err);
  }

  if (totalFlag & UNTANGLED_MESH) {
    patchInvertedCount = count_inverted( pd, err );
    //MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o Num Patch Inverted: " << " " << patchInvertedCount << std::endl;
    MSQ_ERRRTN(err);
  }
}
예제 #24
0
void GlobalPatch::get_patch( PatchHandle patch_handle,
                             std::vector<Mesh::ElementHandle>& elem_handles_out,
                             std::vector<Mesh::VertexHandle>& free_vertices_out,
                             MsqError& err )
{
  free_vertices_out.clear();
  assert(GLOBAL_PATCH_HANDLE == patch_handle);
  get_mesh()->get_all_elements( elem_handles_out, err ); MSQ_ERRRTN(err);
  //get_mesh()->get_all_vertices( free_vertices_out, err ); MSQ_ERRRTN(err);
}
예제 #25
0
void SizeAdaptShapeWrapper::run_wrapper( Mesh* mesh, 
                                         ParallelMesh* pmesh,
                                         MeshDomain* domain, 
                                         Settings* settings,
                                         QualityAssessor* qa,
                                         MsqError& err )
{
  InstructionQueue q;
 
    // calculate average lambda for mesh
  TagVertexMesh init_mesh( err, mesh );  MSQ_ERRRTN(err);
  ReferenceMesh ref_mesh( &init_mesh );
  RefMeshTargetCalculator W_0( &ref_mesh );
  q.add_tag_vertex_mesh( &init_mesh, err );  MSQ_ERRRTN(err);
  
    // create objective function
  IdealShapeTarget W_i;
  LambdaTarget W( &W_0, &W_i );
  Target2DShapeSizeBarrier tm2;
  Target3DShapeSizeBarrier tm3;
  TMPQualityMetric mu( &W, &tm2, &tm3 );
  PMeanPTemplate of( 1.0, &mu );
  
    // create quality assessor
  EdgeLengthMetric len(0.0);
  qa->add_quality_assessment( &mu );
  qa->add_quality_assessment( &len );
  q.add_quality_assessor( qa, err );
  
    // create solver
  TrustRegion solver( &of );
  TerminationCriterion tc, ptc;
  tc.add_absolute_vertex_movement( maxVtxMovement );
  tc.add_iteration_limit( iterationLimit );
  ptc.add_iteration_limit( pmesh ? parallelIterations : 1 );
  solver.set_inner_termination_criterion( &tc );
  solver.set_outer_termination_criterion( &ptc );
  q.set_master_quality_improver( &solver, err ); MSQ_ERRRTN(err);
  q.add_quality_assessor( qa, err );

  // Optimize mesh
  q.run_common( mesh, pmesh, domain, settings, err ); MSQ_CHKERR(err);  
}
예제 #26
0
/*! Writes a gnuplot file directly from the MeshSet.
    This means that any mesh imported successfully into Mesquite
    can be outputed in gnuplot format.

    Within gnuplot, use \b plot 'file1.gpt' w l, 'file2.gpt' w l  
    
    This is not geared for performance, since it has to load a global Patch from
    the mesh to write a mesh file. 
*/
void MeshSet::write_gnuplot(const char* out_filebase,
                   Mesquite::MsqError &err)
{
    // Open the file
  string out_filename = out_filebase;
  out_filename += ".gpt";
  ofstream file(out_filename.c_str());
  if (!file)
  {
    MSQ_SETERR(err)(MsqError::FILE_ACCESS);
    return;
  }

    // loads a global patch
  PatchData pd;
  PatchDataParameters pd_params;
  pd_params.set_patch_type(PatchData::GLOBAL_PATCH, err); MSQ_ERRRTN(err);
  pd_params.no_culling_method();
  get_next_patch(pd, pd_params, err); MSQ_ERRRTN(err);
    
    // Write a header
  file << "\n";
  
  for (size_t i=0; i<pd.num_elements(); ++i)
  {
    std::vector<size_t> vtx_indices;
    pd.elementArray[i].get_node_indices(vtx_indices);
    for (size_t j = 0; j < vtx_indices.size(); ++j)
    {
      file << pd.vertexArray[vtx_indices[j]][0] << ' '
           << pd.vertexArray[vtx_indices[j]][1] << ' '
           << pd.vertexArray[vtx_indices[j]][2] << '\n';
    }
      file << pd.vertexArray[vtx_indices[0]][0] << ' '
           << pd.vertexArray[vtx_indices[0]][1] << ' '
           << pd.vertexArray[vtx_indices[0]][2] << '\n';
    file << '\n';
  }
  
    // Close the file
  file.close();
}
void TerminationCriterion::accumulate_patch( PatchData& pd, MsqError& err )
{
  if (terminationCriterionFlag & MOVEMENT_FLAGS)
  {
    double patch_max_dist = pd.get_max_vertex_movement_squared( previousVerticesMemento, err );
    if (patch_max_dist > maxSquaredMovement)
      maxSquaredMovement = patch_max_dist;
    pd.recreate_vertices_memento( previousVerticesMemento, err );  MSQ_ERRRTN(err);
  }
    
    //if terminating on bounded vertex movement (a bounding box for the mesh)
  if(terminationCriterionFlag & BOUNDED_VERTEX_MOVEMENT)
  {
    const MsqVertex* vert = pd.get_vertex_array(err);
    int num_vert = pd.num_free_vertices();
    int i=0;
      //for each vertex
    for(i=0;i<num_vert;++i)
    {
        //if any of the coordinates are greater than eps
      if( (vert[i][0]>boundedVertexMovementEps) ||
          (vert[i][1]>boundedVertexMovementEps) ||
          (vert[i][2]>boundedVertexMovementEps) )
      {
        ++vertexMovementExceedsBound;
      }
    }
  }


  if ((terminationCriterionFlag|cullingMethodFlag) & UNTANGLED_MESH) {
    size_t new_count = count_inverted( pd, err );
      // be careful here because size_t is unsigned
    globalInvertedCount += new_count;
    globalInvertedCount -= patchInvertedCount;
    patchInvertedCount = new_count;
    //if (innerOuterType==TYPE_OUTER) 
    //  MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o Num Patch Inverted: " << " " << patchInvertedCount << " globalInvertedCount= " << globalInvertedCount << std::endl;
      
    MSQ_ERRRTN(err);
  }
}
void TerminationCriterion::accumulate_inner( PatchData& pd, 
                                             double of_value,
                                             Vector3D* grad_array,
                                             MsqError& err )
{
  //if terminating on the norm of the gradient
  //currentGradL2NormSquared = HUGE_VAL;
  if (terminationCriterionFlag & (GRADIENT_L2_NORM_ABSOLUTE | GRADIENT_L2_NORM_RELATIVE)) 
  {
    currentGradL2NormSquared = length_squared(grad_array, pd.num_free_vertices()); // get the L2 norm
    MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o Info -- gradient L2 norm: " << " "
                                   << RPM(std::sqrt(currentGradL2NormSquared)) << std::endl;
  }
  //currentGradInfNorm = 10e6;
  if (terminationCriterionFlag & (GRADIENT_INF_NORM_ABSOLUTE | GRADIENT_INF_NORM_RELATIVE)) 
  {
    currentGradInfNorm = Linf(grad_array, pd.num_free_vertices()); // get the Linf norm
    MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o Info -- gradient Inf norm: " << " "
                                   << RPM(currentGradInfNorm) << std::endl;
  } 
  
  if (terminationCriterionFlag & VERTEX_MOVEMENT_RELATIVE)
  {
    maxSquaredInitialMovement = pd.get_max_vertex_movement_squared(
                               initialVerticesMemento, err );  MSQ_ERRRTN(err);
    MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o Info -- max initial vertex movement: " << " "
                                   << RPM(maxSquaredInitialMovement) << std::endl;
  }
  
  previousOFValue = currentOFValue;
  currentOFValue = of_value;
  if (terminationCriterionFlag & OF_FLAGS) {
    MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o Info -- OF Value: " << " " << RPM(of_value) << " iterationCounter= " << iterationCounter << std::endl;
  }
  else if (grad_array) {
    MSQ_DBGOUT_P0_ONLY(debugLevel) << par_string() << "  o OF Value: " << " " << RPM(of_value) << " iterationCounter= " << iterationCounter 
      //<< " terminationCriterionFlag= " << terminationCriterionFlag << " OF_FLAGS = " << OF_FLAGS
                                   << std::endl;
  }
  
  ++iterationCounter;
  if (timeStepFileType)
    write_timestep( pd, grad_array, err);
    
  if (plotFile.is_open()) 
    plotFile << iterationCounter 
     << '\t' << mTimer.since_birth() 
     << '\t' << of_value 
     << '\t' << std::sqrt( currentGradL2NormSquared ) 
     << '\t' << currentGradInfNorm 
     << '\t' << (maxSquaredMovement > 0.0 ? std::sqrt( maxSquaredMovement ) : 0.0)
     << '\t' << globalInvertedCount
     << std::endl;
}
void ReferenceMesh::get_reference_vertex_coordinates( 
                           const Mesh::VertexHandle* vertices,
                           size_t num_vertices,
                           Vector3D* coordinates_out,
                           MsqError& err )
{
  tmpStorage.resize( num_vertices );
  mMesh->vertices_get_coordinates( vertices, arrptr(tmpStorage), num_vertices, err );
  MSQ_ERRRTN(err);
  std::copy( tmpStorage.begin(), tmpStorage.end(), coordinates_out );
}
void TerminationCriterion::accumulate_outer(Mesh* mesh, 
                                            MeshDomain* domain, 
                                            OFEvaluator& of_eval,
                                            const Settings* settings,
                                            MsqError &err)
{
  PatchData global_patch;
  if (settings)
    global_patch.attach_settings( settings );
  
    //if we need to fill out the global patch data object.
  if ((terminationCriterionFlag & (GRAD_FLAGS|OF_FLAGS|VERTEX_MOVEMENT_RELATIVE))
      || timeStepFileType)
  {
    global_patch.set_mesh( mesh );
    global_patch.set_domain( domain );
    global_patch.fill_global_patch( err ); MSQ_ERRRTN(err);
  }
  
  accumulate_inner( global_patch, of_eval, err ); MSQ_ERRRTN(err);
}