Beispiel #1
0
    void PeaksWorkspace::saveNexus(::NeXus::File * file) const
    {

      //Number of Peaks
      const size_t np(peaks.size());

      // Column vectors for peaks table
      std::vector<int> detectorID(np);
      std::vector<double> H(np);
      std::vector<double> K(np);
      std::vector<double> L(np);
      std::vector<double> intensity(np);
      std::vector<double> sigmaIntensity(np);
      std::vector<double> binCount(np);
      std::vector<double> initialEnergy(np);
      std::vector<double> finalEnergy(np);
      std::vector<double> waveLength(np);
      std::vector<double> scattering(np);
      std::vector<double> dSpacing(np);
      std::vector<double> TOF(np);
      std::vector<int> runNumber(np);
      std::vector<double> goniometerMatrix(9 * np);

      // Populate column vectors from Peak Workspace
      for (size_t i = 0; i < np; i++)
      {
        Peak p = peaks[i];
        detectorID[i] = p.getDetectorID();
        H[i] = p.getH();
        K[i] = p.getK();
        L[i] = p.getL();
        intensity[i] = p.getIntensity();
        sigmaIntensity[i] = p.getSigmaIntensity();
        binCount[i] = p.getBinCount();
        initialEnergy[i] = p.getInitialEnergy();
        finalEnergy[i] = p.getFinalEnergy();
        waveLength[i] = p.getWavelength();
        scattering[i] = p.getScattering();
        dSpacing[i] = p.getDSpacing();
        TOF[i] = p.getTOF();
        runNumber[i] = p.getRunNumber();
        {
          Matrix<double> gm = p.getGoniometerMatrix();
          goniometerMatrix[9 * i] = gm[0][0];
          goniometerMatrix[9 * i + 1] = gm[1][0];
          goniometerMatrix[9 * i + 2] = gm[2][0];
          goniometerMatrix[9 * i + 3] = gm[0][1];
          goniometerMatrix[9 * i + 4] = gm[1][1];
          goniometerMatrix[9 * i + 5] = gm[2][1];
          goniometerMatrix[9 * i + 6] = gm[0][2];
          goniometerMatrix[9 * i + 7] = gm[1][2];
          goniometerMatrix[9 * i + 8] = gm[1][2];
        }
        // etc.
      }

      // Start Peaks Workspace in Nexus File
      std::string specifyInteger = "An integer";
      std::string specifyDouble = "A double";
      file->makeGroup("peaks_workspace", "NXentry", true);  // For when peaksWorkspace can be loaded

      // Detectors column
      file->writeData("column_1", detectorID);
      file->openData("column_1");
      file->putAttr("name", "Dectector ID");
      file->putAttr("interpret_as", specifyInteger);
      file->putAttr("units", "Not known");
      file->closeData();

      // H column
      file->writeData("column_2", H);
      file->openData("column_2");
      file->putAttr("name", "H");
      file->putAttr("interpret_as", specifyDouble);
      file->putAttr("units", "Not known");  // Units may need changing when known
      file->closeData();

      // K column
      file->writeData("column_3", K);
      file->openData("column_3");
      file->putAttr("name", "K");
      file->putAttr("interpret_as", specifyDouble);
      file->putAttr("units", "Not known");  // Units may need changing when known
      file->closeData();

      // L column
      file->writeData("column_4", L);
      file->openData("column_4");
      file->putAttr("name", "L");
      file->putAttr("interpret_as", specifyDouble);
      file->putAttr("units", "Not known");  // Units may need changing when known
      file->closeData();

      // Intensity column
      file->writeData("column_5", intensity);
      file->openData("column_5");
      file->putAttr("name", "Intensity");
      file->putAttr("interpret_as", specifyDouble);
      file->putAttr("units", "Not known");  // Units may need changing when known
      file->closeData();

      // Sigma Intensity column
      file->writeData("column_6", sigmaIntensity);
      file->openData("column_6");
      file->putAttr("name", "Sigma Intensity");
      file->putAttr("interpret_as", specifyDouble);
      file->putAttr("units", "Not known");  // Units may need changing when known
      file->closeData();

      // Bin Count column
      file->writeData("column_7", binCount);
      file->openData("column_7");
      file->putAttr("name", "Bin Count");
      file->putAttr("interpret_as", specifyDouble);
      file->putAttr("units", "Not known");  // Units may need changing when known
      file->closeData();

      // Initial Energy column
      file->writeData("column_8", initialEnergy);
      file->openData("column_8");
      file->putAttr("name", "Initial Energy");
      file->putAttr("interpret_as", specifyDouble);
      file->putAttr("units", "Not known");  // Units may need changing when known
      file->closeData();

      // Final Energy column
      file->writeData("column_9", finalEnergy);
      file->openData("column_9");
      file->putAttr("name", "Final Energy");
      file->putAttr("interpret_as", specifyDouble);
      file->putAttr("units", "Not known");  // Units may need changing when known
      file->closeData();

      // Wave Length Column
      file->writeData("column_10", waveLength);
      file->openData("column_10");
      file->putAttr("name", "Wave Length");
      file->putAttr("interpret_as", specifyDouble);
      file->putAttr("units", "Not known");  // Units may need changing when known
      file->closeData();

      // Scattering Column
      file->writeData("column_11", scattering);
      file->openData("column_11");
      file->putAttr("name", "Scattering");
      file->putAttr("interpret_as", specifyDouble);
      file->putAttr("units", "Not known");  // Units may need changing when known
      file->closeData();

      // D Spacing Column
      file->writeData("column_12", dSpacing);
      file->openData("column_12");
      file->putAttr("name", "D Spacing");
      file->putAttr("interpret_as", specifyDouble);
      file->putAttr("units", "Not known");  // Units may need changing when known
      file->closeData();

      // TOF Column
      file->writeData("column_13", TOF);
      file->openData("column_13");
      file->putAttr("name", "TOF");
      file->putAttr("interpret_as", specifyDouble);
      file->putAttr("units", "Not known");  // Units may need changing when known
      file->closeData();

      //Run Number column
      file->writeData("column_14", runNumber);
      file->openData("column_14");
      file->putAttr("name", "Run Number");
      file->putAttr("interpret_as", specifyInteger);
      file->putAttr("units", "Not known");  // Units may need changing when known
      file->closeData();

      // Goniometer Matrix Column
      std::vector<int> array_dims;
      array_dims.push_back(static_cast<int>(peaks.size()));
      array_dims.push_back(9);
      file->writeData("column_15", goniometerMatrix, array_dims);
      file->openData("column_15");
      file->putAttr("name", "Goniometer Matrix");
      file->putAttr("interpret_as", "A matrix of 3x3 doubles");
      file->putAttr("units", "Not known");  // Units may need changing when known
      file->closeData();

      // QLab & QSample are calculated and do not need to be saved

      file->closeGroup(); // end of peaks workpace

    }
Beispiel #2
0
void PeakHKLErrors::functionDeriv1D(Jacobian *out, const double *xValues,
                                    const size_t nData) {
  PeaksWorkspace_sptr Peaks =
      AnalysisDataService::Instance().retrieveWS<PeaksWorkspace>(
          PeakWorkspaceName);
  boost::shared_ptr<Geometry::Instrument> instNew = getNewInstrument(Peaks);

  const DblMatrix &UB = Peaks->sample().getOrientedLattice().getUB();
  DblMatrix UBinv(UB);
  UBinv.Invert();
  UBinv /= 2 * M_PI;

  double GonRotx = getParameter("GonRotx");
  double GonRoty = getParameter("GonRoty");
  double GonRotz = getParameter("GonRotz");
  Matrix<double> InvGonRotxMat = RotationMatrixAboutRegAxis(GonRotx, 'x');
  Matrix<double> InvGonRotyMat = RotationMatrixAboutRegAxis(GonRoty, 'y');
  Matrix<double> InvGonRotzMat = RotationMatrixAboutRegAxis(GonRotz, 'z');
  Matrix<double> GonRot = InvGonRotxMat * InvGonRotyMat * InvGonRotzMat;

  InvGonRotxMat.Invert();
  InvGonRotyMat.Invert();
  InvGonRotzMat.Invert();

  std::map<int, Kernel::Matrix<double>> RunNums2GonMatrix;
  getRun2MatMap(Peaks, OptRuns, RunNums2GonMatrix);

  g_log.debug()
      << "----------------------------Derivative------------------------\n";

  V3D samplePosition = instNew->getSample()->getPos();
  IPeak &ppeak = Peaks->getPeak(0);
  double L0 = ppeak.getL1();
  double velocity = (L0 + ppeak.getL2()) / ppeak.getTOF();

  double K =
      2 * M_PI / ppeak.getWavelength() / velocity; // 2pi/lambda = K* velocity
  V3D beamDir = instNew->getBeamDirection();

  size_t paramNums[] = {parameterIndex(std::string("SampleXOffset")),
                        parameterIndex(std::string("SampleYOffset")),
                        parameterIndex(std::string("SampleZOffset"))};

  for (size_t i = 0; i < nData; i += 3) {
    int peakNum = boost::math::iround(xValues[i]);
    IPeak &peak_old = Peaks->getPeak(peakNum);
    Peak peak = createNewPeak(peak_old, instNew, 0, peak_old.getL1());

    int runNum = peak_old.getRunNumber();
    std::string runNumStr = std::to_string(runNum);

    for (int kk = 0; kk < static_cast<int>(nParams()); kk++) {
      out->set(i, kk, 0.0);
      out->set(i + 1, kk, 0.0);
      out->set(i + 2, kk, 0.0);
    }

    double chi, phi, omega;
    size_t chiParamNum, phiParamNum, omegaParamNum;

    size_t N = OptRuns.find("/" + runNumStr);
    if (N < OptRuns.size()) {
      chi = getParameter("chi" + (runNumStr));
      phi = getParameter("phi" + (runNumStr));
      omega = getParameter("omega" + (runNumStr));

      peak.setGoniometerMatrix(GonRot * RunNums2GonMatrix[runNum]);

      chiParamNum = parameterIndex("chi" + (runNumStr));
      phiParamNum = parameterIndex("phi" + (runNumStr));
      omegaParamNum = parameterIndex("omega" + (runNumStr));
    } else {

      Geometry::Goniometer Gon(peak.getGoniometerMatrix());
      std::vector<double> phichiOmega = Gon.getEulerAngles("YZY");
      chi = phichiOmega[1];
      phi = phichiOmega[2];
      omega = phichiOmega[0];
      // peak.setGoniometerMatrix( GonRot*Gon.getR());
      chiParamNum = phiParamNum = omegaParamNum = nParams() + 10;
      peak.setGoniometerMatrix(GonRot * peak.getGoniometerMatrix());
    }
    V3D sampOffsets(getParameter("SampleXOffset"),
                    getParameter("SampleYOffset"),
                    getParameter("SampleZOffset"));
    peak.setSamplePos(peak.getSamplePos() + sampOffsets);
    // NOTE:Use getQLabFrame except for below.
    // For parameters the getGoniometerMatrix should remove GonRot, for derivs
    // wrt GonRot*, wrt chi*,phi*,etc.

    // Deriv wrt chi phi and omega
    if (phiParamNum < nParams()) {
      Matrix<double> chiMatrix = RotationMatrixAboutRegAxis(chi, 'z');
      Matrix<double> phiMatrix = RotationMatrixAboutRegAxis(phi, 'y');
      Matrix<double> omegaMatrix = RotationMatrixAboutRegAxis(omega, 'y');

      Matrix<double> dchiMatrix = DerivRotationMatrixAboutRegAxis(chi, 'z');
      Matrix<double> dphiMatrix = DerivRotationMatrixAboutRegAxis(phi, 'y');
      Matrix<double> domegaMatrix = DerivRotationMatrixAboutRegAxis(omega, 'y');

      Matrix<double> InvG = omegaMatrix * chiMatrix * phiMatrix;
      InvG.Invert();
      // Calculate Derivatives wrt chi(phi,omega) in degrees
      Matrix<double> R = omegaMatrix * chiMatrix * dphiMatrix;
      Matrix<double> InvR = InvG * R * InvG * -1;
      V3D lab = peak.getQLabFrame();
      V3D Dhkl0 = UBinv * InvR * lab;

      R = omegaMatrix * dchiMatrix * phiMatrix;
      InvR = InvG * R * InvG * -1;
      V3D Dhkl1 = UBinv * InvR * peak.getQLabFrame();

      R = domegaMatrix * chiMatrix * phiMatrix;
      InvR = InvG * R * InvG * -1;
      V3D Dhkl2 =
          UBinv * InvR * peak.getQLabFrame(); // R.transpose should be R inverse

      out->set(i, chiParamNum, Dhkl1[0]);
      out->set(i + 1, chiParamNum, Dhkl1[1]);
      out->set(i + 2, chiParamNum, Dhkl1[2]);
      out->set(i, phiParamNum, Dhkl0[0]);
      out->set(i + 1, phiParamNum, Dhkl0[1]);
      out->set(i + 2, phiParamNum, Dhkl0[2]);
      out->set(i, omegaParamNum, Dhkl2[0]);
      out->set(i + 1, omegaParamNum, Dhkl2[1]);
      out->set(i + 2, omegaParamNum, Dhkl2[2]);

    } // if optimize for chi phi and omega on this peak

    //------------------------Goniometer Rotation Derivatives
    //-----------------------
    Matrix<double> InvGonRot(GonRot);
    InvGonRot.Invert();
    Matrix<double> InvGon = InvGonRot * peak.getGoniometerMatrix();
    InvGon.Invert();
    V3D DGonx = (UBinv * InvGon * InvGonRotzMat * InvGonRotyMat *
                 DerivRotationMatrixAboutRegAxis(
                     -GonRotx, 'x') * // - gives inverse of GonRot
                 peak.getQLabFrame()) *
                -1;

    V3D DGony = (UBinv * InvGon * InvGonRotzMat *
                 DerivRotationMatrixAboutRegAxis(-GonRoty, 'y') *
                 InvGonRotxMat * peak.getQLabFrame()) *
                -1;
    V3D DGonz =
        (UBinv * InvGon * DerivRotationMatrixAboutRegAxis(-GonRotz, 'z') *
         InvGonRotyMat * InvGonRotxMat * peak.getQLabFrame()) *
        -1;

    size_t paramnum = parameterIndex("GonRotx");
    out->set(i, paramnum, DGonx[0]);
    out->set(i + 1, paramnum, DGonx[1]);
    out->set(i + 2, paramnum, DGonx[2]);
    out->set(i, parameterIndex("GonRoty"), DGony[0]);
    out->set(i + 1, parameterIndex("GonRoty"), DGony[1]);
    out->set(i + 2, parameterIndex("GonRoty"), DGony[2]);
    out->set(i, parameterIndex("GonRotz"), DGonz[0]);
    out->set(i + 1, parameterIndex("GonRotz"), DGonz[1]);
    out->set(i + 2, parameterIndex("GonRotz"), DGonz[2]);
    //-------------------- Sample Orientation derivatives
    //----------------------------------
    // Qlab = -KV + k|V|*beamdir
    // D = pos-sampPos
    //|V|= vmag=(L0 + D )/tof
    // t1= tof - L0/|V|   {time from sample to pixel}
    // V = D/t1
    V3D D = peak.getDetPos() - samplePosition;
    double vmag = (L0 + D.norm()) / peak.getTOF();
    double t1 = peak.getTOF() - L0 / vmag;

    // Derivs wrt sample x, y, z
    // Ddsx =( - 1, 0, 0),  d|D|^2/dsx -> 2|D|d|D|/dsx =d(tranp(D)* D)/dsx =2
    // Ddsx* tranp(D)
    //|D| also called Dmag
    V3D Dmagdsxsysz(D);
    Dmagdsxsysz *= (-1 / D.norm());

    V3D vmagdsxsysz = Dmagdsxsysz / peak.getTOF();

    V3D t1dsxsysz = vmagdsxsysz * (L0 / vmag / vmag);
    Matrix<double> Gon = peak.getGoniometerMatrix();
    Gon.Invert();

    // x=0 is deriv wrt SampleXoffset, x=1 is deriv wrt SampleYoffset, etc.
    for (int x = 0; x < 3; x++) {
      V3D pp;
      pp[x] = 1;
      V3D dQlab1 = pp / -t1 - D * (t1dsxsysz[x] / t1 / t1);
      V3D dQlab2 = beamDir * vmagdsxsysz[x];
      V3D dQlab = dQlab2 - dQlab1;
      dQlab *= K;

      V3D dQSamp = Gon * dQlab;
      V3D dhkl = UBinv * dQSamp;

      out->set(i, paramNums[x], dhkl[0]);
      out->set(i + 1, paramNums[x], dhkl[1]);
      out->set(i + 2, paramNums[x], dhkl[2]);
    }
  }
}
Beispiel #3
0
  /** Append the peaks from a .peaks file into the workspace
   * @param outWS :: the workspace in which to place the information
   * @param filename :: path to the .peaks file
   */
  void LoadIsawPeaks::appendFile( PeaksWorkspace_sptr outWS, std::string filename )
  {

    // Open the file
    std::ifstream in( filename.c_str() );


    // Read the header, load the instrument
    double T0;
    std::string s = readHeader( outWS, in , T0);
    // set T0 in the run parameters
    API::Run & m_run = outWS->mutableRun();
    m_run.addProperty<double>("T0", T0, true);

    if( !in.good() || s.length() < 1 )
      throw std::runtime_error( "End of Peaks file before peaks" );

    if( s.compare( std::string( "0" ) ) != 0 )
      throw std::logic_error( "No header for Peak segments"  );

    readToEndOfLine( in ,  true );
    s = getWord( in , false );

    int run, bankNum;
    double chi , phi , omega , monCount;

    // Build the universal goniometer that will build the rotation matrix.
    Mantid::Geometry::Goniometer uniGonio;
    uniGonio.makeUniversalGoniometer();

    // TODO: Can we find the number of peaks to get better progress reporting?
    Progress prog(this, 0.0, 1.0, 100);

    while( in.good() )
    {
      // Read the header if necessary
      s = readPeakBlockHeader( s ,  in  , run , bankNum , chi , phi ,
          omega , monCount );
      // Build the Rotation matrix using phi,chi,omega
      uniGonio.setRotationAngle("phi", phi);
      uniGonio.setRotationAngle("chi", chi);
      uniGonio.setRotationAngle("omega", omega);
      //Put goniometer into peaks workspace
      outWS->mutableRun().setGoniometer(uniGonio, false);


      std::ostringstream oss;
      std::string bankString = "bank";
      if (outWS->getInstrument()->getName() == "WISH") bankString = "WISHpanel0";
      oss << bankString << bankNum;
      std::string bankName = oss.str();

      int seqNum = -1;

      try
      {
        // Read the peak
        Peak peak = readPeak(outWS, s, in, seqNum, bankName);

        // Get the calculated goniometer matrix
        Matrix<double> gonMat = uniGonio.getR();

        peak.setGoniometerMatrix(gonMat);
        peak.setRunNumber(run);
        peak.setMonitorCount( monCount );

        double tof = peak.getTOF();
        Kernel::Units::Wavelength wl;

        wl.initialize(peak.getL1(), peak.getL2(), peak.getScattering(), 0,
                  peak.getInitialEnergy(), 0.0);

        peak.setWavelength(wl.singleFromTOF( tof));
        // Add the peak to workspace
        outWS->addPeak(peak);
      }
      catch (std::runtime_error & e)
      {
        g_log.warning() << "Error reading peak SEQN " << seqNum << " : " << e.what() << std::endl;
      }

      prog.report();
    }

  }
void PeaksWorkspace::saveNexus(::NeXus::File *file) const {

  // Number of Peaks
  const size_t np(peaks.size());

  // Column vectors for peaks table
  std::vector<int> detectorID(np);
  std::vector<double> H(np);
  std::vector<double> K(np);
  std::vector<double> L(np);
  std::vector<double> intensity(np);
  std::vector<double> sigmaIntensity(np);
  std::vector<double> binCount(np);
  std::vector<double> initialEnergy(np);
  std::vector<double> finalEnergy(np);
  std::vector<double> waveLength(np);
  std::vector<double> scattering(np);
  std::vector<double> dSpacing(np);
  std::vector<double> TOF(np);
  std::vector<int> runNumber(np);
  std::vector<double> goniometerMatrix(9 * np);
  std::vector<std::string> shapes(np);

  // Populate column vectors from Peak Workspace
  size_t maxShapeJSONLength = 0;
  for (size_t i = 0; i < np; i++) {
    Peak p = peaks[i];
    detectorID[i] = p.getDetectorID();
    H[i] = p.getH();
    K[i] = p.getK();
    L[i] = p.getL();
    intensity[i] = p.getIntensity();
    sigmaIntensity[i] = p.getSigmaIntensity();
    binCount[i] = p.getBinCount();
    initialEnergy[i] = p.getInitialEnergy();
    finalEnergy[i] = p.getFinalEnergy();
    waveLength[i] = p.getWavelength();
    scattering[i] = p.getScattering();
    dSpacing[i] = p.getDSpacing();
    TOF[i] = p.getTOF();
    runNumber[i] = p.getRunNumber();
    {
      Matrix<double> gm = p.getGoniometerMatrix();
      goniometerMatrix[9 * i] = gm[0][0];
      goniometerMatrix[9 * i + 1] = gm[1][0];
      goniometerMatrix[9 * i + 2] = gm[2][0];
      goniometerMatrix[9 * i + 3] = gm[0][1];
      goniometerMatrix[9 * i + 4] = gm[1][1];
      goniometerMatrix[9 * i + 5] = gm[2][1];
      goniometerMatrix[9 * i + 6] = gm[0][2];
      goniometerMatrix[9 * i + 7] = gm[1][2];
      goniometerMatrix[9 * i + 8] = gm[2][2];
    }
    const std::string shapeJSON = p.getPeakShape().toJSON();
    shapes[i] = shapeJSON;
    if (shapeJSON.size() > maxShapeJSONLength) {
      maxShapeJSONLength = shapeJSON.size();
    }
  }

  // Start Peaks Workspace in Nexus File
  const std::string specifyInteger = "An integer";
  const std::string specifyDouble = "A double";
  const std::string specifyString = "A string";
  file->makeGroup("peaks_workspace", "NXentry",
                  true); // For when peaksWorkspace can be loaded

  // Coordinate system
  file->writeData("coordinate_system", static_cast<uint32_t>(m_coordSystem));

  // Write out the Qconvention
  // ki-kf for Inelastic convention; kf-ki for Crystallography convention
  std::string m_QConvention = this->getConvention();
  file->putAttr("QConvention", m_QConvention);

  // Detectors column
  file->writeData("column_1", detectorID);
  file->openData("column_1");
  file->putAttr("name", "Detector ID");
  file->putAttr("interpret_as", specifyInteger);
  file->putAttr("units", "Not known");
  file->closeData();

  // H column
  file->writeData("column_2", H);
  file->openData("column_2");
  file->putAttr("name", "H");
  file->putAttr("interpret_as", specifyDouble);
  file->putAttr("units", "Not known"); // Units may need changing when known
  file->closeData();

  // K column
  file->writeData("column_3", K);
  file->openData("column_3");
  file->putAttr("name", "K");
  file->putAttr("interpret_as", specifyDouble);
  file->putAttr("units", "Not known"); // Units may need changing when known
  file->closeData();

  // L column
  file->writeData("column_4", L);
  file->openData("column_4");
  file->putAttr("name", "L");
  file->putAttr("interpret_as", specifyDouble);
  file->putAttr("units", "Not known"); // Units may need changing when known
  file->closeData();

  // Intensity column
  file->writeData("column_5", intensity);
  file->openData("column_5");
  file->putAttr("name", "Intensity");
  file->putAttr("interpret_as", specifyDouble);
  file->putAttr("units", "Not known"); // Units may need changing when known
  file->closeData();

  // Sigma Intensity column
  file->writeData("column_6", sigmaIntensity);
  file->openData("column_6");
  file->putAttr("name", "Sigma Intensity");
  file->putAttr("interpret_as", specifyDouble);
  file->putAttr("units", "Not known"); // Units may need changing when known
  file->closeData();

  // Bin Count column
  file->writeData("column_7", binCount);
  file->openData("column_7");
  file->putAttr("name", "Bin Count");
  file->putAttr("interpret_as", specifyDouble);
  file->putAttr("units", "Not known"); // Units may need changing when known
  file->closeData();

  // Initial Energy column
  file->writeData("column_8", initialEnergy);
  file->openData("column_8");
  file->putAttr("name", "Initial Energy");
  file->putAttr("interpret_as", specifyDouble);
  file->putAttr("units", "Not known"); // Units may need changing when known
  file->closeData();

  // Final Energy column
  file->writeData("column_9", finalEnergy);
  file->openData("column_9");
  file->putAttr("name", "Final Energy");
  file->putAttr("interpret_as", specifyDouble);
  file->putAttr("units", "Not known"); // Units may need changing when known
  file->closeData();

  // Wave Length Column
  file->writeData("column_10", waveLength);
  file->openData("column_10");
  file->putAttr("name", "Wave Length");
  file->putAttr("interpret_as", specifyDouble);
  file->putAttr("units", "Not known"); // Units may need changing when known
  file->closeData();

  // Scattering Column
  file->writeData("column_11", scattering);
  file->openData("column_11");
  file->putAttr("name", "Scattering");
  file->putAttr("interpret_as", specifyDouble);
  file->putAttr("units", "Not known"); // Units may need changing when known
  file->closeData();

  // D Spacing Column
  file->writeData("column_12", dSpacing);
  file->openData("column_12");
  file->putAttr("name", "D Spacing");
  file->putAttr("interpret_as", specifyDouble);
  file->putAttr("units", "Not known"); // Units may need changing when known
  file->closeData();

  // TOF Column
  file->writeData("column_13", TOF);
  file->openData("column_13");
  file->putAttr("name", "TOF");
  file->putAttr("interpret_as", specifyDouble);
  file->putAttr("units", "Not known"); // Units may need changing when known
  file->closeData();

  // Run Number column
  file->writeData("column_14", runNumber);
  file->openData("column_14");
  file->putAttr("name", "Run Number");
  file->putAttr("interpret_as", specifyInteger);
  file->putAttr("units", "Not known"); // Units may need changing when known
  file->closeData();

  // Goniometer Matrix Column
  std::vector<int> array_dims;
  array_dims.push_back(static_cast<int>(peaks.size()));
  array_dims.push_back(9);
  file->writeData("column_15", goniometerMatrix, array_dims);
  file->openData("column_15");
  file->putAttr("name", "Goniometer Matrix");
  file->putAttr("interpret_as", "A matrix of 3x3 doubles");
  file->putAttr("units", "Not known"); // Units may need changing when known
  file->closeData();

  // Shape
  std::vector<int64_t> dims;
  dims.push_back(np);
  dims.push_back(static_cast<int>(maxShapeJSONLength));
  const std::string name = "column_16";
  file->makeData(name, NeXus::CHAR, dims, false);
  file->openData(name);

  auto toNexus = new char[maxShapeJSONLength * np];
  for (size_t ii = 0; ii < np; ii++) {
    std::string rowStr = shapes[ii];
    for (size_t ic = 0; ic < rowStr.size(); ic++)
      toNexus[ii * maxShapeJSONLength + ic] = rowStr[ic];
    for (size_t ic = rowStr.size();
         ic < static_cast<size_t>(maxShapeJSONLength); ic++)
      toNexus[ii * maxShapeJSONLength + ic] = ' ';
  }

  file->putData((void *)(toNexus));

  delete[] toNexus;
  file->putAttr("units", "Not known"); // Units may need changing when known
  file->putAttr("name", "Shape");
  file->putAttr("interpret_as", specifyString);
  file->closeData();

  // QLab & QSample are calculated and do not need to be saved

  file->closeGroup(); // end of peaks workpace
}