예제 #1
0
파일: MinusMD.cpp 프로젝트: nimgould/mantid
void MinusMD::doMinus(typename MDEventWorkspace<MDE, nd>::sptr ws) {
  typename MDEventWorkspace<MDE, nd>::sptr ws1 = ws;
  typename MDEventWorkspace<MDE, nd>::sptr ws2 =
      boost::dynamic_pointer_cast<MDEventWorkspace<MDE, nd>>(m_operand_event);
  if (!ws1 || !ws2)
    throw std::runtime_error("Incompatible workspace types passed to MinusMD.");

  MDBoxBase<MDE, nd> *box1 = ws1->getBox();
  MDBoxBase<MDE, nd> *box2 = ws2->getBox();

  Progress prog(this, 0.0, 0.4, box2->getBoxController()->getTotalNumMDBoxes());

  // How many events you started with
  size_t initial_numEvents = ws1->getNPoints();

  // Make a leaf-only iterator through all boxes with events in the RHS
  // workspace
  MDBoxIterator<MDE, nd> it2(box2, 1000, true);
  do {
    MDBox<MDE, nd> *box = dynamic_cast<MDBox<MDE, nd> *>(it2.getBox());
    if (box) {
      // Copy the events from WS2 and add them into WS1
      const std::vector<MDE> &events = box->getConstEvents();

      // Perform a copy while flipping the signal
      std::vector<MDE> eventsCopy;
      eventsCopy.reserve(events.size());
      for (auto it = events.begin(); it != events.end(); it++) {
        MDE eventCopy(*it);
        eventCopy.setSignal(-eventCopy.getSignal());
        eventsCopy.push_back(eventCopy);
      }
      // Add events, with bounds checking
      box1->addEvents(eventsCopy);
      box->releaseEvents();
    }
    prog.report("Substracting Events");
  } while (it2.next());

  this->progress(0.41, "Splitting Boxes");
  Progress *prog2 = new Progress(this, 0.4, 0.9, 100);
  ThreadScheduler *ts = new ThreadSchedulerFIFO();
  ThreadPool tp(ts, 0, prog2);
  ws1->splitAllIfNeeded(ts);
  prog2->resetNumSteps(ts->size(), 0.4, 0.6);
  tp.joinAll();

  this->progress(0.95, "Refreshing cache");
  ws1->refreshCache();

  // Set a marker that the file-back-end needs updating if the # of events
  // changed.
  if (ws1->getNPoints() != initial_numEvents)
    ws1->setFileNeedsUpdating(true);
}
예제 #2
0
void addDetectors(DataObjects::Peak &peak, MDBoxBase<MDE, nd> &box,
                  const boost::true_type &) {
  if (box.getNumChildren() > 0) {
    std::cerr << "Box has children\n";
    addDetectors(peak, box, boost::true_type());
  }
  MDBox<MDE, nd> *mdBox = dynamic_cast<MDBox<MDE, nd> *>(&box);
  if (!mdBox) {
    throw std::invalid_argument("FindPeaksMD::addDetectors - Unexpected Box "
                                "type, cannot retrieve events");
  }
  const auto &events = mdBox->getConstEvents();
  auto itend = events.end();
  for (auto it = events.begin(); it != itend; ++it) {
    peak.addContributingDetID(it->getDetectorID());
  }
}
예제 #3
0
파일: SliceMD.cpp 프로젝트: nimgould/mantid
void SliceMD::slice(typename MDEventWorkspace<MDE, nd>::sptr ws) {
  // Create the ouput workspace
  typename MDEventWorkspace<OMDE, ond>::sptr outWS(
      new MDEventWorkspace<OMDE, ond>());
  for (size_t od = 0; od < m_binDimensions.size(); od++) {
    outWS->addDimension(m_binDimensions[od]);
  }
  outWS->setCoordinateSystem(ws->getSpecialCoordinateSystem());
  outWS->initialize();
  // Copy settings from the original box controller
  BoxController_sptr bc = ws->getBoxController();

  // store wrute buffer size for the future
  // uint64_t writeBufSize =
  // bc->getFileIO()getDiskBuffer().getWriteBufferSize();
  // and disable write buffer (if any) for input MD Events for this algorithm
  // purposes;
  // bc->setCacheParameters(1,0);

  BoxController_sptr obc = outWS->getBoxController();
  // Use the "number of bins" as the "split into" parameter
  for (size_t od = 0; od < m_binDimensions.size(); od++)
    obc->setSplitInto(od, m_binDimensions[od]->getNBins());
  obc->setSplitThreshold(bc->getSplitThreshold());

  bool bTakeDepthFromInputWorkspace =
      getProperty("TakeMaxRecursionDepthFromInput");
  int tempDepth = getProperty("MaxRecursionDepth");
  size_t maxDepth =
      bTakeDepthFromInputWorkspace ? bc->getMaxDepth() : size_t(tempDepth);
  obc->setMaxDepth(maxDepth);

  // size_t outputSize = writeBufSize;
  // obc->setCacheParameters(sizeof(OMDE),outputSize);

  obc->resetNumBoxes();
  // Perform the first box splitting
  outWS->splitBox();
  size_t lastNumBoxes = obc->getTotalNumMDBoxes();

  // --- File back end ? ----------------
  std::string filename = getProperty("OutputFilename");
  if (!filename.empty()) {

    // First save to the NXS file
    g_log.notice() << "Running SaveMD to create file back-end" << std::endl;
    IAlgorithm_sptr alg = createChildAlgorithm("SaveMD");
    alg->setPropertyValue("Filename", filename);
    alg->setProperty("InputWorkspace", outWS);
    alg->setProperty("MakeFileBacked", true);
    alg->executeAsChildAlg();

    if (!obc->isFileBacked())
      throw std::runtime_error("SliceMD with file-backed output: Can not set "
                               "up file-backed output workspace ");

    auto IOptr = obc->getFileIO();
    size_t outBufSize = IOptr->getWriteBufferSize();
    // the buffer size for resulting workspace; reasonable size is at least 10
    // data chunk sizes (nice to verify)
    if (outBufSize < 10 * IOptr->getDataChunk()) {
      outBufSize = 10 * IOptr->getDataChunk();
      IOptr->setWriteBufferSize(outBufSize);
    }
  }

  // Function defining which events (in the input dimensions) to place in the
  // output
  MDImplicitFunction *function = this->getImplicitFunctionForChunk(NULL, NULL);

  std::vector<API::IMDNode *> boxes;
  // Leaf-only; no depth limit; with the implicit function passed to it.
  ws->getBox()->getBoxes(boxes, 1000, true, function);
  // Sort boxes by file position IF file backed. This reduces seeking time,
  // hopefully.
  bool fileBackedWS = bc->isFileBacked();
  if (fileBackedWS)
    API::IMDNode::sortObjByID(boxes);

  Progress *prog = new Progress(this, 0.0, 1.0, boxes.size());

  // The root of the output workspace
  MDBoxBase<OMDE, ond> *outRootBox = outWS->getBox();

  // if target workspace has events, we should count them as added
  uint64_t totalAdded = outWS->getNEvents();
  uint64_t numSinceSplit = 0;

  // Go through every box for this chunk.
  // PARALLEL_FOR_IF( !bc->isFileBacked() )
  for (int i = 0; i < int(boxes.size()); i++) {
    MDBox<MDE, nd> *box = dynamic_cast<MDBox<MDE, nd> *>(boxes[i]);
    // Perform the binning in this separate method.
    if (box) {
      // An array to hold the rotated/transformed coordinates
      coord_t outCenter[ond];

      const std::vector<MDE> &events = box->getConstEvents();

      typename std::vector<MDE>::const_iterator it = events.begin();
      typename std::vector<MDE>::const_iterator it_end = events.end();
      for (; it != it_end; it++) {
        // Cache the center of the event (again for speed)
        const coord_t *inCenter = it->getCenter();

        if (function->isPointContained(inCenter)) {
          // Now transform to the output dimensions
          m_transformFromOriginal->apply(inCenter, outCenter);

          // Create the event
          OMDE newEvent(it->getSignal(), it->getErrorSquared(), outCenter);
          // Copy extra data, if any
          copyEvent(*it, newEvent);
          // Add it to the workspace
          outRootBox->addEvent(newEvent);

          numSinceSplit++;
        }
      }
      box->releaseEvents();

      // Ask BC if one needs to split boxes
      if (obc->shouldSplitBoxes(totalAdded, numSinceSplit, lastNumBoxes))
      // if (numSinceSplit > 20000000 || (i == int(boxes.size()-1)))
      {
        // This splits up all the boxes according to split thresholds and sizes.
        Kernel::ThreadScheduler *ts = new ThreadSchedulerFIFO();
        ThreadPool tp(ts);
        outWS->splitAllIfNeeded(ts);
        tp.joinAll();
        // Accumulate stats
        totalAdded += numSinceSplit;
        numSinceSplit = 0;
        lastNumBoxes = obc->getTotalNumMDBoxes();
        // Progress reporting
        if (!fileBackedWS)
          prog->report(i);
      }
      if (fileBackedWS) {
        if (!(i % 10))
          prog->report(i);
      }
    } // is box

  } // for each box in the vector
  prog->report();

  outWS->splitAllIfNeeded(NULL);
  // Refresh all cache.
  outWS->refreshCache();

  g_log.notice() << totalAdded << " " << OMDE::getTypeName()
                 << "'s added to the output workspace." << std::endl;

  if (outWS->isFileBacked()) {
    // Update the file-back-end
    g_log.notice() << "Running SaveMD" << std::endl;
    IAlgorithm_sptr alg = createChildAlgorithm("SaveMD");
    alg->setProperty("UpdateFileBackEnd", true);
    alg->setProperty("InputWorkspace", outWS);
    alg->executeAsChildAlg();
  }
  // return the size of the input workspace write buffer to its initial value
  // bc->setCacheParameters(sizeof(MDE),writeBufSize);
  this->setProperty("OutputWorkspace",
                    boost::dynamic_pointer_cast<IMDEventWorkspace>(outWS));
  delete prog;
}
예제 #4
0
파일: BinMD.cpp 프로젝트: liyulun/mantid
void BinMD::binByIterating(typename MDEventWorkspace<MDE, nd>::sptr ws) {
  BoxController_sptr bc = ws->getBoxController();
  // store exisiting write buffer size for the future
  // uint64_t writeBufSize =bc->getDiskBuffer().getWriteBufferSize();
  // and disable write buffer (if any) for input MD Events for this algorithm
  // purposes;
  // bc->setCacheParameters(1,0);

  // Cache some data to speed up accessing them a bit
  indexMultiplier = new size_t[m_outD];
  for (size_t d = 0; d < m_outD; d++) {
    if (d > 0)
      indexMultiplier[d] = outWS->getIndexMultiplier()[d - 1];
    else
      indexMultiplier[d] = 1;
  }
  signals = outWS->getSignalArray();
  errors = outWS->getErrorSquaredArray();
  numEvents = outWS->getNumEventsArray();

  // Start with signal/error/numEvents at 0.0
  outWS->setTo(0.0, 0.0, 0.0);

  // The dimension (in the output workspace) along which we chunk for parallel
  // processing
  // TODO: Find the smartest dimension to chunk against
  size_t chunkDimension = 0;

  // How many bins (in that dimension) per chunk.
  // Try to split it so each core will get 2 tasks:
  int chunkNumBins = int(m_binDimensions[chunkDimension]->getNBins() /
                         (PARALLEL_GET_MAX_THREADS * 2));
  if (chunkNumBins < 1)
    chunkNumBins = 1;

  // Do we actually do it in parallel?
  bool doParallel = getProperty("Parallel");
  // Not if file-backed!
  if (bc->isFileBacked())
    doParallel = false;
  if (!doParallel)
    chunkNumBins = int(m_binDimensions[chunkDimension]->getNBins());

  // Total number of steps
  size_t progNumSteps = 0;
  if (prog)
    prog->setNotifyStep(0.1);
  if (prog)
    prog->resetNumSteps(100, 0.00, 1.0);

  // Run the chunks in parallel. There is no overlap in the output workspace so
  // it is thread safe to write to it..
  // cppcheck-suppress syntaxError
    PRAGMA_OMP( parallel for schedule(dynamic,1) if (doParallel) )
    for (int chunk = 0;
         chunk < int(m_binDimensions[chunkDimension]->getNBins());
         chunk += chunkNumBins) {
      PARALLEL_START_INTERUPT_REGION
      // Region of interest for this chunk.
      std::vector<size_t> chunkMin(m_outD);
      std::vector<size_t> chunkMax(m_outD);
      for (size_t bd = 0; bd < m_outD; bd++) {
        // Same limits in the other dimensions
        chunkMin[bd] = 0;
        chunkMax[bd] = m_binDimensions[bd]->getNBins();
      }
      // Parcel out a chunk in that single dimension dimension
      chunkMin[chunkDimension] = size_t(chunk);
      if (size_t(chunk + chunkNumBins) >
          m_binDimensions[chunkDimension]->getNBins())
        chunkMax[chunkDimension] = m_binDimensions[chunkDimension]->getNBins();
      else
        chunkMax[chunkDimension] = size_t(chunk + chunkNumBins);

      // Build an implicit function (it needs to be in the space of the
      // MDEventWorkspace)
      MDImplicitFunction *function =
          this->getImplicitFunctionForChunk(chunkMin.data(), chunkMax.data());

      // Use getBoxes() to get an array with a pointer to each box
      std::vector<API::IMDNode *> boxes;
      // Leaf-only; no depth limit; with the implicit function passed to it.
      ws->getBox()->getBoxes(boxes, 1000, true, function);

      // Sort boxes by file position IF file backed. This reduces seeking time,
      // hopefully.
      if (bc->isFileBacked())
        API::IMDNode::sortObjByID(boxes);

      // For progress reporting, the # of boxes
      if (prog) {
        PARALLEL_CRITICAL(BinMD_progress) {
          g_log.debug() << "Chunk " << chunk << ": found " << boxes.size()
                        << " boxes within the implicit function.\n";
          progNumSteps += boxes.size();
          prog->setNumSteps(progNumSteps);
        }
      }

      // Go through every box for this chunk.
      for (auto &boxe : boxes) {
        MDBox<MDE, nd> *box = dynamic_cast<MDBox<MDE, nd> *>(boxe);
        // Perform the binning in this separate method.
        if (box && !box->getIsMasked())
          this->binMDBox(box, chunkMin.data(), chunkMax.data());

        // Progress reporting
        if (prog)
          prog->report();
        // For early cancelling of the loop
        if (this->m_cancel)
          break;
      } // for each box in the vector
      PARALLEL_END_INTERUPT_REGION
    } // for each chunk in parallel
    PARALLEL_CHECK_INTERUPT_REGION

    // Now the implicit function
    if (implicitFunction) {
      if (prog)
        prog->report("Applying implicit function.");
      signal_t nan = std::numeric_limits<signal_t>::quiet_NaN();
      outWS->applyImplicitFunction(implicitFunction, nan, nan);
    }

    // return the size of the input workspace write buffer to its initial value
    // bc->setCacheParameters(sizeof(MDE),writeBufSize);
}
예제 #5
0
void PlusMD::doPlus(typename MDEventWorkspace<MDE, nd>::sptr ws) {
    typename MDEventWorkspace<MDE, nd>::sptr ws1 = ws;
    typename MDEventWorkspace<MDE, nd>::sptr ws2 =
        boost::dynamic_pointer_cast<MDEventWorkspace<MDE, nd>>(m_operand_event);
    if (!ws1 || !ws2)
        throw std::runtime_error("Incompatible workspace types passed to PlusMD.");

    MDBoxBase<MDE, nd> *box1 = ws1->getBox();
    MDBoxBase<MDE, nd> *box2 = ws2->getBox();

    Progress prog(this, 0.0, 0.4, box2->getBoxController()->getTotalNumMDBoxes());

    // How many events you started with
    size_t initial_numEvents = ws1->getNPoints();

    // Make a leaf-only iterator through all boxes with events in the RHS
    // workspace
    // TODO:    OMP
    MDBoxIterator<MDE, nd> it2(box2, 1000, true);
    do {
        MDBox<MDE, nd> *box = dynamic_cast<MDBox<MDE, nd> *>(it2.getBox());
        if (box) {
            // Copy the events from WS2 and add them into WS1
            const std::vector<MDE> &events = box->getConstEvents();
            // Add events, with bounds checking
            box1->addEvents(events);
            box->releaseEvents();
        }
        prog.report("Adding Events");
    } while (it2.next());

    this->progress(0.41, "Splitting Boxes");
    Progress *prog2 = new Progress(this, 0.4, 0.9, 100);
    ThreadScheduler *ts = new ThreadSchedulerFIFO();
    ThreadPool tp(ts, 0, prog2);
    ws1->splitAllIfNeeded(ts);
    prog2->resetNumSteps(ts->size(), 0.4, 0.6);
    tp.joinAll();

    //// Now we need to save all the data that was not saved before.
    // if (ws1->isFileBacked())
    //{
    //    // flusush disk kernel buffer and save all still in memory
    //    ws1->getBoxController()->getFileIO()->flushCache();
    //  // Flush the data writes to disk from nexus IO buffer
    //    ws1->getBoxController()->getFileIO()->flushData();
    //}
    // if(ws2->isFileBacked())
    //{
    //    // flusush disk kernel buffer and save all still in memory
    //    ws2->getBoxController()->getFileIO()->flushCache();
    //  // Flush the data writes to disk from nexus IO buffer
    //    ws2->getBoxController()->getFileIO()->flushData();

    //  //// Flush anything else in the to-write buffer
    //  //BoxController_sptr bc = ws1->getBoxController();

    //  //prog.resetNumSteps(bc->getTotalNumMDBoxes(), 0.6, 1.0);
    //  //MDBoxIterator<MDE,nd> it1(box1, 1000, true);
    //  //while (true)
    //  //{
    //  //  MDBox<MDE,nd> * box = dynamic_cast<MDBox<MDE,nd> *>(it1.getBox());
    //  //  if (box)
    //  //  {
    //  //    // Something was maybe added to this box
    //  //    if (box->getEventVectorSize() > 0)
    //  //    {
    //  //      // By getting the events, this will merge the newly added and the
    //  cached events.
    //  //      box->getEvents();
    //  //      // The MRU to-write cache will optimize writes by reducing seek
    //  times
    //  //      box->releaseEvents();
    //  //    }
    //  //  }
    //  //  prog.report("Saving");
    //  //  if (!it1.next()) break;
    //  //}
    //  //bc->getDiskBuffer().flushCache();

    //}

    this->progress(0.95, "Refreshing cache");
    ws1->refreshCache();

    // Set a marker that the file-back-end needs updating if the # of events
    // changed.
    if (ws1->getNPoints() != initial_numEvents)
        ws1->setFileNeedsUpdating(true);
}
예제 #6
0
/** Add MDEvents to MDEventWorkspace from data set in the experiment
 *  Run number is determined by the row of the file in the input table workspace
 * @brief ConvertCWSDExpToMomentum::addMDEvents
 * @param usevirtual :: flag to use virtual instrument
 */
void ConvertCWSDExpToMomentum::addMDEvents(bool usevirtual) {
  MatrixWorkspace_sptr spicews;

  // Check whether to add / or \ to m_dataDir
  std::string sep("");
  if (m_dataDir.size() > 0) {
// Determine system
#if _WIN64
    const bool isWindows = true;
#elif _WIN32
    const bool isWindows = true;
#else
    const bool isWindows = false;
#endif

    if (isWindows && *m_dataDir.rbegin() != '\\') {
      sep = "\\";
    } else if (!isWindows && *m_dataDir.rbegin() != '/')
      sep = "/";
  }

  // Init some variables
  size_t numrows = m_expDataTableWS->rowCount();
  if (numrows > 1 && !usevirtual) {
    g_log.warning("There are more than 1 experiment to import. "
                  "Make sure that all of them have the same instrument.");
  }
  size_t numFileNotLoaded(0);

  // Loop through all data files in the experiment
  for (size_t ir = 0; ir < numrows; ++ir) {
    std::string rawfilename =
        m_expDataTableWS->cell<std::string>(ir, m_iColFilename);
    detid_t start_detid = 0;
    if (usevirtual)
      start_detid = m_expDataTableWS->cell<detid_t>(ir, m_iColStartDetID);

    // Load data
    bool loaded;
    std::string errmsg;

    std::stringstream filess;
    if (m_isBaseName) {
      filess << m_dataDir << sep;
    }
    filess << rawfilename;
    std::string filename(filess.str());

    spicews = loadSpiceData(filename, loaded, errmsg);
    if (!loaded) {
      g_log.error(errmsg);
      ++numFileNotLoaded;
      continue;
    }

    // Convert from MatrixWorkspace to MDEvents and add events to
    int runid = static_cast<int>(ir) + 1;
    if (!usevirtual)
      start_detid = 0;
    convertSpiceMatrixToMomentumMDEvents(spicews, usevirtual, start_detid,
                                         runid);
  }

  // Set box extentes
  // typename std::vector<API::IMDNode *> boxes;
  std::vector<API::IMDNode *> boxes;

  // Set extents for all MDBoxes
  progress(0.90, "Set up MDBoxes' dimensions. ");
  m_outputWS->getBoxes(boxes, 1000, true);
  auto it1 = boxes.begin();
  auto it1_end = boxes.end();
  for (; it1 != it1_end; it1++) {
    auto box = *it1;
    for (size_t dim = 0; dim < 3; ++dim) {
      MDBox<MDEvent<3>, 3> *mdbox =
          dynamic_cast<DataObjects::MDBox<MDEvent<3>, 3> *>(box);
      if (!mdbox)
        throw std::runtime_error("Unable to cast to MDBox");
      mdbox->setExtents(dim, -10, 10);
      mdbox->calcVolume();
      mdbox->refreshCache(nullptr);
    }
  }

  return;
}
예제 #7
0
  void vtkSplatterPlotFactory::doCreate(typename MDEventWorkspace<MDE, nd>::sptr ws) const
  {
    bool VERBOSE = true;
    CPUTimer tim;
    // Acquire a scoped read-only lock to the workspace (prevent segfault
    // from algos modifying ws)
    ReadLock lock(*ws);

    // Find out how many events to plot, and the percentage of the largest
    // boxes to use.
    size_t totalPoints = ws->getNPoints();
    size_t numPoints = m_numPoints;

    if (numPoints > totalPoints)
    {
      numPoints = totalPoints;
    }

    double percent_to_use = m_percentToUse;
    // Fail safe limits on fraction of boxes to use
    if (percent_to_use <= 0)
    {
      percent_to_use = 5;
    }

    if (percent_to_use > 100)
    {
      percent_to_use = 100;
    }

    // First we get all the boxes, up to the given depth; with or wo the
    // slice function
    std::vector<API::IMDNode *> boxes;
    if (this->slice)
    {
      ws->getBox()->getBoxes(boxes, 1000, true, this->sliceImplicitFunction);
    }
    else
    {
      ws->getBox()->getBoxes(boxes, 1000, true);
    }

    if (VERBOSE)
    {
      std::cout << tim << " to retrieve the "<< boxes.size() << " boxes down."<< std::endl;
    }

    std::string new_name = ws->getName();
    if (new_name != m_wsName || m_buildSortedList)
    {
      m_wsName = new_name;
      m_buildSortedList = false;
      m_sortedBoxes.clear();
      // get list of boxes with signal > 0 and sort
      // the list in order of decreasing signal
      for (size_t i = 0; i < boxes.size(); i++)
      {
        MDBox<MDE,nd> * box = dynamic_cast<MDBox<MDE,nd> *>(boxes[i]);
        if (box)
        {
          size_t newPoints = box->getNPoints();
          if (newPoints > 0)
          {
            m_sortedBoxes.push_back(box);
          }
        }
      }

      if (VERBOSE)
      {
        std::cout << "START SORTING" << std::endl;
      }
      std::sort(m_sortedBoxes.begin(), m_sortedBoxes.end(),
                CompareNormalizedSignal);
      if (VERBOSE)
      {
        std::cout << "DONE SORTING" << std::endl;
      }
    }
    size_t num_boxes_to_use = static_cast<size_t>(percent_to_use * static_cast<double>(m_sortedBoxes.size()) / 100.0);
    if (num_boxes_to_use >= m_sortedBoxes.size())
    {
      num_boxes_to_use = m_sortedBoxes.size()-1;
    }

    // restrict the number of points to the
    // number of points in boxes being used
    size_t total_points_available = 0;
    for (size_t i = 0; i < num_boxes_to_use; i++)
    {
      size_t newPoints = m_sortedBoxes[i]->getNPoints();
      total_points_available += newPoints;
    }

    if (numPoints > total_points_available)
    {
      numPoints = total_points_available;
    }

    size_t points_per_box = 0;
    // calculate the average number of points to use per box
    if (num_boxes_to_use > 0)
    {
      points_per_box = numPoints / num_boxes_to_use;
    }

    if (points_per_box < 1)
    {
      points_per_box = 1;
    }

    if (VERBOSE)
    {
      std::cout << "numPoints                 = " << numPoints << std::endl;
      std::cout << "num boxes in all          = " << boxes.size() << std::endl;
      std::cout << "num boxes above zero      = " << m_sortedBoxes.size() << std::endl;
      std::cout << "num boxes to use          = " << num_boxes_to_use << std::endl;
      std::cout << "total_points_available    = " << total_points_available << std::endl;
      std::cout << "points needed per box     = " << points_per_box << std::endl;
    }

    // First save the events and signals that we actually use.
    // For each box, get up to the average number of points
    // we want from each box, limited by the number of points
    // in the box.  NOTE: since boxes have different numbers
    // of events, we will not get all the events requested.
    // Also, if we are using a smaller number of points, we
    // won't get points from some of the boxes with lower signal.

    std::vector<float>            saved_signals;
    std::vector<const coord_t*>   saved_centers;
    std::vector<size_t>           saved_n_points_in_cell;
    saved_signals.reserve(numPoints);
    saved_centers.reserve(numPoints);
    saved_n_points_in_cell.reserve(numPoints);
  
    size_t pointIndex = 0;
    size_t box_index  = 0;
    bool   done       = false;
    while (box_index < num_boxes_to_use && !done)
    {
      MDBox<MDE,nd> *box = dynamic_cast<MDBox<MDE,nd> *>(m_sortedBoxes[box_index]);
      box_index++;
      if (NULL == box)
      {
        continue;
      }
      float signal_normalized = static_cast<float>(box->getSignalNormalized());
      size_t newPoints = box->getNPoints();
      size_t num_from_this_box = points_per_box;
      if (num_from_this_box > newPoints)
      {
        num_from_this_box = newPoints;
      }
      const std::vector<MDE> & events = box->getConstEvents();
      size_t startPointIndex = pointIndex;
      size_t event_index = 0;
      while (event_index < num_from_this_box && !done)
      {
        const MDE & ev = events[event_index];
        event_index++;
        const coord_t * center = ev.getCenter();
        // Save location
        saved_centers.push_back(center);
        pointIndex++;
        if (pointIndex >= numPoints)
        {
          done = true;
        }
      }
      box->releaseEvents();
      // Save signal
      saved_signals.push_back(signal_normalized);
      // Save cell size
      saved_n_points_in_cell.push_back(pointIndex-startPointIndex);
    } 

    numPoints = saved_centers.size();
    size_t numCells = saved_signals.size();

    if (VERBOSE)
    {
      std::cout << "Recorded data for all points" << std::endl;
      std::cout << "numPoints = " << numPoints << std::endl;
      std::cout << "numCells  = " << numCells << std::endl;
    }

    // Create the point list, one position for each point actually used
    vtkPoints *points = vtkPoints::New();
    points->Allocate(numPoints);
    points->SetNumberOfPoints(numPoints);

    // The list of IDs of points used, one ID per point, since points
    // are not reused to form polygon facets, etc.
    vtkIdType *ids = new vtkIdType[numPoints];

    // Only one scalar for each cell, NOT one per point
    vtkFloatArray *signal = vtkFloatArray::New();
    signal->Allocate(numCells);
    signal->SetName(m_scalarName.c_str());

    // Create the data set.  Need space for each cell, not for each point
    vtkUnstructuredGrid *visualDataSet = vtkUnstructuredGrid::New();
    this->dataSet = visualDataSet;
    visualDataSet->Allocate(numCells);
    // Now copy the saved point, cell and signal info into vtk data structures
    pointIndex = 0;
    for (size_t cell_i = 0; cell_i < numCells; cell_i++)
    {
      size_t startPointIndex = pointIndex;
      for (size_t point_i = 0; point_i < saved_n_points_in_cell[cell_i]; point_i++)
      {
        points->SetPoint(pointIndex, saved_centers[pointIndex]);
        ids[pointIndex] = pointIndex;
        pointIndex++;
      }
      signal->InsertNextTuple1(saved_signals[cell_i]);
      visualDataSet->InsertNextCell(VTK_POLY_VERTEX, saved_n_points_in_cell[cell_i], ids+startPointIndex);
    }

    if (VERBOSE)
    {
      std::cout << tim << " to create " << pointIndex << " points." << std::endl;
    }

    // Shrink to fit
    //points->Squeeze();
    signal->Squeeze();
    visualDataSet->Squeeze();

    // Add points and scalars
    visualDataSet->SetPoints(points);
    visualDataSet->GetCellData()->SetScalars(signal);

    delete [] ids;
  }