/** * Creates the output workspace for this algorithm * @param inputWorkspace A parent workspace to initialize from. * @return A pointer to the output workspace. */ API::MatrixWorkspace_sptr Transpose::createOutputWorkspace( API::MatrixWorkspace_const_sptr inputWorkspace) { Mantid::API::Axis *yAxis = getVerticalAxis(inputWorkspace); const size_t oldNhist = inputWorkspace->getNumberHistograms(); const auto &inX = inputWorkspace->x(0); const size_t oldYlength = inputWorkspace->blocksize(); const size_t oldVerticalAxislength = yAxis->length(); // The input Y axis may be binned so the new X data should be too size_t newNhist(oldYlength), newXsize(oldVerticalAxislength), newYsize(oldNhist); MatrixWorkspace_sptr outputWorkspace = inputWorkspace->cloneEmpty(); outputWorkspace->initialize(newNhist, newXsize, newYsize); outputWorkspace->setTitle(inputWorkspace->getTitle()); outputWorkspace->setComment(inputWorkspace->getComment()); outputWorkspace->copyExperimentInfoFrom(inputWorkspace.get()); outputWorkspace->setYUnit(inputWorkspace->YUnit()); outputWorkspace->setYUnitLabel(inputWorkspace->YUnitLabel()); outputWorkspace->setDistribution(inputWorkspace->isDistribution()); // Create a new numeric axis for Y the same length as the old X array // Values come from input X API::NumericAxis *newYAxis(nullptr); if (inputWorkspace->isHistogramData()) { newYAxis = new API::BinEdgeAxis(inX.rawData()); } else { newYAxis = new API::NumericAxis(inX.rawData()); } newYAxis->unit() = inputWorkspace->getAxis(0)->unit(); outputWorkspace->getAxis(0)->unit() = inputWorkspace->getAxis(1)->unit(); outputWorkspace->replaceAxis(1, newYAxis); setProperty("OutputWorkspace", outputWorkspace); return outputWorkspace; }
void GatherWorkspaces::exec() { // Every process in an MPI job must hit this next line or everything hangs! mpi::communicator world; // The communicator containing all processes inputWorkspace = getProperty("InputWorkspace"); // Create a new communicator that includes only those processes that have an // input workspace const int haveWorkspace(inputWorkspace ? 1 : 0); included = world.split(haveWorkspace); // If the present process doesn't have an input workspace then its work is // done if (!haveWorkspace) { g_log.information("No input workspace on this process, so nothing to do."); return; } // Get the number of bins in each workspace and check they're all the same numBins = inputWorkspace->blocksize(); std::vector<std::size_t> all_numBins; all_gather(included, numBins, all_numBins); if (std::count(all_numBins.begin(), all_numBins.end(), numBins) != (int)all_numBins.size()) { // All the processes will error out if all the workspaces don't have the // same number of bins throw Exception::MisMatch<std::size_t>( numBins, 0, "All input workspaces must have the same number of bins"); } // Also check that all workspaces are either histogram or not // N.B. boost mpi doesn't seem to like me using booleans in the all_gather hist = inputWorkspace->isHistogramData(); std::vector<int> all_hist; all_gather(included, hist, all_hist); if (std::count(all_hist.begin(), all_hist.end(), hist) != (int)all_hist.size()) { // All the processes will error out if we don't have either all histogram or // all point-data workspaces throw Exception::MisMatch<int>( hist, 0, "The input workspaces must be all histogram or all point data"); } // How do we accumulate the data? std::string accum = this->getPropertyValue("AccumulationMethod"); // Get the total number of spectra in the combined inputs totalSpec = inputWorkspace->getNumberHistograms(); sumSpec = totalSpec; if (accum == "Append") { reduce(included, totalSpec, sumSpec, std::plus<std::size_t>(), 0); } else if (accum == "Add") { // barrier only helps when memory is too low for communication // included.barrier(); } eventW = boost::dynamic_pointer_cast<const EventWorkspace>(inputWorkspace); if (eventW != NULL) { if (getProperty("PreserveEvents")) { // Input workspace is an event workspace. Use the other exec method this->execEvent(); return; } } // The root process needs to create a workspace of the appropriate size MatrixWorkspace_sptr outputWorkspace; if (included.rank() == 0) { g_log.debug() << "Total number of spectra is " << sumSpec << "\n"; // Create the workspace for the output outputWorkspace = WorkspaceFactory::Instance().create( inputWorkspace, sumSpec, numBins + hist, numBins); setProperty("OutputWorkspace", outputWorkspace); ExperimentInfo_sptr inWS = inputWorkspace; outputWorkspace->copyExperimentInfoFrom(inWS.get()); } for (size_t wi = 0; wi < totalSpec; wi++) { if (included.rank() == 0) { const ISpectrum *inSpec = inputWorkspace->getSpectrum(wi); if (accum == "Add") { outputWorkspace->dataX(wi) = inputWorkspace->readX(wi); reduce(included, inputWorkspace->readY(wi), outputWorkspace->dataY(wi), vplus(), 0); reduce(included, inputWorkspace->readE(wi), outputWorkspace->dataE(wi), eplus(), 0); } else if (accum == "Append") { // Copy over data from own input workspace outputWorkspace->dataX(wi) = inputWorkspace->readX(wi); outputWorkspace->dataY(wi) = inputWorkspace->readY(wi); outputWorkspace->dataE(wi) = inputWorkspace->readE(wi); const int numReqs(3 * (included.size() - 1)); mpi::request reqs[numReqs]; int j(0); // Receive data from all the other processes // This works because the process ranks are ordered the same in // 'included' as // they are in 'world', but in general this is not guaranteed. TODO: // robustify for (int i = 1; i < included.size(); ++i) { size_t index = wi + i * totalSpec; reqs[j++] = included.irecv(i, 0, outputWorkspace->dataX(index)); reqs[j++] = included.irecv(i, 1, outputWorkspace->dataY(index)); reqs[j++] = included.irecv(i, 2, outputWorkspace->dataE(index)); ISpectrum *outSpec = outputWorkspace->getSpectrum(index); outSpec->clearDetectorIDs(); outSpec->addDetectorIDs(inSpec->getDetectorIDs()); } // Make sure everything's been received before exiting the algorithm mpi::wait_all(reqs, reqs + numReqs); } ISpectrum *outSpec = outputWorkspace->getSpectrum(wi); outSpec->clearDetectorIDs(); outSpec->addDetectorIDs(inSpec->getDetectorIDs()); } else { if (accum == "Add") { reduce(included, inputWorkspace->readY(wi), vplus(), 0); reduce(included, inputWorkspace->readE(wi), eplus(), 0); } else if (accum == "Append") { mpi::request reqs[3]; // Send the spectrum to the root process reqs[0] = included.isend(0, 0, inputWorkspace->readX(0)); reqs[1] = included.isend(0, 1, inputWorkspace->readY(0)); reqs[2] = included.isend(0, 2, inputWorkspace->readE(0)); // Make sure the sends have completed before exiting the algorithm mpi::wait_all(reqs, reqs + 3); } } } }