<commit_before>/****************************************************************/
/*               DO NOT MODIFY THIS HEADER                      */
/* MOOSE - Multiphysics Object Oriented Simulation Environment  */
/*                                                              */
/*           (c) 2010 Battelle Energy Alliance, LLC             */
/*                   ALL RIGHTS RESERVED                        */
/*                                                              */
/*          Prepared by Battelle Energy Alliance, LLC           */
/*            Under Contract No. DE-AC07-05ID14517              */
/*            With the U. S. Department of Energy               */
/*                                                              */
/*            See COPYRIGHT for full restrictions               */
/****************************************************************/

#include "NodalFloodCount.h"
#include "MooseMesh.h"
#include "MooseVariable.h"
#include "SubProblem.h"

#include "NonlinearSystem.h"
#include "FEProblem.h"


//libMesh includes
#include "dof_map.h"
#include "mesh_tools.h"
#include "periodic_boundaries.h"
#include "point_locator_base.h"

#include <algorithm>
#include <limits>

template<>
InputParameters validParams<NodalFloodCount>()
{
  InputParameters params = validParams<ElementPostprocessor>();
  params.addParam<Real>("threshold", 0.5, "The threshold value of the bubble boundary");
  params.addParam<std::string>("elem_avg_value", "If supplied, will be used to find the scaled threshold of the bubble edges");
  return params;
}

NodalFloodCount::NodalFloodCount(const std::string & name, InputParameters parameters) :
    ElementPostprocessor(name, parameters),
    _threshold(getParam<Real>("threshold")),
    _mesh(_subproblem.mesh()),
    _var_number(_var.number()),
    _region_count(0),
    _pbs(NULL),
    _element_average_value(parameters.isParamValid("elem_avg_value") ? _real_zero : _real_zero)
{}

void
NodalFloodCount::initialize()
{
  // Get a pointer to the PeriodicBoundaries buried in libMesh
  // TODO: Can we do this in the constructor (i.e. are all objects necessary for this call in existance during ctor?)
  _pbs = dynamic_cast<FEProblem *>(&_subproblem)->getNonlinearSystem().dofMap().get_periodic_boundaries();

  // Clear the bubble marking map
  _bubble_map.clear();

  // Reset the ownership structure
  _region_to_var_idx.clear();

  // Reset the region counter
  _region_count = 0;

  // Build a new node to element map
  _nodes_to_elem_map.clear();
  MeshTools::build_nodes_to_elem_map(_mesh._mesh, _nodes_to_elem_map);

  // TODO: We might only need to build this once if adaptivity is turned off
  _mesh.buildPeriodicNodeMap(_periodic_node_map, _var_number, _pbs);
}

void
NodalFloodCount::execute()
{
  unsigned int n_nodes = _current_elem->n_vertices();
  for (unsigned int i=0; i < n_nodes; ++i)
  {
    const Node *node = _current_elem->get_node(i);

    flood(node, 0, -1);
  }
}

void
NodalFloodCount::finalize()
{
  // Exchange data in parallel
  pack(_packed_data);
  Parallel::allgather(_packed_data, false);
  unpack(_packed_data);

  mergeSets();
}

Real
NodalFloodCount::getValue()
{
  return _bubble_sets.size();
}

void
NodalFloodCount::threadJoin(const UserObject &y)
{
   const NodalFloodCount & pps = dynamic_cast<const NodalFloodCount &>(y);

   // Pack up the data on both of the threads
   pack(_packed_data);

   std::vector<unsigned int> pps_packed_data;
   pps.pack(pps_packed_data);

   // Append the packed data structures together
   std::copy(pps_packed_data.begin(), pps_packed_data.end(), std::back_inserter(_packed_data));
}


void
NodalFloodCount::pack(std::vector<unsigned int> & packed_data, bool merge_periodic_info) const
{
  packed_data.clear();

  std::vector<std::set<unsigned int> > data(_region_count+1);
  unsigned int n_periodic_nodes = 0;

  {
    std::map<unsigned int, int>::const_iterator end = _bubble_map.end();
    // Reorganize the data by values
    for (std::map<unsigned int, int>::const_iterator it = _bubble_map.begin(); it != end; ++it)
      data[(it->second)].insert(it->first);

    // Append our periodic neighbor nodes to the data structure before packing
    if (merge_periodic_info)
      n_periodic_nodes = appendPeriodicNeighborNodes(data);

    mooseAssert(_region_count+1 == data.size(), "Error in packing data");
  }

  {
    /**
     * The size of the packed data structure should be the sum of all of the following:
     * total number of marked nodes
     * the owning variable index for the current bubble
     * inserted periodic neighbor information
     * the number of unique bubbles.
     *
     * This first resize will be an overesitmate because we aren't going to store nodes
     * marked with zeros (outside of bubbles).
     *
     * We will pack the data into a series of groups representing each unique bubble
     * the nodes for each group will be proceeded by the number of nodes in that group
     * [ <i_nodes> <var_idx> <n_0> <n_1> ... <n_i> <j_nodes> <var_idx> <n_0> <n_1> ... <n_j> ]
     */
    packed_data.resize(_bubble_map.size() + n_periodic_nodes + _region_count*2 /* <- owning idx + number of unique bubbles */);

    // Now pack it up
    unsigned int current_idx = 0;

    // Note: The zeroth "region" is everything outside of a bubble - we don't want to put
    // that into our packed data structure so start at 1 here!
    for (unsigned int i=1 /* Yes - start at 1 */; i<=_region_count; ++i)
    {
      packed_data[current_idx++] = data[i].size();                      // The number of nodes in the current bubble
      mooseAssert(i-1 < _region_to_var_idx.size(), "Index out of bounds in NodalFloodCounter");
      packed_data[current_idx++] = _region_to_var_idx[i-1];             // The variable owning this bubble

      std::set<unsigned int>::iterator end = data[i].end();
      for (std::set<unsigned int>::iterator it = data[i].begin(); it != end; ++it)
        packed_data[current_idx++] = *it;                               // The individual node ids
    }

    // Resize the structure to it's actual final size
    packed_data.resize(current_idx);
  }
}

void
NodalFloodCount::unpack(const std::vector<unsigned int> & packed_data)
{
  bool next_set = true;
  unsigned int curr_set_length=0;
  std::set<unsigned int> curr_set;
  unsigned int curr_region = std::numeric_limits<unsigned int>::max();

  _bubble_sets.clear();
  _region_to_var_idx.clear();
  for (unsigned int i=0; i<packed_data.size(); ++i)
  {
    if (next_set)
    {
      if (i > 0)
      {
        _bubble_sets.push_back(BubbleData(curr_set, curr_region));
        _region_to_var_idx.push_back(curr_region);
        curr_set.clear();
      }

      // Get the length of the next set
      curr_set_length = packed_data[i];
      // Also get the owning variable idx.
      // Note: We are intentionally advancing "i" here too!
      curr_region = packed_data[++i];
    }
    else
    {
      // unpack each bubble
      curr_set.insert(packed_data[i]);
      --curr_set_length;
    }

    next_set = !(curr_set_length);
  }
  _bubble_sets.push_back(BubbleData(curr_set, curr_region));
  _region_to_var_idx.push_back(curr_region);

  mooseAssert(curr_set_length == 0, "Error in unpacking data");
}

void
NodalFloodCount::mergeSets()
{
  std::set<unsigned int> set_union;
  bool set_merged;

  /**
   * This loop will continue as long as sets of nodes are merged
   * creating "new" sets.  There may be other ways to optimize this loop
   * but it should not be a bottleneck in practice.
   */
  do
  {
    set_merged = false;
    std::list<BubbleData>::iterator end = _bubble_sets.end();
    for (std::list<BubbleData>::iterator it1 = _bubble_sets.begin(); it1 != end; ++it1)
    {
      std::list<BubbleData>::iterator it2 = it1;
      ++it2; // Don't compare this set with itself - advance the iterator to the next set
      while (it2 != end)
      {
        set_union.clear();
        std::set_union(it1->_nodes.begin(), it1->_nodes.end(), it2->_nodes.begin(), it2->_nodes.end(),
                       std::inserter(set_union, set_union.end()));

        if (set_union.size() < it1->_nodes.size() + it2->_nodes.size())
        {
          // Merge these two sets and remove the duplicate set
          it1->_nodes = set_union;
          _bubble_sets.erase(it2++);
          set_merged = true;
        }
        else
          ++it2;
      }
    }
  } while (set_merged);

  /**
   * Finally update the original bubble map with field data from the merged sets
   */
  _region_to_var_idx.resize(_bubble_sets.size());
  unsigned int counter = 1;
  for (std::list<BubbleData>::iterator it1 = _bubble_sets.begin(); it1 != _bubble_sets.end(); ++it1)
  {
    for (std::set<unsigned int>::iterator it2 = it1->_nodes.begin(); it2 != it1->_nodes.end(); ++it2)
      _bubble_map[*it2] = counter;

    _region_to_var_idx[counter-1] = it1->_var_idx;

    ++counter;
  }

  _region_count = counter-1;
}

void
NodalFloodCount::flood(const Node *node, unsigned int region, int current_idx)
{
  if (node == NULL)
    return;

  unsigned int node_id = node->id();

  // Has this node already been marked? - if so move along
  if (_bubble_map.find(node_id) != _bubble_map.end())
    return;

  /**
   * This node hasn't been marked - check to see if it in a bubble.
   * If current_idx is set (>= zero) then we are in the process of marking a bubble.
   */
  if (current_idx >= 0)
  {
    if (_vars[current_idx]->getNodalValue(*node) < _threshold)
    {
      // No - mark and return
      _bubble_map[node_id] = 0;
      return;
    }
  }
  else  // If current_idx is not set (< zero), then we can look for the start of a new bubble
  {
    for (unsigned int i=0; i<_vars.size(); ++i)
      if (_vars[i]->getNodalValue(*node) >= _threshold)
      {
        current_idx = i;
        break;
      }
    // Did we still fail to find any new bubbles?
    if (current_idx < 0)
    {
      _bubble_map[node_id] = 0;
      return;
    }
  }

  std::vector< const Node * > neighbors;
  MeshTools::find_nodal_neighbors(_mesh._mesh, *node, _nodes_to_elem_map, neighbors);
  // Important!  If this node doesn't have any neighbors (i.e. floating node in the center
  // of an element) we need to just unmark it for now since it can't be easiliy flooded<commit_msg>More test cases and features!!! <- only three :)<commit_after>/****************************************************************/
/*               DO NOT MODIFY THIS HEADER                      */
/* MOOSE - Multiphysics Object Oriented Simulation Environment  */
/*                                                              */
/*           (c) 2010 Battelle Energy Alliance, LLC             */
/*                   ALL RIGHTS RESERVED                        */
/*                                                              */
/*          Prepared by Battelle Energy Alliance, LLC           */
/*            Under Contract No. DE-AC07-05ID14517              */
/*            With the U. S. Department of Energy               */
/*                                                              */
/*            See COPYRIGHT for full restrictions               */
/****************************************************************/

#include "NodalFloodCount.h"
#include "MooseMesh.h"
#include "MooseVariable.h"
#include "SubProblem.h"

#include "NonlinearSystem.h"
#include "FEProblem.h"


//libMesh includes
#include "dof_map.h"
#include "mesh_tools.h"
#include "periodic_boundaries.h"
#include "point_locator_base.h"

#include <algorithm>
#include <limits>

template<>
InputParameters validParams<NodalFloodCount>()
{
  InputParameters params = validParams<ElementPostprocessor>();
  params.addParam<Real>("threshold", 0.5, "The threshold value of the bubble boundary");
  params.addParam<std::string>("elem_avg_value", "If supplied, will be used to find the scaled threshold of the bubble edges");
  return params;
}

NodalFloodCount::NodalFloodCount(const std::string & name, InputParameters parameters) :
    ElementPostprocessor(name, parameters),
    _threshold(getParam<Real>("threshold")),
    _mesh(_subproblem.mesh()),
    _var_number(_var.number()),
    _region_count(0),
    _pbs(NULL),
    _element_average_value(parameters.isParamValid("elem_avg_value") ? getPostprocessorValue("elem_avg_value") : _real_zero)
{}

void
NodalFloodCount::initialize()
{
  // Get a pointer to the PeriodicBoundaries buried in libMesh
  // TODO: Can we do this in the constructor (i.e. are all objects necessary for this call in existance during ctor?)
  _pbs = dynamic_cast<FEProblem *>(&_subproblem)->getNonlinearSystem().dofMap().get_periodic_boundaries();

  // Clear the bubble marking map
  _bubble_map.clear();

  // Reset the ownership structure
  _region_to_var_idx.clear();

  // Reset the region counter
  _region_count = 0;

  // Build a new node to element map
  _nodes_to_elem_map.clear();
  MeshTools::build_nodes_to_elem_map(_mesh._mesh, _nodes_to_elem_map);

  // TODO: We might only need to build this once if adaptivity is turned off
  _mesh.buildPeriodicNodeMap(_periodic_node_map, _var_number, _pbs);
}

void
NodalFloodCount::execute()
{
  unsigned int n_nodes = _current_elem->n_vertices();
  for (unsigned int i=0; i < n_nodes; ++i)
  {
    const Node *node = _current_elem->get_node(i);

    flood(node, 0, -1);
  }
}

void
NodalFloodCount::finalize()
{
  // Exchange data in parallel
  pack(_packed_data);
  Parallel::allgather(_packed_data, false);
  unpack(_packed_data);

  mergeSets();
}

Real
NodalFloodCount::getValue()
{
  return _bubble_sets.size();
}

void
NodalFloodCount::threadJoin(const UserObject &y)
{
   const NodalFloodCount & pps = dynamic_cast<const NodalFloodCount &>(y);

   // Pack up the data on both of the threads
   pack(_packed_data);

   std::vector<unsigned int> pps_packed_data;
   pps.pack(pps_packed_data);

   // Append the packed data structures together
   std::copy(pps_packed_data.begin(), pps_packed_data.end(), std::back_inserter(_packed_data));
}


void
NodalFloodCount::pack(std::vector<unsigned int> & packed_data, bool merge_periodic_info) const
{
  packed_data.clear();

  std::vector<std::set<unsigned int> > data(_region_count+1);
  unsigned int n_periodic_nodes = 0;

  {
    std::map<unsigned int, int>::const_iterator end = _bubble_map.end();
    // Reorganize the data by values
    for (std::map<unsigned int, int>::const_iterator it = _bubble_map.begin(); it != end; ++it)
      data[(it->second)].insert(it->first);

    // Append our periodic neighbor nodes to the data structure before packing
    if (merge_periodic_info)
      n_periodic_nodes = appendPeriodicNeighborNodes(data);

    mooseAssert(_region_count+1 == data.size(), "Error in packing data");
  }

  {
    /**
     * The size of the packed data structure should be the sum of all of the following:
     * total number of marked nodes
     * the owning variable index for the current bubble
     * inserted periodic neighbor information
     * the number of unique bubbles.
     *
     * This first resize will be an overesitmate because we aren't going to store nodes
     * marked with zeros (outside of bubbles).
     *
     * We will pack the data into a series of groups representing each unique bubble
     * the nodes for each group will be proceeded by the number of nodes in that group
     * [ <i_nodes> <var_idx> <n_0> <n_1> ... <n_i> <j_nodes> <var_idx> <n_0> <n_1> ... <n_j> ]
     */
    packed_data.resize(_bubble_map.size() + n_periodic_nodes + _region_count*2 /* <- owning idx + number of unique bubbles */);

    // Now pack it up
    unsigned int current_idx = 0;

    // Note: The zeroth "region" is everything outside of a bubble - we don't want to put
    // that into our packed data structure so start at 1 here!
    for (unsigned int i=1 /* Yes - start at 1 */; i<=_region_count; ++i)
    {
      packed_data[current_idx++] = data[i].size();                      // The number of nodes in the current bubble
      mooseAssert(i-1 < _region_to_var_idx.size(), "Index out of bounds in NodalFloodCounter");
      packed_data[current_idx++] = _region_to_var_idx[i-1];             // The variable owning this bubble

      std::set<unsigned int>::iterator end = data[i].end();
      for (std::set<unsigned int>::iterator it = data[i].begin(); it != end; ++it)
        packed_data[current_idx++] = *it;                               // The individual node ids
    }

    // Resize the structure to it's actual final size
    packed_data.resize(current_idx);
  }
}

void
NodalFloodCount::unpack(const std::vector<unsigned int> & packed_data)
{
  bool next_set = true;
  unsigned int curr_set_length=0;
  std::set<unsigned int> curr_set;
  unsigned int curr_region = std::numeric_limits<unsigned int>::max();

  _bubble_sets.clear();
  _region_to_var_idx.clear();
  for (unsigned int i=0; i<packed_data.size(); ++i)
  {
    if (next_set)
    {
      if (i > 0)
      {
        _bubble_sets.push_back(BubbleData(curr_set, curr_region));
        _region_to_var_idx.push_back(curr_region);
        curr_set.clear();
      }

      // Get the length of the next set
      curr_set_length = packed_data[i];
      // Also get the owning variable idx.
      // Note: We are intentionally advancing "i" here too!
      curr_region = packed_data[++i];
    }
    else
    {
      // unpack each bubble
      curr_set.insert(packed_data[i]);
      --curr_set_length;
    }

    next_set = !(curr_set_length);
  }
  _bubble_sets.push_back(BubbleData(curr_set, curr_region));
  _region_to_var_idx.push_back(curr_region);

  mooseAssert(curr_set_length == 0, "Error in unpacking data");
}

void
NodalFloodCount::mergeSets()
{
  std::set<unsigned int> set_union;
  bool set_merged;

  /**
   * This loop will continue as long as sets of nodes are merged
   * creating "new" sets.  There may be other ways to optimize this loop
   * but it should not be a bottleneck in practice.
   */
  do
  {
    set_merged = false;
    std::list<BubbleData>::iterator end = _bubble_sets.end();
    for (std::list<BubbleData>::iterator it1 = _bubble_sets.begin(); it1 != end; ++it1)
    {
      std::list<BubbleData>::iterator it2 = it1;
      ++it2; // Don't compare this set with itself - advance the iterator to the next set
      while (it2 != end)
      {
        set_union.clear();
        std::set_union(it1->_nodes.begin(), it1->_nodes.end(), it2->_nodes.begin(), it2->_nodes.end(),
                       std::inserter(set_union, set_union.end()));

        if (set_union.size() < it1->_nodes.size() + it2->_nodes.size())
        {
          // Merge these two sets and remove the duplicate set
          it1->_nodes = set_union;
          _bubble_sets.erase(it2++);
          set_merged = true;
        }
        else
          ++it2;
      }
    }
  } while (set_merged);

  /**
   * Finally update the original bubble map with field data from the merged sets
   */
  _region_to_var_idx.resize(_bubble_sets.size());
  unsigned int counter = 1;
  for (std::list<BubbleData>::iterator it1 = _bubble_sets.begin(); it1 != _bubble_sets.end(); ++it1)
  {
    for (std::set<unsigned int>::iterator it2 = it1->_nodes.begin(); it2 != it1->_nodes.end(); ++it2)
      _bubble_map[*it2] = counter;

    _region_to_var_idx[counter-1] = it1->_var_idx;

    ++counter;
  }

  _region_count = counter-1;
}

void
NodalFloodCount::flood(const Node *node, unsigned int region, int current_idx)
{
  if (node == NULL)
    return;

  unsigned int node_id = node->id();

  // Has this node already been marked? - if so move along
  if (_bubble_map.find(node_id) != _bubble_map.end())
    return;

  /**
   * This node hasn't been marked - check to see if it in a bubble.
   * If current_idx is set (>= zero) then we are in the process of marking a bubble.
   */
  if (current_idx >= 0)
  {
    if (_vars[current_idx]->getNodalValue(*node) < _element_average_value + _threshold)
    {
      // No - mark and return
      _bubble_map[node_id] = 0;
      return;
    }
  }
  else  // If current_idx is not set (< zero), then we can look for the start of a new bubble
  {
    for (unsigned int i=0; i<_vars.size(); ++i)
      if (_vars[i]->getNodalValue(*node) >= _element_average_value + _threshold)
      {
        current_idx = i;
        break;
      }
    // Did we still fail to find any new bubbles?
    if (current_idx < 0)
    {
      _bubble_map[node_id] = 0;
      return;
    }
  }

  std::vector< const Node * > neighbors;
  MeshTools::find_nodal_neighbors(_mesh._mesh, *node, _nodes_to_elem_map, neighbors);
  // Important!  If this node doesn't have any neighbors (i.e. floating node in the center
  // of an element) we need to just unmark it for now since it can't be easiliy flooded
{'fraction_non_alphanumeric': 0.12840448135986093, 'fraction_numerical': 0.006953834266949971, 'mean_word_length': 2.875187125748503, 'pattern_counts': {'":': 0, '<': 100, '<?xml version=': 0, '>': 123, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 14, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7555159', 'n_tokens_mistral': 6708, 'n_tokens_neox': 6404, 'n_words': 1802}