15
15
// Maximum base file name size on modern windows systems is 260 characters
16
16
#define NAME_MAX 260
17
17
#endif /* _WIN32 */
18
+
18
19
#include " MantidAPI/NumericAxis.h"
19
20
#include " MantidDataHandling/SaveNexusProcessedHelper.h"
20
21
#include " MantidDataObjects/EventWorkspace.h"
21
22
#include " MantidDataObjects/PeaksWorkspace.h"
22
23
#include " MantidDataObjects/RebinnedOutput.h"
23
24
#include " MantidDataObjects/TableWorkspace.h"
25
+ #include " MantidHistogramData/Histogram.h"
24
26
25
27
#include < Poco/File.h>
26
28
#include < Poco/Path.h>
29
+ #include < memory>
27
30
28
31
namespace Mantid ::NeXus {
29
32
using namespace Kernel ;
30
33
using namespace API ;
31
34
using namespace DataObjects ;
32
35
36
+ using HistogramData::HistogramDx;
37
+ using HistogramData::HistogramE;
38
+ using HistogramData::HistogramX;
39
+ using HistogramData::HistogramY;
40
+ using Mantid::MantidVec; // for `<rebinned output>->readF(size_t index)`
41
+
33
42
namespace {
34
43
// / static logger
35
44
Logger g_log (" NexusFileIO" );
@@ -189,6 +198,79 @@ int NexusFileIO::writeNexusProcessedHeader(const std::string &title, const std::
189
198
return (0 );
190
199
}
191
200
201
+ // -------------------------------------------------------------------------------------
202
+ // Internal constants and template functions, used by `NexusFileIO::writeNexusProcessedData2D`:
203
+
204
+ namespace {
205
+
206
+ const double _DEFAULT_FILL_VALUE (0.0 );
207
+
208
+ // Typedef for vector-accessor member functions with signatures like:
209
+ // `const HistogramData::HistogramY & Mantid::API::MatrixWorkspace::y ( const size_t index )
210
+ // const`
211
+ template <class V , class WS > using _VAccessor = const V &(WS::*)(size_t ) const ;
212
+
213
+ // Return the data pointer for a vector:
214
+ // this allows us to work with both `MantidVec` and `FixedLengthVector`.
215
+ template <class V > const double *_dataPointer (const V &v) { return v.rawData ().data (); }
216
+
217
+ template <> const double *_dataPointer<MantidVec>(const MantidVec &v) { return v.data (); }
218
+
219
+ // Internal-use method:
220
+ // * Create the dataset and write chunks of double-precision data;
221
+ // * Optionally fill the chunks with a specified fill value;
222
+ // * Optionally close the dataset.
223
+ template <class V , class WS >
224
+ void _writeChunkedData (std::shared_ptr<::NeXus::File> dest, // Must have open group, but NO open dataset
225
+ const std::string &name,
226
+ std::shared_ptr<const WS> src, // Do not pass std::shared_ptr<..> by reference!
227
+ const std::vector<int > &indices, _VAccessor<V, WS> vData, bool raggedSpectra = false ,
228
+ ::NeXus::NXcompression compressionType = ::NeXus::NXcompression::NONE,
229
+ double fillValue = _DEFAULT_FILL_VALUE, bool closeData = true ) {
230
+
231
+ const size_t N_chunk = indices.size (); // number of spectra
232
+
233
+ size_t _chunk_size = ((*src).*vData)(0 ).size ();
234
+ if (raggedSpectra) {
235
+ for (size_t n : indices)
236
+ _chunk_size = std::max (_chunk_size, ((*src).*vData)(n).size ());
237
+ }
238
+ const size_t chunk_size = _chunk_size;
239
+
240
+ const ::NeXus::DimVector dims = {static_cast <::NeXus::dimsize_t >(N_chunk),
241
+ static_cast <::NeXus::dimsize_t >(chunk_size)};
242
+ const ::NeXus::DimSizeVector chunk_dims = {1 , static_cast <::NeXus::dimsize_t >(chunk_size)};
243
+
244
+ // Create and open the dataset.
245
+ // (If compressionType == NXcompression::NONE, this just creates a non-compressed dataset.)
246
+ dest->makeCompData (name, NXnumtype::FLOAT64, dims, compressionType, chunk_dims, true );
247
+
248
+ // Prepare a padding vector. (Unfortunately, NeXus-api does not access `setFillValue`.)
249
+ const bool pad_data (fillValue != _DEFAULT_FILL_VALUE);
250
+ const std::vector<double > vFill (pad_data ? chunk_size : 0 , fillValue);
251
+
252
+ // Write the data.
253
+ ::NeXus::DimVector start{0 , 0 };
254
+ for (size_t n : indices) {
255
+ const auto &v = ((*src).*vData)(n);
256
+ const ::NeXus::DimSizeVector data_dims = {1 , static_cast <::NeXus::dimsize_t >(v.size ())};
257
+ dest->putSlab (_dataPointer<V>(v), start, data_dims);
258
+ if (pad_data && data_dims[1 ] != static_cast <::NeXus::dimsize_t >(chunk_size)) {
259
+ // Fill the remainder of the slab.
260
+ const ::NeXus::DimSizeVector fill_dims = {1 , static_cast <::NeXus::dimsize_t >(chunk_size) - data_dims[1 ]};
261
+ const ::NeXus::DimVector _start = {start[0 ], data_dims[1 ]};
262
+ dest->putSlab (vFill.data (), _start, fill_dims);
263
+ }
264
+
265
+ ++start[0 ];
266
+ }
267
+
268
+ if (closeData)
269
+ dest->closeData ();
270
+ }
271
+
272
+ } // namespace
273
+
192
274
// -------------------------------------------------------------------------------------
193
275
/* * Write out a MatrixWorkspace's data as a 2D matrix.
194
276
* Use writeNexusProcessedDataEvent if writing an EventWorkspace.
@@ -210,13 +292,6 @@ int NexusFileIO::writeNexusProcessedData2D(const API::MatrixWorkspace_const_sptr
210
292
const size_t nHist = localworkspace->getNumberHistograms ();
211
293
if (nHist < 1 )
212
294
return (2 );
213
- const size_t nSpect = indices.size ();
214
- size_t nSpectBins = localworkspace->y (0 ).size ();
215
- if (raggedSpectra)
216
- for (size_t i = 0 ; i < nSpect; i++)
217
- nSpectBins = std::max (nSpectBins, localworkspace->y (indices[i]).size ());
218
- ::NeXus::DimVector dims_array = {static_cast <::NeXus::dimsize_t >(nSpect),
219
- static_cast <::NeXus::dimsize_t >(nSpectBins)};
220
295
221
296
// Set the axis labels and values
222
297
Mantid::API::Axis *xAxis = localworkspace->getAxis (0 );
@@ -240,24 +315,21 @@ int NexusFileIO::writeNexusProcessedData2D(const API::MatrixWorkspace_const_sptr
240
315
}
241
316
// Get the values on the vertical axis
242
317
std::vector<double > axis2;
318
+ const size_t nSpect = indices.size ();
243
319
if (nSpect < nHist)
244
320
for (size_t i = 0 ; i < nSpect; i++)
245
321
axis2.emplace_back ((*sAxis )(indices[i]));
246
322
else
247
323
for (size_t i = 0 ; i < sAxis ->length (); i++)
248
324
axis2.emplace_back ((*sAxis )(i));
249
325
250
- ::NeXus::DimVector start = {0 , 0 };
251
- ::NeXus::DimSizeVector asize = {1 , dims_array[1 ]};
252
-
253
326
// -------------- Actually write the 2D data ----------------------------
254
327
if (write2Ddata) {
255
- std::string name = " values" ;
256
- m_filehandle->makeCompData (name, NXnumtype::FLOAT64, dims_array, m_nexuscompression, asize, true );
257
- for (size_t i = 0 ; i < nSpect; i++) {
258
- m_filehandle->putSlab (localworkspace->y (indices[i]).rawData ().data (), start, asize);
259
- start[0 ]++;
260
- }
328
+ _writeChunkedData<HistogramY, MatrixWorkspace>(m_filehandle, " values" , localworkspace, indices, &MatrixWorkspace::y,
329
+ raggedSpectra, m_nexuscompression, _DEFAULT_FILL_VALUE,
330
+ false // don't close the dataset
331
+ );
332
+
261
333
if (m_progress != nullptr )
262
334
m_progress->reportIncrement (1 , " Writing data" );
263
335
m_filehandle->putAttr (" signal" , 1 );
@@ -267,28 +339,21 @@ int NexusFileIO::writeNexusProcessedData2D(const API::MatrixWorkspace_const_sptr
267
339
m_filehandle->putAttr (" unit_label" , localworkspace->YUnitLabel (), false );
268
340
m_filehandle->closeData ();
269
341
270
- // error
271
- name = " errors" ;
272
- m_filehandle->makeCompData (name, NXnumtype::FLOAT64, dims_array, m_nexuscompression, asize, true );
273
- start[0 ] = 0 ;
274
- for (size_t i = 0 ; i < nSpect; i++) {
275
- m_filehandle->putSlab (localworkspace->e (indices[i]).rawData ().data (), start, asize);
276
- start[0 ]++;
277
- }
278
-
342
+ // errors
343
+ _writeChunkedData<HistogramE, MatrixWorkspace>(m_filehandle, " errors" , localworkspace, indices, &MatrixWorkspace::e,
344
+ raggedSpectra, m_nexuscompression);
279
345
if (m_progress != nullptr )
280
346
m_progress->reportIncrement (1 , " Writing data" );
281
347
282
348
// Fractional area for RebinnedOutput
283
349
if (localworkspace->id () == " RebinnedOutput" ) {
284
350
RebinnedOutput_const_sptr rebin_workspace = std::dynamic_pointer_cast<const RebinnedOutput>(localworkspace);
285
- name = " frac_area" ;
286
- m_filehandle->makeCompData (name, NXnumtype::FLOAT64, dims_array, m_nexuscompression, asize, true );
287
- start[0 ] = 0 ;
288
- for (size_t i = 0 ; i < nSpect; i++) {
289
- m_filehandle->putSlab (rebin_workspace->readF (indices[i]).data (), start, asize);
290
- start[0 ]++;
291
- }
351
+
352
+ _writeChunkedData<MantidVec, RebinnedOutput>(m_filehandle, " frac_area" , rebin_workspace, indices,
353
+ &RebinnedOutput::readF, raggedSpectra, m_nexuscompression,
354
+ _DEFAULT_FILL_VALUE,
355
+ false // don't close the dataset
356
+ );
292
357
293
358
std::string finalized = (rebin_workspace->isFinalized ()) ? " 1" : " 0" ;
294
359
m_filehandle->putAttr (" finalized" , finalized);
@@ -297,65 +362,28 @@ int NexusFileIO::writeNexusProcessedData2D(const API::MatrixWorkspace_const_sptr
297
362
298
363
if (m_progress != nullptr )
299
364
m_progress->reportIncrement (1 , " Writing data" );
365
+ m_filehandle->closeData ();
300
366
}
301
367
302
- // Potentially x error
368
+ // x errors
303
369
if (localworkspace->hasDx (0 )) {
304
- dims_array[0 ] = static_cast <int >(nSpect);
305
- dims_array[1 ] = static_cast <int >(localworkspace->dx (0 ).size ());
306
- std::string dxErrorName = " xerrors" ;
307
- m_filehandle->makeCompData (dxErrorName, NXnumtype::FLOAT64, dims_array, m_nexuscompression, asize, true );
308
- start[0 ] = 0 ;
309
- asize[1 ] = dims_array[1 ];
310
- for (size_t i = 0 ; i < nSpect; i++) {
311
- m_filehandle->putSlab (localworkspace->dx (indices[i]).rawData ().data (), start, asize);
312
- start[0 ]++;
313
- }
370
+ _writeChunkedData<HistogramDx, MatrixWorkspace>(m_filehandle, " xerrors" , localworkspace, indices,
371
+ &MatrixWorkspace::dx, raggedSpectra, m_nexuscompression);
314
372
}
315
-
316
- m_filehandle->closeData ();
317
373
}
318
374
319
375
// write X data, as single array or all values if "ragged"
320
- if (raggedSpectra) {
321
- size_t max_x_size{0 };
322
- for (size_t i = 0 ; i < nSpect; i++)
323
- max_x_size = std::max (max_x_size, localworkspace->x (indices[i]).size ());
324
- dims_array[0 ] = static_cast <int >(nSpect);
325
- dims_array[1 ] = static_cast <int >(max_x_size);
326
- m_filehandle->makeData (" axis1" , NXnumtype::FLOAT64, dims_array, true );
327
- start[0 ] = 0 ;
328
-
329
- // create vector of NaNs to fill invalid space at end of ragged array
330
- std::vector<double > nans (max_x_size, std::numeric_limits<double >::quiet_NaN ());
331
-
332
- for (size_t i = 0 ; i < nSpect; i++) {
333
- size_t nBins = localworkspace->x (indices[i]).size ();
334
- asize[1 ] = static_cast <int >(nBins);
335
- m_filehandle->putSlab (localworkspace->x (indices[i]).rawData ().data (), start, asize);
336
- if (nBins < max_x_size) {
337
- start[1 ] = asize[1 ];
338
- asize[1 ] = static_cast <int >(max_x_size - nBins);
339
- m_filehandle->putSlab (nans.data (), start, asize);
340
- start[1 ] = 0 ;
341
- }
342
- start[0 ]++;
343
- }
344
- } else if (uniformSpectra) {
376
+ if (uniformSpectra) {
345
377
m_filehandle->makeData (" axis1" , NXnumtype::FLOAT64,
346
378
::NeXus::DimVector{static_cast <::NeXus::dimsize_t >(localworkspace->x (0 ).size ())}, true );
347
379
m_filehandle->putData (localworkspace->x (0 ).rawData ().data ());
348
380
349
381
} else {
350
- dims_array[0 ] = static_cast <int >(nSpect);
351
- dims_array[1 ] = static_cast <int >(localworkspace->x (0 ).size ());
352
- m_filehandle->makeData (" axis1" , NXnumtype::FLOAT64, dims_array, true );
353
- start[0 ] = 0 ;
354
- asize[1 ] = dims_array[1 ];
355
- for (size_t i = 0 ; i < nSpect; i++) {
356
- m_filehandle->putSlab (localworkspace->x (indices[i]).rawData ().data (), start, asize);
357
- start[0 ]++;
358
- }
382
+ _writeChunkedData<HistogramX, MatrixWorkspace>(
383
+ m_filehandle, " axis1" , localworkspace, indices, &MatrixWorkspace::x, raggedSpectra,
384
+ ::NeXus::NXcompression::NONE, raggedSpectra ? std::numeric_limits<double >::quiet_NaN () : _DEFAULT_FILL_VALUE,
385
+ false // don't close the dataset
386
+ );
359
387
}
360
388
361
389
std::string dist = (localworkspace->isDistribution ()) ? " 1" : " 0" ;
0 commit comments