Skip to content

Commit f36a732

Browse files
authored
ENH: WriteAsciData now writes String Arrays (#856)
Signed-off-by: Michael Jackson <[email protected]>
1 parent a55bf29 commit f36a732

File tree

6 files changed

+82
-39
lines changed

6 files changed

+82
-39
lines changed
71.2 KB
Loading
65.6 KB
Loading

src/Plugins/SimplnxCore/docs/WriteASCIIDataFilter.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,28 @@ IO (Output) (Write) (Export) (Text) (CSV) (ASCII)
66

77
## Description
88

9-
This **Filter** accepts DataArray(s) as input, extracts the data, creates the file(s), and writes it out according to parameter choices
9+
This filter will write the selected DataArrays to either individual files or as a single CSV style of file.
10+
11+
## String Data Array Caveats
12+
13+
- The "Maximum Tuples per Line" will not have any effect for that specific array.
14+
- If the output is for a single file, then each String value will be enclosed in a set of Single Quotes (') characters.
15+
16+
### Multiple Files
17+
18+
Each input data array will be written to its own output file. The name of the file will be the name of the Data Array + the extension from the parameters.
19+
20+
![Example of multiple output files](Images/Write_Asci_1.png)
21+
22+
### Single File
23+
24+
The output data file will be a column oriented CSV file. The optional header of each column will be the name of the Data Array. If the Data Array has multiple components then the zero based index will also be appended to the data array name. For example Euler Angles have 3 components, the header would look like:
25+
26+
```console
27+
Euler_0,Euler_1,Euler_2
28+
```
29+
30+
![Example of single output file](Images/Write_Asci_2.png)
1031

1132
% Auto generated parameter table will be inserted here
1233

src/Plugins/SimplnxCore/src/SimplnxCore/Filters/WriteASCIIDataFilter.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,23 +65,23 @@ Parameters WriteASCIIDataFilter::parameters() const
6565

6666
// Create the parameter descriptors that are needed for this filter
6767
params.insertSeparator(Parameters::Separator{"Input Parameters"});
68-
params.insertLinkableParameter(std::make_unique<ChoicesParameter>(k_OutputStyle_Key, "Output Type", "Whether to output a folder of files or a single file with all the data in column form",
69-
to_underlying(OutputStyle::MultipleFiles),
68+
params.insertLinkableParameter(std::make_unique<ChoicesParameter>(k_OutputStyle_Key, "Output File Generation",
69+
"Whether to output a folder of files or a single file with all the data in column form", to_underlying(OutputStyle::SingleFile),
7070
ChoicesParameter::Choices{"Multiple Files", "Single File"})); // sequence dependent DO NOT REORDER
7171
params.insert(std::make_unique<FileSystemPathParameter>(k_OutputPath_Key, "Output Path", "The output file path", fs::path(""), FileSystemPathParameter::ExtensionsType{},
7272
FileSystemPathParameter::PathType::OutputFile, true));
7373
params.insert(std::make_unique<FileSystemPathParameter>(k_OutputDir_Key, "Output Directory", "The output file path", fs::path(""), FileSystemPathParameter::ExtensionsType{},
7474
FileSystemPathParameter::PathType::OutputDir, true));
75-
params.insert(std::make_unique<StringParameter>(k_FileExtension_Key, "File Extension", "The file extension for the output file(s)", ".txt"));
76-
params.insert(std::make_unique<Int32Parameter>(k_MaxValPerLine_Key, "Maximum Elements Per Line", "Number of tuples to print on each line", 0));
75+
params.insert(std::make_unique<StringParameter>(k_FileExtension_Key, "File Extension", "The file extension for the output file(s)", ".csv"));
76+
params.insert(std::make_unique<Int32Parameter>(k_MaxValPerLine_Key, "Maximum Tuples Per Line", "Number of tuples to print on each line. Does not apply to string arrays", 1));
7777
params.insert(std::make_unique<ChoicesParameter>(k_Delimiter_Key, "Delimiter", "The delimiter separating the data", to_underlying(OStreamUtilities::Delimiter::Comma),
7878
ChoicesParameter::Choices{"Space", "Semicolon", "Comma", "Colon", "Tab"})); // sequence dependent DO NOT REORDER
7979
params.insert(std::make_unique<ChoicesParameter>(k_Includes_Key, "Header and Index Options", "Default Include is Headers only", to_underlying(Includes::Headers),
8080
ChoicesParameter::Choices{"Neither", "Headers", "Index", "Both"})); // sequence dependent DO NOT REORDER
8181
params.insertSeparator(Parameters::Separator{"Required Data Objects"});
82-
params.insert(std::make_unique<MultiArraySelectionParameter>(k_SelectedDataArrayPaths_Key, "Attribute Arrays to Export", "Output arrays to be written as ASCII representations",
83-
MultiArraySelectionParameter::ValueType{}, MultiArraySelectionParameter::AllowedTypes{IArray::ArrayType::DataArray},
84-
nx::core::GetAllDataTypes()));
82+
params.insert(std::make_unique<MultiArraySelectionParameter>(k_SelectedDataArrayPaths_Key, "Attribute Arrays to Export", "Data Arrays to be written to disk",
83+
MultiArraySelectionParameter::ValueType{},
84+
MultiArraySelectionParameter::AllowedTypes{IArray::ArrayType::DataArray, IArray::ArrayType::StringArray}, nx::core::GetAllDataTypes()));
8585

8686
// Associate the Linkable Parameter(s) to the children parameters that they control
8787
params.linkParameters(k_OutputStyle_Key, k_MaxValPerLine_Key, std::make_any<uint64>(to_underlying(OutputStyle::MultipleFiles)));

src/simplnx/Utilities/DataArrayUtilities.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ bool CheckArraysHaveSameTupleCount(const DataStructure& dataStructure, const std
136136
std::set<size_t> types;
137137
for(const auto& dataPath : dataArrayPaths)
138138
{
139-
const auto* dataArray = dataStructure.getDataAs<IDataArray>(dataPath);
140-
types.insert(dataArray->getNumberOfTuples());
139+
const auto* iArrayPtr = dataStructure.getDataAs<IArray>(dataPath);
140+
types.insert(iArrayPtr->getNumberOfTuples());
141141
}
142142
return types.size() == 1;
143143
}

src/simplnx/Utilities/OStreamUtilities.cpp

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -200,18 +200,13 @@ struct PrintDataArray
200200
* @param mesgHandler The message handler to dump progress updates to
201201
* // default parameters
202202
* @param delimiter The delimiter to insert between values
203-
* @param componentsPerLine The number of components per line
203+
* @return A result object with any errors or warnings
204204
*/
205205
Result<> PrintStringArray(std::ostream& outputStrm, const StringArray& inputStringArray, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel,
206-
const std::string& delimiter = ",", int32 componentsPerLine = 0)
206+
const std::string& delimiter = ",")
207207
{
208208
auto start = std::chrono::steady_clock::now();
209209
auto numTuples = inputStringArray.getNumberOfTuples();
210-
auto maxLine = static_cast<size_t>(componentsPerLine);
211-
if(componentsPerLine == 0)
212-
{
213-
maxLine = static_cast<size_t>(inputStringArray.getNumberOfComponents());
214-
}
215210

216211
for(size_t tuple = 0; tuple < numTuples; tuple++)
217212
{
@@ -226,19 +221,7 @@ Result<> PrintStringArray(std::ostream& outputStrm, const StringArray& inputStri
226221
return {};
227222
}
228223
}
229-
230-
for(size_t index = 0; index < inputStringArray.getNumberOfComponents(); index++)
231-
{
232-
outputStrm << inputStringArray[index];
233-
if(index != maxLine - 1)
234-
{
235-
outputStrm << delimiter;
236-
}
237-
else
238-
{
239-
outputStrm << "\n";
240-
}
241-
}
224+
outputStrm << inputStringArray[tuple] << "\n";
242225
}
243226

244227
return {};
@@ -253,6 +236,39 @@ class ITupleWriter
253236
virtual void writeHeader(std::ostream& outputStrm) const = 0;
254237
};
255238

239+
class StringTupleWriter : public ITupleWriter
240+
{
241+
using DataArrayType = StringArray;
242+
243+
public:
244+
StringTupleWriter(const StringArray& iDataArray, const std::string& delimiter)
245+
: m_DataArray(dynamic_cast<const StringArray&>(iDataArray))
246+
, m_Delimiter(delimiter)
247+
{
248+
}
249+
~StringTupleWriter() override = default;
250+
251+
StringTupleWriter(const StringTupleWriter&) = delete;
252+
StringTupleWriter(StringTupleWriter&&) noexcept = delete;
253+
254+
StringTupleWriter& operator=(const StringTupleWriter&) = delete;
255+
StringTupleWriter& operator=(StringTupleWriter&&) noexcept = delete;
256+
257+
void write(std::ostream& outputStrm, usize tupleIndex) const override
258+
{
259+
outputStrm << m_Delimiter << m_DataArray[tupleIndex] << m_Delimiter;
260+
}
261+
262+
void writeHeader(std::ostream& outputStrm) const override
263+
{
264+
outputStrm << m_DataArray.getName();
265+
}
266+
267+
private:
268+
const DataArrayType& m_DataArray;
269+
const std::string m_Delimiter = "'";
270+
};
271+
256272
template <typename ScalarType>
257273
class TupleWriter : public ITupleWriter
258274
{
@@ -396,11 +412,7 @@ void PrintDataSetsToMultipleFiles(const std::vector<DataPath>& objectPaths, Data
396412
auto* stringArray = dataStructure.getDataAs<StringArray>(dataPath);
397413
if(stringArray != nullptr)
398414
{
399-
if(exportToBinary)
400-
{
401-
throw std::runtime_error(fmt::format("{}({}): Function {}: Error. Cannot print a StringArray to binary: '{}'", "PrintDataSetsToMultipleFiles", __FILE__, __LINE__, dataPath.getTargetName()));
402-
}
403-
PrintStringArray(outStrm, *stringArray, mesgHandler, shouldCancel, delimiter, componentsPerLine);
415+
PrintStringArray(outStrm, *stringArray, mesgHandler, shouldCancel, delimiter);
404416
}
405417
auto* neighborList = dataStructure.getDataAs<INeighborList>(dataPath);
406418
if(neighborList != nullptr)
@@ -449,7 +461,7 @@ void PrintSingleDataObject(std::ostream& outputStrm, const DataPath& objectPath,
449461
auto* stringArray = dataStructure.getDataAs<StringArray>(objectPath);
450462
if(stringArray != nullptr)
451463
{
452-
PrintStringArray(outputStrm, *stringArray, mesgHandler, shouldCancel, delimiter, componentsPerLine);
464+
PrintStringArray(outputStrm, *stringArray, mesgHandler, shouldCancel, delimiter);
453465
}
454466
auto* neighborList = dataStructure.getDataAs<INeighborList>(objectPath);
455467
if(neighborList != nullptr)
@@ -476,16 +488,26 @@ void PrintDataSetsToSingleFile(std::ostream& outputStrm, const std::vector<DataP
476488
const std::atomic_bool& shouldCancel, const std::string& delimiter, bool includeIndex, bool includeHeaders, bool writeFirstIndex, const std::string& indexName,
477489
const std::vector<DataPath>& neighborLists, bool writeNumOfFeatures)
478490
{
479-
const auto& firstDataArray = dataStructure.getDataRefAs<IDataArray>(objectPaths[0]);
491+
const auto& firstDataArray = dataStructure.getDataRefAs<IArray>(objectPaths[0]);
480492
usize numTuples = firstDataArray.getNumberOfTuples();
481493
auto start = std::chrono::steady_clock::now();
482494

483495
// Create our wrapper classes for each DataArray
484496
std::vector<std::shared_ptr<ITupleWriter>> writers;
485497
for(const auto& selectedArrayPath : objectPaths)
486498
{
487-
const auto& iDataArray = dataStructure.getDataRefAs<IDataArray>(selectedArrayPath);
488-
ExecuteDataFunction(AddTupleWriter{}, iDataArray.getDataType(), writers, iDataArray, delimiter);
499+
auto* dataArrayPtr = dataStructure.getDataAs<IDataArray>(selectedArrayPath);
500+
if(nullptr != dataArrayPtr)
501+
{
502+
const auto& iDataArrayRef = dataStructure.getDataRefAs<IDataArray>(selectedArrayPath);
503+
ExecuteDataFunction(AddTupleWriter{}, iDataArrayRef.getDataType(), writers, iDataArrayRef, delimiter);
504+
}
505+
auto* stringArrayPtr = dataStructure.getDataAs<StringArray>(selectedArrayPath);
506+
if(nullptr != stringArrayPtr)
507+
{
508+
const auto& iDataArrayRef = dataStructure.getDataRefAs<StringArray>(selectedArrayPath);
509+
writers.push_back(std::make_shared<StringTupleWriter>(iDataArrayRef, "'"));
510+
}
489511
}
490512
size_t writersCount = writers.size();
491513

0 commit comments

Comments
 (0)