void interp_nodal_hessian(
      const int hess_id,
      PerceptMesh& eMesh, 
      VectorFieldType* nodal_hessian_field)
    {
      const int spatial_dim = eMesh.get_spatial_dim();

      stk::mesh::Selector selector = 
	eMesh.get_fem_meta_data()->locally_owned_part() | 
	eMesh.get_fem_meta_data()->globally_shared_part();

      std::vector<stk::mesh::Bucket*> buckets;
      stk::mesh::get_buckets( selector, eMesh.get_bulk_data()->buckets( eMesh.node_rank() ), buckets );
      
      for ( vector<stk::mesh::Bucket*>::const_iterator k = buckets.begin() ; k != buckets.end() ; ++k ) {

	stk::mesh::Bucket & bucket = **k ;
	
	const unsigned num_nodes_in_bucket = bucket.size();	

	for (unsigned i = 0; i < num_nodes_in_bucket; i++) {

	  stk::mesh::Entity& node = bucket[i];

	  const double *coords = stk::mesh::field_data( *eMesh.get_coordinates_field() , node);
	  double *hess = stk::mesh::field_data( *nodal_hessian_field , node);
	    
	  exact_hessian(hess_id, coords, hess, spatial_dim);
	}
      }
    }
      void UnitTestSupport::save_or_diff(PerceptMesh& eMesh, std::string filename, int option )
      {
        if (always_do_regression_tests || option == 1)
          {
            unsigned p_size = eMesh.get_parallel_size();
            unsigned p_rank = eMesh.get_parallel_rank();
            std::string par_filename = filename;
            if (p_size > 1)
              {
                par_filename = filename+"."+toString(p_size)+"."+toString(p_rank);
              }
            if (Util::file_exists(par_filename))
              {
                int spatialDim = eMesh.get_spatial_dim();

                PerceptMesh eMesh1(spatialDim);
                eMesh.save_as("./tmp.e");
                eMesh1.open_read_only("./tmp.e");

                PerceptMesh eMesh_gold(spatialDim);
                eMesh_gold.open_read_only(filename);
                //eMesh_gold.print_info("gold copy: "+filename, 2);
                //eMesh1.print_info("compare to: "+filename, 2);
                {
                  std::string diff_msg = "gold file diff report: "+filename+" \n";
                  bool print_during_diff = false;
                  bool diff = PerceptMesh::mesh_difference(eMesh1, eMesh_gold, diff_msg, print_during_diff);
                  if (diff)
                    {
                      //std::cout << "tmp writing and reading to cleanup parts" << std::endl;

                      // write out and read back in to cleanup old parts
                      eMesh1.save_as("./tmp.e");
                      PerceptMesh eMesh2(spatialDim);
                      eMesh2.open_read_only("./tmp.e");
                      //std::cout << "tmp done writing and reading to cleanup parts" << std::endl;
                      bool diff_2 = PerceptMesh::mesh_difference(eMesh2, eMesh_gold, diff_msg, print_during_diff);
                      //std::cout << "tmp diff_2= " << diff_2 << std::endl;
                      diff = diff_2;
                      if (diff_2)
                        {
                          //bool diff_3 =
                          PerceptMesh::mesh_difference(eMesh2, eMesh_gold, diff_msg, true);
                        }
                    }

                  STKUNIT_EXPECT_TRUE(!diff);
                }
              }
            else
              {
                eMesh.save_as(filename);
              }
          }
        else
          {
            eMesh.save_as(filename);
          }
      }
Beispiel #3
0
    void PMMParallelReferenceMeshSmoother::run_wrapper( Mesh* mesh,
                                                        ParallelMesh* pmesh,
                                                        MeshDomain* domain,
                                                        Settings* settings,
                                                        QualityAssessor* qa,
                                                        MsqError& err )
    {
      std::cout << "\nP[" << Mesquite::get_parallel_rank() << "] tmp srk PMMParallelReferenceMeshSmoother innerIter= " << innerIter << " parallelIterations= " << parallelIterations << std::endl;

      //if (!get_parallel_rank()) 
      std::cout << "\nP[" << get_parallel_rank() << "] tmp srk PMMParallelReferenceMeshSmoother: running shape improver... \n" << std::endl;

      PerceptMesquiteMesh *pmm = dynamic_cast<PerceptMesquiteMesh *>(mesh);
      PerceptMesh *eMesh = pmm->getPerceptMesh();
      m_pmm= pmm;
      m_eMesh = eMesh;
      m_num_nodes = m_eMesh->get_number_nodes();

      print_comm_list(*eMesh->get_bulk_data(), false);

      stk::mesh::FieldBase *coord_field           = eMesh->get_coordinates_field();
      stk::mesh::FieldBase *coord_field_current   = coord_field;
      stk::mesh::FieldBase *coord_field_projected = eMesh->get_field("coordinates_N"); 
      stk::mesh::FieldBase *coord_field_original  = eMesh->get_field("coordinates_NM1");
      stk::mesh::FieldBase *coord_field_lagged    = eMesh->get_field("coordinates_lagged");

      m_coord_field_original  = coord_field_original;
      m_coord_field_projected = coord_field_projected;
      m_coord_field_lagged    = coord_field_lagged;
      m_coord_field_current   = coord_field_current;

      eMesh->copy_field(coord_field_lagged, coord_field_original);

      // untangle
      PMMSmootherMetricUntangle untangle_metric(eMesh);

      // shape-size-orient smooth
      PMMSmootherMetricShapeSizeOrient shape_metric(eMesh);

      // shape
      PMMSmootherMetricShapeB1 shape_b1_metric(eMesh);

      // scaled jacobian
      PMMSmootherMetricScaledJacobianElemental scaled_jac_metric(eMesh);

      // scaled jacobian - nodal
      PMMSmootherMetricScaledJacobianNodal scaled_jac_metric_nodal(eMesh);

      //double omegas[] = {0.0, 0.001, 0.01, 0.1, 0.2, 0.4, 0.6, 0.8, 1.0};
      //double omegas[] = {0.001, 1.0};
      //double omegas[] = { 0.001, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.2, 0.4, 0.45,0.46,0.47,0.48,0.49,0.5,0.52,0.54,0.56,0.59, 0.6, 0.8, 1.0};
      double omegas[] = { 1.0};
      //double omegas[] = {0.0, 0.001, 0.01, 0.1, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.2, 0.4, 0.6, 0.8, 1.0};
      int nomega = sizeof(omegas)/sizeof(omegas[0]);
      
      for (int outer = 0; outer < nomega; outer++)
        {
          double omega = (outer < nomega ? omegas[outer] : 1.0);
          m_omega = omega;
          m_omega_prev = omega;
          if (outer > 0) m_omega_prev = omegas[outer-1];

          // set current state and evaluate mesh validity (current = omega*project + (1-omega)*original)
          eMesh->nodal_field_axpbypgz(omega, coord_field_projected, (1.0-omega), coord_field_original, 0.0, coord_field_current);

          int num_invalid = PMMParallelShapeImprover::parallel_count_invalid_elements(m_eMesh);

          if (!get_parallel_rank()) 
            std::cout << "\ntmp srk PMMParallelReferenceMeshSmoother num_invalid current= " << num_invalid << " for outer_iter= " << outer 
                      << " omega= " << omega
                      << (num_invalid ? " WARNING: invalid elements exist before Mesquite smoothing" : " OK")
                      << std::endl;
          //if (num_invalid) return;
          m_num_invalid = num_invalid;
          m_untangled = (m_num_invalid == 0);

          int iter_all=0;

          int do_anim = 0; // = frequency of anim writes
          if (do_anim)
            {
              eMesh->save_as("anim_all."+toString(iter_all)+".e");
            }

          for (int stage = 0; stage < 2; stage++)
            {
              m_stage = stage;
              if (stage==0) 
                {
                  m_metric = &untangle_metric;
                  //m_metric = &scaled_jac_metric_nodal;
                }
              else 
                {
                  int num_invalid_1 = PMMParallelShapeImprover::parallel_count_invalid_elements(m_eMesh);
                  VERIFY_OP_ON(num_invalid_1, ==, 0, "Invalid elements exist for start of stage 2, aborting");

                  //m_metric = &shape_metric;
                  m_metric = &shape_b1_metric;
                  //m_metric = &scaled_jac_metric;

                }

              for (int iter = 0; iter < innerIter; ++iter, ++iter_all)
                {
                  m_iter = iter;
                  int num_invalid_0 = PMMParallelShapeImprover::parallel_count_invalid_elements(m_eMesh);
                  m_num_invalid = num_invalid_0;

                  //               if (!get_parallel_rank() && num_invalid_0) 
                  //                 std::cout << "\ntmp srk PMMParallelReferenceMeshSmoother num_invalid current= " << num_invalid_0 
                  //                           << (num_invalid ? " WARNING: invalid elements exist before Mesquite smoothing" : "OK")
                  //                           << std::endl;

                  m_global_metric = run_one_iteration(mesh, domain, err);

                  sync_fields(iter);
                  num_invalid_0 = PMMParallelShapeImprover::parallel_count_invalid_elements(m_eMesh);
                  m_num_invalid = num_invalid_0;
                  bool conv = check_convergence();
                  if (!get_parallel_rank())
                  {
                    std::cout << "P[" << get_parallel_rank() << "] " << "tmp srk iter= " << iter << " dmax= " << m_dmax << " m_dnew= " << m_dnew 
                              << " m_d0= " << m_d0 << " m_alpha= " << m_alpha << " m_grad_norm= " << m_grad_norm << " m_scaled_grad_norm = " << m_scaled_grad_norm
                              << " num_invalid= " << num_invalid_0 
                              << " m_global_metric= " << m_global_metric << " m_untangled= " << m_untangled
                              << std::endl;
                  }

                  if (do_anim)
                    {
                       eMesh->save_as("iter_"+toString(outer)+"_"+toString(stage)+"."+toString(iter+1)+".e");
                       if (iter_all % do_anim == 0) eMesh->save_as("anim_all."+toString(iter_all+1)+".e");
                    }

                  if (!m_untangled && m_num_invalid == 0)
                    {
                      m_untangled = true;
                    }
                  if (conv && m_untangled) break;
                  //if (iter == 5) break;
                  //if (iter == 0) exit(1);
                }

              eMesh->save_as("outer_iter_"+toString(outer)+"_"+toString(stage)+"_mesh.e");
            }

          eMesh->copy_field(coord_field_lagged, coord_field);

        }

      //if (!get_parallel_rank()) 

      MPI_Barrier( MPI_COMM_WORLD );

      std::cout << "\nP[" << get_parallel_rank() << "] tmp srk PMMParallelReferenceMeshSmoother: running shape improver... done \n" << std::endl;

      MSQ_ERRRTN(err);
    }
Beispiel #4
0
    /// modeled after code from Mesquite::IdealWeightMeanRatio::evaluate()
    bool JacobianUtil::operator()(double& m,  PerceptMesh& eMesh, stk::mesh::Entity& element, stk::mesh::FieldBase *coord_field,
                                  const CellTopologyData * topology_data )
    {
      MsqError err;
      static MsqMatrix<3,3> J;

      static Vector3D n;			// Surface normal for 2D objects

      // Prism and Hex element descriptions
      static const int locs_prism[6][4] = {{0, 1, 2, 3}, {1, 2, 0, 4},
                                           {2, 0, 1, 5}, {3, 5, 4, 0},
                                           {4, 3, 5, 1}, {5, 4, 3, 2}};
      static const int locs_hex[8][4] = {{0, 1, 3, 4}, {1, 2, 0, 5},
                                         {2, 3, 1, 6}, {3, 0, 2, 7},
                                         {4, 7, 5, 0}, {5, 4, 6, 1},
                                         {6, 5, 7, 2}, {7, 6, 4, 3}};
      int i=0;

      static const Vector3D d_con(1.0, 1.0, 1.0);

      bool metric_valid = false;
      if (!topology_data) topology_data = stk::percept::PerceptMesh::get_cell_topology(element);

      stk::mesh::PairIterRelation v_i = element.relations(eMesh.node_rank());
      m_num_nodes = v_i.size();

      //#define VERTEX_2D(vi) vector_2D( eMesh.field_data(coord_field, *vi.entity() ) )
      //#define VERTEX_3D(vi) vector_3D( eMesh.field_data(coord_field, *vi.entity() ) )

#define VERTEX_2D(vi) vector_2D( stk::mesh::field_data( *static_cast<const VectorFieldType *>(coord_field) , *vi.entity() ) )
#define VERTEX_3D(vi) vector_3D( stk::mesh::field_data( *static_cast<const VectorFieldType *>(coord_field) , *vi.entity() ) )


      switch(topology_data->key) 
        {
        case shards::Triangle<3>::key:
          n[0] = 0; n[1] = 0; n[2] = 1;
          mCoords[0] = VERTEX_2D(v_i[0]);
          mCoords[1] = VERTEX_2D(v_i[1]);
          mCoords[2] = VERTEX_2D(v_i[2]);
          metric_valid = jacobian_matrix_2D(m, J, mCoords, n, d_con);
          for (i = 0; i < 4; i++) { m_detJ[i] = m; m_J[i] = J; }
          break;
    
        case shards::Quadrilateral<4>::key:
          n[0] = 0; n[1] = 0; n[2] = 1;
          for (i = 0; i < 4; ++i) {
            mCoords[0] = VERTEX_2D(v_i[locs_hex[i][0]]);
            mCoords[1] = VERTEX_2D(v_i[locs_hex[i][1]]);
            mCoords[2] = VERTEX_2D(v_i[locs_hex[i][2]]);
            metric_valid = jacobian_matrix_2D(m_detJ[i], m_J[i], mCoords, n, d_con);
          }
          m = average_metrics(m_detJ, 4, err); MSQ_ERRZERO(err);
          break;

        case shards::Tetrahedron<4>::key:
          mCoords[0] = VERTEX_3D(v_i[0]);
          mCoords[1] = VERTEX_3D(v_i[1]);
          mCoords[2] = VERTEX_3D(v_i[2]);
          mCoords[3] = VERTEX_3D(v_i[3]);
          metric_valid = jacobian_matrix_3D(m, J, mCoords, n, d_con);
          for (i = 0; i < 4; i++) { m_detJ[i] = m; m_J[i] = J; }
          break;

        case shards::Pyramid<5>::key:
          for (i = 0; i < 4; ++i) {
            mCoords[0] = VERTEX_3D(v_i[ i     ]);
            mCoords[1] = VERTEX_3D(v_i[(i+1)%4]);
            mCoords[2] = VERTEX_3D(v_i[(i+3)%4]);
            mCoords[3] = VERTEX_3D(v_i[ 4     ]);
            metric_valid = jacobian_matrix_3D(m_detJ[i], m_J[i], mCoords, n, d_con);
          }
          // FIXME
          m_J[4] = (m_J[0]+m_J[1]+m_J[2]+m_J[3]);
          m_J[4] *= 0.25;
          m_detJ[4] = det(m_J[4]);
          m = average_metrics(m_detJ, 5, err); MSQ_ERRZERO(err);
          break;

        case shards::Wedge<6>::key:
          for (i = 0; i < 6; ++i) {
            mCoords[0] = VERTEX_3D(v_i[locs_prism[i][0]]);
            mCoords[1] = VERTEX_3D(v_i[locs_prism[i][1]]);
            mCoords[2] = VERTEX_3D(v_i[locs_prism[i][2]]);
            mCoords[3] = VERTEX_3D(v_i[locs_prism[i][3]]);
            metric_valid = jacobian_matrix_3D(m_detJ[i], m_J[i], mCoords, n, d_con);
          }
          m = average_metrics(m_detJ, 6, err); MSQ_ERRZERO(err);
          break;

        case shards::Hexahedron<8>::key:
          for (i = 0; i < 8; ++i) {
            mCoords[0] = VERTEX_3D(v_i[locs_hex[i][0]]);
            mCoords[1] = VERTEX_3D(v_i[locs_hex[i][1]]);
            mCoords[2] = VERTEX_3D(v_i[locs_hex[i][2]]);
            mCoords[3] = VERTEX_3D(v_i[locs_hex[i][3]]);
            metric_valid = jacobian_matrix_3D(m_detJ[i], m_J[i], mCoords, n, d_con);
          }
          m = average_metrics(m_detJ, 8, err); MSQ_ERRZERO(err);
          break;


          // unimplemented
        case shards::Node::key:
        case shards::Particle::key:
        case shards::Line<2>::key:
        case shards::Line<3>::key:
        case shards::ShellLine<2>::key:
        case shards::ShellLine<3>::key:
        case shards::Beam<2>::key:
        case shards::Beam<3>::key:
      
        case shards::Triangle<4>::key:
        case shards::Triangle<6>::key:
        case shards::ShellTriangle<3>::key:
        case shards::ShellTriangle<6>::key:
      
        case shards::Quadrilateral<8>::key:
        case shards::Quadrilateral<9>::key:
        case shards::ShellQuadrilateral<4>::key:
        case shards::ShellQuadrilateral<8>::key:
        case shards::ShellQuadrilateral<9>::key:
      
        case shards::Tetrahedron<8>::key:
        case shards::Tetrahedron<10>::key:
        case shards::Tetrahedron<11>::key:
      
        case shards::Hexahedron<20>::key:
        case shards::Hexahedron<27>::key:
      
        case shards::Pyramid<13>::key:
        case shards::Pyramid<14>::key:
      
        case shards::Wedge<15>::key:
        case shards::Wedge<18>::key:
      
        case shards::Pentagon<5>::key:
        case shards::Hexagon<6>::key:

        default:
          shards::CellTopology topology(topology_data);
          std::cout << "topology = " << topology.getName() << std::endl;
          throw std::runtime_error("unknown/unhandled topology in JacobianUtil");
          break;

        } // end switch over element type

      return metric_valid;
    }
Beispiel #5
0
 void Fixture::setup_read_mesh_create_coordsMag_field(PerceptMesh& meshUtil, bool create_field, const std::string file, bool print)
 {
   std::vector<PerceptMesh::FieldCreateOrder> create_field_vec;
   meshUtil.readModelCreateOptionalFields(create_field_vec, file, print);
 }
double PMMParallelReferenceMeshSmoother2::run_one_iteration( Mesh* mesh, MeshDomain *domain,
        MsqError& err )
{
    PerceptMesquiteMesh *pmm = dynamic_cast<PerceptMesquiteMesh *>(mesh);
    PerceptMesh *eMesh = pmm->getPerceptMesh();

    int spatialDim = m_eMesh->get_spatial_dim();

    stk_classic::mesh::FieldBase *cg_g_field    = eMesh->get_field("cg_g");
    stk_classic::mesh::FieldBase *cg_r_field    = eMesh->get_field("cg_r");
    stk_classic::mesh::FieldBase *cg_d_field    = eMesh->get_field("cg_d");
    stk_classic::mesh::FieldBase *cg_s_field    = eMesh->get_field("cg_s");

    stk_classic::mesh::Selector on_locally_owned_part =  ( eMesh->get_fem_meta_data()->locally_owned_part() );
    stk_classic::mesh::Selector on_globally_shared_part =  ( eMesh->get_fem_meta_data()->globally_shared_part() );
    bool total_valid=true;

    bool reduced_metric=false;

    m_dmax = 0.0;
    get_gradient(mesh, domain);

    {
        /// r = -g
        eMesh->nodal_field_axpby(-1.0, cg_g_field, 0.0, cg_r_field);
        /// s = r  (allows for preconditioning later s = M^-1 r)
        eMesh->copy_field(cg_s_field, cg_r_field);
        /// d = s
        eMesh->copy_field(cg_d_field, cg_s_field);
        /// dnew = r.d
        m_dnew = eMesh->nodal_field_dot(cg_r_field, cg_d_field);
    }

    double metric_orig = total_metric(mesh, 0.0, 1.0, total_valid);
    m_total_metric = metric_orig;
    if (check_convergence() || metric_orig == 0.0)
    {
        PRINT_1( "tmp srk already converged m_dnew= " << m_dnew << " gradNorm= " << gradNorm << " m_d0= " << m_d0 );
        //update_node_positions
        return total_metric(mesh,0.0,1.0, total_valid);
    }

    // node loop: local line search
    double alpha_min = 1.e+30;
    double alpha_max = 0.0;
    {
        const std::vector<stk_classic::mesh::Bucket*> & buckets = eMesh->get_bulk_data()->buckets( eMesh->node_rank() );
        for ( std::vector<stk_classic::mesh::Bucket*>::const_iterator k = buckets.begin() ; k != buckets.end() ; ++k )
        {
            // update local and globally shared
            //if (on_locally_owned_part(**k) || on_globally_shared_part(**k))
            if (on_locally_owned_part(**k))
            {
                stk_classic::mesh::Bucket & bucket = **k ;
                const unsigned num_nodes_in_bucket = bucket.size();

                for (unsigned i_node = 0; i_node < num_nodes_in_bucket; i_node++)
                {
                    stk_classic::mesh::Entity& node = bucket[i_node];
                    bool fixed = pmm->get_fixed_flag(&node);
                    bool isGhostNode = !(on_locally_owned_part(node) || on_globally_shared_part(node));
                    if (fixed || isGhostNode)
                    {
                        continue;
                    }

                    //double edge_length_ave = m_eMesh->edge_length_ave(element);
                    double edge_length_ave = nodal_edge_length_ave(node);

                    double *coord_current = PerceptMesh::field_data(m_coord_field_current, node);
                    double *cg_d = PerceptMesh::field_data(cg_d_field, node);
                    double *cg_g = PerceptMesh::field_data(cg_g_field, node);

                    double local_scale = 0.0;
                    for (int i=0; i < spatialDim; i++)
                    {
                        local_scale = std::max(local_scale, std::abs(cg_g[i])/edge_length_ave);
                    }

                    local_scale = (local_scale < 1.0) ? 1.0 : 1.0/local_scale;
                    //PRINT("tmp srk node= " << node.identifier() << " iter= " << m_iter << " local_scale= " << local_scale);

                    /// line search

                    //get_nodal_gradient(node, cg_g_local);
                    double norm_gradient2 = 0.0;
                    for (int i=0; i < spatialDim; i++)
                    {
                        norm_gradient2 += cg_g[i]*cg_g[i];
                    }

                    double alpha = local_scale;  // m_scale
                    if (std::sqrt(norm_gradient2) > edge_length_ave*1.e-8)
                    {
                        bool local_valid=true, local_valid_0=true, local_valid_1=true;
                        double metric_0 = nodal_metric(node, 0.0, coord_current, cg_d, local_valid_0);
                        double metric=0.0;
                        //double sigma=0.95;
                        double tau = 0.5;
                        double c0 = 1.e-4;

                        double armijo_offset_factor = c0*norm_gradient2;
                        bool converged = false;
                        while (!converged)
                        {
                            metric = nodal_metric(node, alpha, coord_current, cg_d, local_valid);

                            //converged = (metric > sigma*metric_0) && (alpha > 1.e-16);
                            double mfac = alpha*armijo_offset_factor;
                            converged = metric == 0.0 || (metric < metric_0 + mfac);
                            if (m_untangled) converged = converged && local_valid;
                            PRINT(  "tmp srk node= " << node.identifier() << " iter= " << m_iter
                                    << " alpha= " << alpha << " metric_0= " << metric_0 << " metric= " << metric << " diff= " << metric - (metric_0 + mfac)
                                    << " m_untangled = " << m_untangled << " norm_gradient2= " << norm_gradient2
                                    << " local_valid= " << local_valid );
                            if (!converged)
                                alpha *= tau;
                            if (alpha < std::max(1.e-6*m_scale, 1.e-16))
                            {
                                PRINT_1(  "tmp srk not conv node= " << node.identifier() << " iter= " << m_iter
                                          << " alpha= " << alpha << " metric_0= " << metric_0 << " metric= " << metric << " diff= " << metric - (metric_0 + mfac)
                                          << " m_untangled = " << m_untangled << " norm_gradient2= " << norm_gradient2
                                          << " local_valid= " << local_valid    );

                                break;
                            }
                        }
                        //if (metric > sigma*metric_0)
                        if (!converged)
                        {
                            metric_0 = nodal_metric(node, 0.0, coord_current, cg_d, local_valid_0);
                            double metric_1 = nodal_metric(node, 1.e-6, coord_current, cg_d, local_valid_1);
                            PRINT_1(  "tmp srk can't reduce metric, node= " << node.identifier() << " iter= " << m_iter
                                      << " alpha= " << alpha << " metric_0= " << metric_0 << " metric[1.e-6]= " << metric_1 << " diff[want neg]= " << metric_1 - metric_0
                                      << "\n m_untangled = " << m_untangled << " norm_gradient2= " << norm_gradient2
                                      << " local_valid= " << local_valid
                                      << " local_valid_0= " << local_valid_0
                                      << " local_valid_1= " << local_valid_1
                                      << " cg_d= " << cg_d[0] << " " << cg_d[1] << " edge_length_ave= " << edge_length_ave);
                            alpha = 0.0;
                        }
                        else
                        {
                            reduced_metric = true;
                            double a1 = alpha/2.;
                            double a2 = alpha;
                            double f0 = metric_0,
                                   f1 = nodal_metric(node, a1, coord_current, cg_d, total_valid),
                                   f2 = nodal_metric(node, a2, coord_current, cg_d, total_valid);
                            double den = 2.*(a2*(-f0 + f1) + a1*(f0 - f2));
                            double num = a2*a2*(f1-f0)+a1*a1*(f0-f2);
                            if (std::fabs(den) > 1.e-10)
                            {
                                double alpha_quadratic = num/den;
                                if (alpha_quadratic < 2.0*alpha)
                                {
                                    double fm = nodal_metric(node, alpha_quadratic, coord_current, cg_d, total_valid);
                                    //if (fm < f2 && (!m_untangled || total_valid))
                                    if (fm < f2)
                                    {
                                        alpha = alpha_quadratic;
                                        PRINT( "tmp srk alpha_quadratic= " << alpha_quadratic << " alpha= " << a2 );
                                    }
                                }
                            }
                        }
                    }
                    alpha_min = std::min(alpha_min, alpha);
                    alpha_max = std::max(alpha_max, alpha);
                    // scale cg_d by local alpha
                    for (int i=0; i < spatialDim; i++)
                    {
                        cg_d[i] *= alpha;
                    }
                } // node in bucket loop
            }
        } // bucket loop
    }

    /// check for valid mesh
    double alpha_global = 1.0;
    if (m_stage == 1)
    {
        bool converged = false;
        while (!converged)
        {
            int num_invalid=0;
            double metric_new = total_metric(mesh, alpha_global, 1.0, total_valid, &num_invalid);
            PRINT_1(  "tmp srk global alpha = " << alpha_global << " metric_new= " << metric_new << " total_valid= " << total_valid << " num_invalid= " << num_invalid);
            if (total_valid)
            {
                converged = true;
                break;
            }
            else
            {
                alpha_global *= 0.5;
            }
            if (alpha_global < 1.e-10)
            {
                break;
            }
        }
        //if (metric > sigma*metric_0)
        if (!converged)
        {
            bool local_valid_0=true, local_valid_1=true;
            double metric_0 = total_metric(mesh,0.0, 1.0, local_valid_0);
            double metric_1 = total_metric(mesh, 1.e-6, 1.0, local_valid_1);
            PRINT_1(  "tmp srk can't get valid mesh... "
                      " metric_0= " << metric_0 << " metric[1.e-6]= " << metric_1 << " diff[want neg]= " << metric_1 - metric_0
                      << "\n m_untangled = " << m_untangled
                      << " local_valid_0= " << local_valid_0
                      << " local_valid_1= " << local_valid_1 );
        }
    }

    /// x = x + alpha*d
    m_dmax=0.0;
    update_node_positions(mesh, alpha_global);
    double metric_new = total_metric(mesh,0.0, 1.0, total_valid);
    PRINT_1( "tmp srk iter= "<< m_iter << " dmax= " << m_dmax << " alpha_min= " << alpha_min << " alpha_max= " << alpha_max << " total_valid= " << total_valid);
    if (m_stage == 1 && !total_valid)
    {
        throw std::runtime_error("created bad mesh");
    }

    //if (!reduced_metric || metric_new >= metric_orig)
    if (!reduced_metric )
    {
        PRINT_1( "can't reduce metric, metric_new= " << metric_new << " metric_0 = " << metric_orig << " reduced_metric= " << reduced_metric);
        throw std::runtime_error("can't reduce metric");
    }

    return metric_new;
}
void compute_elem_mesh_size_ratio(
    PerceptMesh& eMesh,
    ScalarFieldType* elem_ratio_field,
    const double &global_error_tol)
{
    const int spatial_dim = eMesh.get_spatial_dim();
    double local_error_tol = global_error_tol;

    static bool first_run = true;

    stk::mesh::Part * activeElementsPart = eMesh.get_non_const_part("refine_active_elements_part");

    stk::mesh::Selector selector = first_run  ?
                                   eMesh.get_fem_meta_data()->locally_owned_part() :
                                   ( eMesh.get_fem_meta_data()->locally_owned_part() & (*activeElementsPart) );

    first_run = false;

    std::vector<unsigned> count ;
    stk::mesh::count_entities( selector, *eMesh.get_bulk_data(), count );

    const double num_elems = (double) count[eMesh.element_rank()];
    local_error_tol /= sqrt(num_elems);

    std::vector<stk::mesh::Bucket*> buckets;
    stk::mesh::get_buckets( selector, eMesh.get_bulk_data()->buckets( eMesh.element_rank() ), buckets );

    for ( vector<stk::mesh::Bucket*>::const_iterator k = buckets.begin() ; k != buckets.end() ; ++k ) {

        stk::mesh::Bucket & bucket = **k ;

        shards::CellTopology ct = stk::mesh::fem::get_cell_topology(bucket);
        const int Nnpe = ct.getNodeCount();

        std::vector<double> nodal_interp(Nnpe);
        std::vector<double> nodal_coords(Nnpe*spatial_dim);

        const unsigned num_elems_in_bucket = bucket.size();
        for (unsigned i = 0; i < num_elems_in_bucket; i++) {

            stk::mesh::Entity& element = bucket[i];

            // gather nodal coords and compute centroid
            std::vector<double> centroid(spatial_dim, 0.0);
            stk::mesh::PairIterRelation elem_nodes = element.relations(stk::mesh::fem::FEMMetaData::NODE_RANK);
            for (unsigned inode=0; inode < elem_nodes.size(); inode++) {
                stk::mesh::Entity *node = elem_nodes[inode].entity();
                double *coords = stk::mesh::field_data( *eMesh.get_coordinates_field() , *node);

                for (int d=0; d<spatial_dim; d++) {
                    centroid[d] += coords[d];
                    nodal_coords[inode*spatial_dim+d] = coords[d];
                }

                exact_nodal_solution(coords, &nodal_interp[inode], spatial_dim);
            }

            for (int d=0; d<spatial_dim; d++) {
                centroid[d] /= (double) Nnpe;
            }

            // calc interpolation error at midpoint
            double eval_centroid;
            exact_nodal_solution(&centroid[0], &eval_centroid, spatial_dim);

            double interp_centroid = 0.0;
            for (unsigned inode=0; inode < elem_nodes.size(); inode++) {
                interp_centroid += nodal_interp[inode];
            }
            interp_centroid /= (double) Nnpe;

            const double err_centroid = eval_centroid - interp_centroid;

            // HACK triangles for now
            const double area = triangle_area(nodal_coords);

            const double local_error = fabs(err_centroid) * area;

            double *ratio = stk::mesh::field_data( *elem_ratio_field , element);

            // calc elem ratio
            *ratio = sqrt(local_error / local_error_tol);
        }
    }
}