/** Converts X axis to theta representation * @param progress :: Progress indicator * @param targetUnit :: Target conversion unit * @param inputWS :: Input Workspace * @param nHist :: Stores the number of histograms */ void ConvertSpectrumAxis2::createThetaMap(API::Progress &progress, const std::string &targetUnit, API::MatrixWorkspace_sptr &inputWS, size_t nHist) { // Set up binding to member funtion. Avoids condition as part of loop over // nHistograms. boost::function<double(const IDetector &)> thetaFunction; if (targetUnit.compare("signed_theta") == 0 || targetUnit.compare("SignedTheta") == 0) { thetaFunction = boost::bind(&MatrixWorkspace::detectorSignedTwoTheta, inputWS, _1); } else if (targetUnit == "theta" || targetUnit == "Theta") { thetaFunction = boost::bind(&MatrixWorkspace::detectorTwoTheta, inputWS, _1); } bool warningGiven = false; for (size_t i = 0; i < nHist; ++i) { try { IDetector_const_sptr det = inputWS->getDetector(i); // Invoke relevant member function. m_indexMap.emplace(thetaFunction(*det) * rad2deg, i); } catch (Exception::NotFoundError &) { if (!warningGiven) g_log.warning("The instrument definition is incomplete - spectra " "dropped from output"); warningGiven = true; } progress.report("Converting to theta..."); } }
void CalculateDIFC::calculate(API::Progress &progress, API::MatrixWorkspace_sptr &outputWs, DataObjects::OffsetsWorkspace_sptr &offsetsWS, double l1, double beamlineNorm, Kernel::V3D &beamline, Kernel::V3D &samplePos, detid2det_map &allDetectors) { SpecialWorkspace2D_sptr localWS = boost::dynamic_pointer_cast<SpecialWorkspace2D>(outputWs); // Now go through all detid2det_map::const_iterator it = allDetectors.begin(); for (; it != allDetectors.end(); ++it) { Geometry::IDetector_const_sptr det = it->second; if ((!det->isMasked()) && (!det->isMonitor())) { const detid_t detID = it->first; double offset = 0.; if (offsetsWS) offset = offsetsWS->getValue(detID, 0.); double difc = Geometry::Instrument::calcConversion( l1, beamline, beamlineNorm, samplePos, det, offset); difc = 1. / difc; // calcConversion gives 1/DIFC localWS->setValue(detID, difc); } progress.report("Calculate DIFC"); } }
/** * Main work portion of algorithm. Calculates mean of standard deviation, * ignoring * the detectors marked as "bad", then determines if any of the detectors are * "bad". * @param progress :: progress indicator * @param valid :: eventual output workspace, holding 0 for bad and 1 for good * @param values :: stddeviations of each spectra (I think) */ void IdentifyNoisyDetectors::getStdDev(API::Progress &progress, MatrixWorkspace_sptr valid, MatrixWorkspace_sptr values) { const int nhist = static_cast<int>(valid->getNumberHistograms()); int count = 0; double mean = 0.0; double mean2 = 0.0; for (int i = 0; i < nhist; i++) { if (valid->readY(i)[0] > 0) { mean += values->readY(i)[0]; mean2 += std::pow(values->readY(i)[0], 2); count++; } progress.report(); } if (0 == count) { // all values are zero, no need to loop return; } mean = mean / count; double stddev = sqrt((mean2 / count) - std::pow(mean, 2)); double upper = mean + 3 * stddev; double lower = mean - 3 * stddev; double min = mean * 0.0001; for (int i = 0; i < nhist; i++) { double value = values->readY(i)[0]; if (value > upper) { valid->dataY(i)[0] = 0.0; } else if (value < lower) { valid->dataY(i)[0] = 0.0; } else if (value < min) { valid->dataY(i)[0] = 0.0; } progress.report("Calculating StdDev..."); } }
/// Executes the algorithm for events void UnaryOperation::execEvent() { g_log.information("Processing event workspace"); const MatrixWorkspace_const_sptr matrixInputWS = getProperty(inputPropName()); // generate the output workspace pointer API::MatrixWorkspace_sptr matrixOutputWS = getProperty(outputPropName()); if (matrixOutputWS != matrixInputWS) { matrixOutputWS = matrixInputWS->clone(); setProperty(outputPropName(), matrixOutputWS); } auto outputWS = boost::dynamic_pointer_cast<EventWorkspace>(matrixOutputWS); // Now fetch any properties defined by concrete algorithm retrieveProperties(); int64_t numHistograms = static_cast<int64_t>(outputWS->getNumberHistograms()); API::Progress prog = API::Progress(this, 0.0, 1.0, numHistograms); PARALLEL_FOR_IF(Kernel::threadSafe(*outputWS)) for (int64_t i = 0; i < numHistograms; ++i) { PARALLEL_START_INTERUPT_REGION // switch to weighted events if needed, and use the appropriate helper // function auto &evlist = outputWS->getSpectrum(i); switch (evlist.getEventType()) { case TOF: // Switch to weights if needed. evlist.switchTo(WEIGHTED); /* no break */ // Fall through case WEIGHTED: unaryOperationEventHelper(evlist.getWeightedEvents()); break; case WEIGHTED_NOTIME: unaryOperationEventHelper(evlist.getWeightedEventsNoTime()); break; } prog.report(); PARALLEL_END_INTERUPT_REGION } PARALLEL_CHECK_INTERUPT_REGION outputWS->clearMRU(); auto inputWS = boost::dynamic_pointer_cast<EventWorkspace>(matrixOutputWS); if (inputWS->getNumberEvents() != outputWS->getNumberEvents()) { g_log.information() << "Number of events has changed!!!\n"; } }
/** Create the final output workspace after converting the X axis * @returns the final output workspace * * @param progress :: Progress indicator * @param targetUnit :: Target conversion unit * @param inputWS :: Input workspace * @param nHist :: Stores the number of histograms */ MatrixWorkspace_sptr ConvertSpectrumAxis2::createOutputWorkspace( API::Progress &progress, const std::string &targetUnit, API::MatrixWorkspace_sptr &inputWS, size_t nHist) { // Create the output workspace. Can not re-use the input one because the // spectra are re-ordered. MatrixWorkspace_sptr outputWorkspace = WorkspaceFactory::Instance().create( inputWS, m_indexMap.size(), inputWS->x(0).size(), inputWS->y(0).size()); // Now set up a new numeric axis holding the theta values corresponding to // each spectrum. auto const newAxis = new NumericAxis(m_indexMap.size()); outputWorkspace->replaceAxis(1, newAxis); progress.setNumSteps(nHist + m_indexMap.size()); // Set the units of the axis. if (targetUnit == "theta" || targetUnit == "Theta" || targetUnit == "signed_theta" || targetUnit == "SignedTheta") { newAxis->unit() = boost::make_shared<Units::Degrees>(); } else if (targetUnit == "ElasticQ") { newAxis->unit() = UnitFactory::Instance().create("MomentumTransfer"); } else if (targetUnit == "ElasticQSquared") { newAxis->unit() = UnitFactory::Instance().create("QSquared"); } std::multimap<double, size_t>::const_iterator it; size_t currentIndex = 0; for (it = m_indexMap.begin(); it != m_indexMap.end(); ++it) { // Set the axis value. newAxis->setValue(currentIndex, it->first); // Copy over the data. outputWorkspace->setHistogram(currentIndex, inputWS->histogram(it->second)); // We can keep the spectrum numbers etc. outputWorkspace->getSpectrum(currentIndex) .copyInfoFrom(inputWS->getSpectrum(it->second)); ++currentIndex; progress.report("Creating output workspace..."); } return outputWorkspace; }
/** Convert X axis to Elastic Q representation * @param progress :: Progress indicator * @param targetUnit :: Target conversion unit * @param inputWS :: Input workspace * @param nHist :: Stores the number of histograms */ void ConvertSpectrumAxis2::createElasticQMap(API::Progress &progress, const std::string &targetUnit, API::MatrixWorkspace_sptr &inputWS, size_t nHist) { IComponent_const_sptr source = inputWS->getInstrument()->getSource(); IComponent_const_sptr sample = inputWS->getInstrument()->getSample(); const std::string emodeStr = getProperty("EMode"); int emode = 0; if (emodeStr == "Direct") emode = 1; else if (emodeStr == "Indirect") emode = 2; for (size_t i = 0; i < nHist; i++) { IDetector_const_sptr detector = inputWS->getDetector(i); double twoTheta(0.0), efixed(0.0); if (!detector->isMonitor()) { twoTheta = 0.5 * inputWS->detectorTwoTheta(*detector); efixed = getEfixed(detector, inputWS, emode); // get efixed } else { twoTheta = 0.0; efixed = DBL_MIN; } // Convert to MomentumTransfer double elasticQInAngstroms = Kernel::UnitConversion::run(twoTheta, efixed); if (targetUnit == "ElasticQ") { m_indexMap.emplace(elasticQInAngstroms, i); } else if (targetUnit == "ElasticQSquared") { // The QSquared value. double elasticQSquaredInAngstroms = elasticQInAngstroms * elasticQInAngstroms; m_indexMap.emplace(elasticQSquaredInAngstroms, i); } progress.report("Converting to Elastic Q..."); } }
/** Execute the algorithm. */ void LoadNXSPE::exec() { std::string filename = getProperty("Filename"); // quicly check if it's really nxspe try { ::NeXus::File file(filename); std::string mainEntry = (*(file.getEntries().begin())).first; file.openGroup(mainEntry, "NXentry"); file.openData("definition"); if (identiferConfidence(file.getStrData()) < 1) { throw std::invalid_argument("Not NXSPE"); } file.close(); } catch (...) { throw std::invalid_argument("Not NeXus or not NXSPE"); } // Load the data ::NeXus::File file(filename); std::string mainEntry = (*(file.getEntries().begin())).first; file.openGroup(mainEntry, "NXentry"); file.openGroup("NXSPE_info", "NXcollection"); std::map<std::string, std::string> entries = file.getEntries(); std::vector<double> temporary; double fixed_energy, psi = 0.; if (!entries.count("fixed_energy")) { throw std::invalid_argument("fixed_energy field was not found"); } file.openData("fixed_energy"); file.getData(temporary); fixed_energy = temporary.at(0); file.closeData(); if (entries.count("psi")) { file.openData("psi"); file.getData(temporary); psi = temporary.at(0); file.closeData(); } int kikfscaling = 0; if (entries.count("ki_over_kf_scaling")) { file.openData("ki_over_kf_scaling"); std::vector<int> temporaryint; file.getData(temporaryint); kikfscaling = temporaryint.at(0); file.closeData(); } file.closeGroup(); // NXSPE_Info file.openGroup("data", "NXdata"); entries = file.getEntries(); if (!entries.count("data")) { throw std::invalid_argument("data field was not found"); } file.openData("data"); ::NeXus::Info info = file.getInfo(); std::size_t numSpectra = static_cast<std::size_t>(info.dims.at(0)); std::size_t numBins = static_cast<std::size_t>(info.dims.at(1)); std::vector<double> data; file.getData(data); file.closeData(); if (!entries.count("error")) { throw std::invalid_argument("error field was not found"); } file.openData("error"); std::vector<double> error; file.getData(error); file.closeData(); if (!entries.count("energy")) { throw std::invalid_argument("energy field was not found"); } file.openData("energy"); std::vector<double> energies; file.getData(energies); file.closeData(); if (!entries.count("azimuthal")) { throw std::invalid_argument("azimuthal field was not found"); } file.openData("azimuthal"); std::vector<double> azimuthal; file.getData(azimuthal); file.closeData(); if (!entries.count("azimuthal_width")) { throw std::invalid_argument("azimuthal_width field was not found"); } file.openData("azimuthal_width"); std::vector<double> azimuthal_width; file.getData(azimuthal_width); file.closeData(); if (!entries.count("polar")) { throw std::invalid_argument("polar field was not found"); } file.openData("polar"); std::vector<double> polar; file.getData(polar); file.closeData(); if (!entries.count("polar_width")) { throw std::invalid_argument("polar_width field was not found"); } file.openData("polar_width"); std::vector<double> polar_width; file.getData(polar_width); file.closeData(); // distance might not have been saved in all NXSPE files std::vector<double> distance; if (entries.count("distance")) { file.openData("distance"); file.getData(distance); file.closeData(); } file.closeGroup(); // data group file.closeGroup(); // Main entry file.close(); // check if dimensions of the vectors are correct if ((error.size() != data.size()) || (azimuthal.size() != numSpectra) || (azimuthal_width.size() != numSpectra) || (polar.size() != numSpectra) || (polar_width.size() != numSpectra) || ((energies.size() != numBins) && (energies.size() != numBins + 1))) { throw std::invalid_argument( "incompatible sizes of fields in the NXSPE file"); } MatrixWorkspace_sptr outputWS = boost::dynamic_pointer_cast<MatrixWorkspace>( WorkspaceFactory::Instance().create("Workspace2D", numSpectra, energies.size(), numBins)); // Need to get hold of the parameter map outputWS->getAxis(0)->unit() = UnitFactory::Instance().create("DeltaE"); outputWS->setYUnit("SpectraNumber"); // add logs outputWS->mutableRun().addLogData( new PropertyWithValue<double>("Ei", fixed_energy)); outputWS->mutableRun().addLogData(new PropertyWithValue<double>("psi", psi)); outputWS->mutableRun().addLogData(new PropertyWithValue<std::string>( "ki_over_kf_scaling", kikfscaling == 1 ? "true" : "false")); // Set Goniometer Geometry::Goniometer gm; gm.pushAxis("psi", 0, 1, 0, psi); outputWS->mutableRun().setGoniometer(gm, true); // generate instrument Geometry::Instrument_sptr instrument(new Geometry::Instrument("NXSPE")); outputWS->setInstrument(instrument); Geometry::ObjComponent *source = new Geometry::ObjComponent("source"); source->setPos(0.0, 0.0, -10.0); instrument->add(source); instrument->markAsSource(source); Geometry::ObjComponent *sample = new Geometry::ObjComponent("sample"); instrument->add(sample); instrument->markAsSamplePos(sample); Geometry::Object_const_sptr cuboid( createCuboid(0.1, 0.1, 0.1)); // FIXME: memory hog on rendering. Also, // make each detector separate size for (std::size_t i = 0; i < numSpectra; ++i) { double r = 1.0; if (!distance.empty()) { r = distance.at(i); } Kernel::V3D pos; pos.spherical(r, polar.at(i), azimuthal.at(i)); Geometry::Detector *det = new Geometry::Detector("pixel", static_cast<int>(i + 1), sample); det->setPos(pos); det->setShape(cuboid); instrument->add(det); instrument->markAsDetector(det); } Geometry::ParameterMap &pmap = outputWS->instrumentParameters(); std::vector<double>::iterator itdata = data.begin(), iterror = error.begin(), itdataend, iterrorend; API::Progress prog = API::Progress(this, 0.0, 0.9, numSpectra); for (std::size_t i = 0; i < numSpectra; ++i) { itdataend = itdata + numBins; iterrorend = iterror + numBins; outputWS->dataX(i) = energies; if ((!boost::math::isfinite(*itdata)) || (*itdata <= -1e10)) // masked bin { outputWS->dataY(i) = std::vector<double>(numBins, 0); outputWS->dataE(i) = std::vector<double>(numBins, 0); pmap.addBool(outputWS->getDetector(i)->getComponentID(), "masked", true); } else { outputWS->dataY(i) = std::vector<double>(itdata, itdataend); outputWS->dataE(i) = std::vector<double>(iterror, iterrorend); } itdata = (itdataend); iterror = (iterrorend); prog.report(); } setProperty("OutputWorkspace", outputWS); }
/// Executes the algorithm for events void UnaryOperation::execEvent() { g_log.information("Processing event workspace"); const MatrixWorkspace_const_sptr matrixInputWS = this->getProperty(inputPropName()); EventWorkspace_const_sptr inputWS = boost::dynamic_pointer_cast<const EventWorkspace>(matrixInputWS); // generate the output workspace pointer API::MatrixWorkspace_sptr matrixOutputWS = this->getProperty(outputPropName()); EventWorkspace_sptr outputWS; if (matrixOutputWS == matrixInputWS) { outputWS = boost::dynamic_pointer_cast<EventWorkspace>(matrixOutputWS); } else { // Make a brand new EventWorkspace outputWS = boost::dynamic_pointer_cast<EventWorkspace>( API::WorkspaceFactory::Instance().create( "EventWorkspace", inputWS->getNumberHistograms(), 2, 1)); // Copy geometry over. API::WorkspaceFactory::Instance().initializeFromParent(inputWS, outputWS, false); // You need to copy over the data as well. outputWS->copyDataFrom((*inputWS)); // Cast to the matrixOutputWS and save it matrixOutputWS = boost::dynamic_pointer_cast<MatrixWorkspace>(outputWS); this->setProperty("OutputWorkspace", matrixOutputWS); } // Now fetch any properties defined by concrete algorithm retrieveProperties(); int64_t numHistograms = static_cast<int64_t>(inputWS->getNumberHistograms()); API::Progress prog = API::Progress(this, 0.0, 1.0, numHistograms); PARALLEL_FOR1(outputWS) for (int64_t i = 0; i < numHistograms; ++i) { PARALLEL_START_INTERUPT_REGION // switch to weighted events if needed, and use the appropriate helper // function EventList *evlist = outputWS->getEventListPtr(i); switch (evlist->getEventType()) { case TOF: // Switch to weights if needed. evlist->switchTo(WEIGHTED); /* no break */ // Fall through case WEIGHTED: unaryOperationEventHelper(evlist->getWeightedEvents()); break; case WEIGHTED_NOTIME: unaryOperationEventHelper(evlist->getWeightedEventsNoTime()); break; } prog.report(); PARALLEL_END_INTERUPT_REGION } PARALLEL_CHECK_INTERUPT_REGION outputWS->clearMRU(); if (inputWS->getNumberEvents() != outputWS->getNumberEvents()) { g_log.information() << "Number of events has changed!!!" << std::endl; } }
void CorrectKiKf::execEvent() { g_log.information("Processing event workspace"); const MatrixWorkspace_const_sptr matrixInputWS = this->getProperty("InputWorkspace"); EventWorkspace_const_sptr inputWS= boost::dynamic_pointer_cast<const EventWorkspace>(matrixInputWS); // generate the output workspace pointer API::MatrixWorkspace_sptr matrixOutputWS = this->getProperty("OutputWorkspace"); EventWorkspace_sptr outputWS; if (matrixOutputWS == matrixInputWS) outputWS = boost::dynamic_pointer_cast<EventWorkspace>(matrixOutputWS); else { //Make a brand new EventWorkspace outputWS = boost::dynamic_pointer_cast<EventWorkspace>( API::WorkspaceFactory::Instance().create("EventWorkspace", inputWS->getNumberHistograms(), 2, 1)); //Copy geometry over. API::WorkspaceFactory::Instance().initializeFromParent(inputWS, outputWS, false); //You need to copy over the data as well. outputWS->copyDataFrom( (*inputWS) ); //Cast to the matrixOutputWS and save it matrixOutputWS = boost::dynamic_pointer_cast<MatrixWorkspace>(outputWS); this->setProperty("OutputWorkspace", matrixOutputWS); } const std::string emodeStr = getProperty("EMode"); double efixedProp = getProperty("EFixed"),efixed; if( efixedProp == EMPTY_DBL() ) { if (emodeStr == "Direct") { // Check if it has been store on the run object for this workspace if( this->inputWS->run().hasProperty("Ei")) { Kernel::Property* eiprop = this->inputWS->run().getProperty("Ei"); efixedProp = boost::lexical_cast<double>(eiprop->value()); g_log.debug() << "Using stored Ei value " << efixedProp << "\n"; } else { throw std::invalid_argument("No Ei value has been set or stored within the run information."); } } else { // If not specified, will try to get Ef from the parameter file for indirect geometry, // but it will be done for each spectrum separately, in case of different analyzer crystals } } // Get the parameter map const ParameterMap& pmap = outputWS->constInstrumentParameters(); int64_t numHistograms = static_cast<int64_t>(inputWS->getNumberHistograms()); API::Progress prog = API::Progress(this, 0.0, 1.0, numHistograms); PARALLEL_FOR1(outputWS) for (int64_t i=0; i < numHistograms; ++i) { PARALLEL_START_INTERUPT_REGION double Efi = 0; // Now get the detector object for this histogram to check if monitor // or to get Ef for indirect geometry if (emodeStr == "Indirect") { if ( efixedProp != EMPTY_DBL()) Efi = efixedProp; else try { IDetector_const_sptr det = inputWS->getDetector(i); if (!det->isMonitor()) { try { Parameter_sptr par = pmap.getRecursive(det.get(),"Efixed"); if (par) { Efi = par->value<double>(); g_log.debug() << "Detector: " << det->getID() << " EFixed: " << Efi << "\n"; } } catch (std::runtime_error&) { /* Throws if a DetectorGroup, use single provided value */ } } } catch(std::runtime_error&) { g_log.information() << "Workspace Index " << i << ": cannot find detector" << "\n"; } } if (emodeStr == "Indirect") efixed=Efi; else efixed=efixedProp; //Do the correction EventList *evlist=outputWS->getEventListPtr(i); switch (evlist->getEventType()) { case TOF: //Switch to weights if needed. evlist->switchTo(WEIGHTED); /* no break */ // Fall through case WEIGHTED: correctKiKfEventHelper(evlist->getWeightedEvents(), efixed,emodeStr); break; case WEIGHTED_NOTIME: correctKiKfEventHelper(evlist->getWeightedEventsNoTime(), efixed,emodeStr); break; } prog.report(); PARALLEL_END_INTERUPT_REGION } PARALLEL_CHECK_INTERUPT_REGION outputWS->clearMRU(); if (inputWS->getNumberEvents( ) != outputWS->getNumberEvents( )) { g_log.information() <<"Ef <= 0 or Ei <= 0 for "<<inputWS->getNumberEvents( )-outputWS->getNumberEvents( )<<" events, out of "<<inputWS->getNumberEvents( )<<std::endl; if ( efixedProp == EMPTY_DBL()) g_log.information()<<"Try to set fixed energy"<<std::endl ; } }
/** * Create an test instrument with n panels of rectangular detectors, *pixels*pixels in size, * a source and spherical sample shape. * * Banks' lower-left corner is at position (0,0,5*banknum) and they go up to *(pixels*0.008, pixels*0.008, Z) * Pixels are 4 mm wide. * * @param progress :: progress indicator * @param num_banks :: number of rectangular banks to create * @param pixels :: number of pixels in each direction. * @param pixelSpacing :: padding between pixels * @param bankDistanceFromSample :: Distance of first bank from sample (defaults *to 5.0m) * @param sourceSampleDistance :: The distance from the source to the sample * @returns A shared pointer to the generated instrument */ Instrument_sptr CreateSampleWorkspace::createTestInstrumentRectangular( API::Progress &progress, int num_banks, int pixels, double pixelSpacing, const double bankDistanceFromSample, const double sourceSampleDistance) { boost::shared_ptr<Instrument> testInst(new Instrument("basic_rect")); // The instrument is going to be set up with z as the beam axis and y as the // vertical axis. testInst->setReferenceFrame( boost::shared_ptr<ReferenceFrame>(new ReferenceFrame(Y, Z, Left, ""))); const double cylRadius(pixelSpacing / 2); const double cylHeight(0.0002); // One object Object_sptr pixelShape = createCappedCylinder( cylRadius, cylHeight, V3D(0.0, -cylHeight / 2.0, 0.0), V3D(0., 1.0, 0.), "pixel-shape"); for (int banknum = 1; banknum <= num_banks; banknum++) { // Make a new bank std::ostringstream bankname; bankname << "bank" << banknum; RectangularDetector *bank = new RectangularDetector(bankname.str()); bank->initialize(pixelShape, pixels, 0.0, pixelSpacing, pixels, 0.0, pixelSpacing, banknum * pixels * pixels, true, pixels); // Mark them all as detectors for (int x = 0; x < pixels; x++) for (int y = 0; y < pixels; y++) { boost::shared_ptr<Detector> detector = bank->getAtXY(x, y); if (detector) // Mark it as a detector (add to the instrument cache) testInst->markAsDetector(detector.get()); } testInst->add(bank); // Set the bank along the z-axis of the instrument. (beam direction). bank->setPos(V3D(0.0, 0.0, bankDistanceFromSample * banknum)); progress.report(); } // Define a source component ObjComponent *source = new ObjComponent("moderator", Object_sptr(new Object), testInst.get()); source->setPos(V3D(0.0, 0.0, -sourceSampleDistance)); testInst->add(source); testInst->markAsSource(source); // Add chopper ObjComponent *chopper = new ObjComponent( "chopper-position", Object_sptr(new Object), testInst.get()); chopper->setPos(V3D(0.0, 0.0, -0.25 * sourceSampleDistance)); testInst->add(chopper); // Define a sample as a simple sphere Object_sptr sampleSphere = createSphere(0.001, V3D(0.0, 0.0, 0.0), "sample-shape"); ObjComponent *sample = new ObjComponent("sample", sampleSphere, testInst.get()); testInst->setPos(0.0, 0.0, 0.0); testInst->add(sample); testInst->markAsSamplePos(sample); return testInst; }