Skip to content

Commit

Permalink
ENH: WriteAsciData now writes String Arrays (#856)
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Jackson <[email protected]>
  • Loading branch information
imikejackson authored Feb 13, 2024
1 parent a55bf29 commit f36a732
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 39 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 22 additions & 1 deletion src/Plugins/SimplnxCore/docs/WriteASCIIDataFilter.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,28 @@ IO (Output) (Write) (Export) (Text) (CSV) (ASCII)

## Description

This **Filter** accepts DataArray(s) as input, extracts the data, creates the file(s), and writes it out according to parameter choices
This filter will write the selected DataArrays to either individual files or as a single CSV style of file.

## String Data Array Caveats

- The "Maximum Tuples per Line" will not have any effect for that specific array.
- If the output is for a single file, then each String value will be enclosed in a set of Single Quotes (') characters.

### Multiple Files

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.

![Example of multiple output files](Images/Write_Asci_1.png)

### Single File

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:

```console
Euler_0,Euler_1,Euler_2
```

![Example of single output file](Images/Write_Asci_2.png)

% Auto generated parameter table will be inserted here

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,23 +65,23 @@ Parameters WriteASCIIDataFilter::parameters() const

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

// Associate the Linkable Parameter(s) to the children parameters that they control
params.linkParameters(k_OutputStyle_Key, k_MaxValPerLine_Key, std::make_any<uint64>(to_underlying(OutputStyle::MultipleFiles)));
Expand Down
4 changes: 2 additions & 2 deletions src/simplnx/Utilities/DataArrayUtilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ bool CheckArraysHaveSameTupleCount(const DataStructure& dataStructure, const std
std::set<size_t> types;
for(const auto& dataPath : dataArrayPaths)
{
const auto* dataArray = dataStructure.getDataAs<IDataArray>(dataPath);
types.insert(dataArray->getNumberOfTuples());
const auto* iArrayPtr = dataStructure.getDataAs<IArray>(dataPath);
types.insert(iArrayPtr->getNumberOfTuples());
}
return types.size() == 1;
}
Expand Down
80 changes: 51 additions & 29 deletions src/simplnx/Utilities/OStreamUtilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,18 +200,13 @@ struct PrintDataArray
* @param mesgHandler The message handler to dump progress updates to
* // default parameters
* @param delimiter The delimiter to insert between values
* @param componentsPerLine The number of components per line
* @return A result object with any errors or warnings
*/
Result<> PrintStringArray(std::ostream& outputStrm, const StringArray& inputStringArray, const IFilter::MessageHandler& mesgHandler, const std::atomic_bool& shouldCancel,
const std::string& delimiter = ",", int32 componentsPerLine = 0)
const std::string& delimiter = ",")
{
auto start = std::chrono::steady_clock::now();
auto numTuples = inputStringArray.getNumberOfTuples();
auto maxLine = static_cast<size_t>(componentsPerLine);
if(componentsPerLine == 0)
{
maxLine = static_cast<size_t>(inputStringArray.getNumberOfComponents());
}

for(size_t tuple = 0; tuple < numTuples; tuple++)
{
Expand All @@ -226,19 +221,7 @@ Result<> PrintStringArray(std::ostream& outputStrm, const StringArray& inputStri
return {};
}
}

for(size_t index = 0; index < inputStringArray.getNumberOfComponents(); index++)
{
outputStrm << inputStringArray[index];
if(index != maxLine - 1)
{
outputStrm << delimiter;
}
else
{
outputStrm << "\n";
}
}
outputStrm << inputStringArray[tuple] << "\n";
}

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

class StringTupleWriter : public ITupleWriter
{
using DataArrayType = StringArray;

public:
StringTupleWriter(const StringArray& iDataArray, const std::string& delimiter)
: m_DataArray(dynamic_cast<const StringArray&>(iDataArray))
, m_Delimiter(delimiter)
{
}
~StringTupleWriter() override = default;

StringTupleWriter(const StringTupleWriter&) = delete;
StringTupleWriter(StringTupleWriter&&) noexcept = delete;

StringTupleWriter& operator=(const StringTupleWriter&) = delete;
StringTupleWriter& operator=(StringTupleWriter&&) noexcept = delete;

void write(std::ostream& outputStrm, usize tupleIndex) const override
{
outputStrm << m_Delimiter << m_DataArray[tupleIndex] << m_Delimiter;
}

void writeHeader(std::ostream& outputStrm) const override
{
outputStrm << m_DataArray.getName();
}

private:
const DataArrayType& m_DataArray;
const std::string m_Delimiter = "'";
};

template <typename ScalarType>
class TupleWriter : public ITupleWriter
{
Expand Down Expand Up @@ -396,11 +412,7 @@ void PrintDataSetsToMultipleFiles(const std::vector<DataPath>& objectPaths, Data
auto* stringArray = dataStructure.getDataAs<StringArray>(dataPath);
if(stringArray != nullptr)
{
if(exportToBinary)
{
throw std::runtime_error(fmt::format("{}({}): Function {}: Error. Cannot print a StringArray to binary: '{}'", "PrintDataSetsToMultipleFiles", __FILE__, __LINE__, dataPath.getTargetName()));
}
PrintStringArray(outStrm, *stringArray, mesgHandler, shouldCancel, delimiter, componentsPerLine);
PrintStringArray(outStrm, *stringArray, mesgHandler, shouldCancel, delimiter);
}
auto* neighborList = dataStructure.getDataAs<INeighborList>(dataPath);
if(neighborList != nullptr)
Expand Down Expand Up @@ -449,7 +461,7 @@ void PrintSingleDataObject(std::ostream& outputStrm, const DataPath& objectPath,
auto* stringArray = dataStructure.getDataAs<StringArray>(objectPath);
if(stringArray != nullptr)
{
PrintStringArray(outputStrm, *stringArray, mesgHandler, shouldCancel, delimiter, componentsPerLine);
PrintStringArray(outputStrm, *stringArray, mesgHandler, shouldCancel, delimiter);
}
auto* neighborList = dataStructure.getDataAs<INeighborList>(objectPath);
if(neighborList != nullptr)
Expand All @@ -476,16 +488,26 @@ void PrintDataSetsToSingleFile(std::ostream& outputStrm, const std::vector<DataP
const std::atomic_bool& shouldCancel, const std::string& delimiter, bool includeIndex, bool includeHeaders, bool writeFirstIndex, const std::string& indexName,
const std::vector<DataPath>& neighborLists, bool writeNumOfFeatures)
{
const auto& firstDataArray = dataStructure.getDataRefAs<IDataArray>(objectPaths[0]);
const auto& firstDataArray = dataStructure.getDataRefAs<IArray>(objectPaths[0]);
usize numTuples = firstDataArray.getNumberOfTuples();
auto start = std::chrono::steady_clock::now();

// Create our wrapper classes for each DataArray
std::vector<std::shared_ptr<ITupleWriter>> writers;
for(const auto& selectedArrayPath : objectPaths)
{
const auto& iDataArray = dataStructure.getDataRefAs<IDataArray>(selectedArrayPath);
ExecuteDataFunction(AddTupleWriter{}, iDataArray.getDataType(), writers, iDataArray, delimiter);
auto* dataArrayPtr = dataStructure.getDataAs<IDataArray>(selectedArrayPath);
if(nullptr != dataArrayPtr)
{
const auto& iDataArrayRef = dataStructure.getDataRefAs<IDataArray>(selectedArrayPath);
ExecuteDataFunction(AddTupleWriter{}, iDataArrayRef.getDataType(), writers, iDataArrayRef, delimiter);
}
auto* stringArrayPtr = dataStructure.getDataAs<StringArray>(selectedArrayPath);
if(nullptr != stringArrayPtr)
{
const auto& iDataArrayRef = dataStructure.getDataRefAs<StringArray>(selectedArrayPath);
writers.push_back(std::make_shared<StringTupleWriter>(iDataArrayRef, "'"));
}
}
size_t writersCount = writers.size();

Expand Down

0 comments on commit f36a732

Please sign in to comment.