Skip to content

Commit

Permalink
Merge pull request #12751 from aeslaughter/vpp-csv-11087
Browse files Browse the repository at this point in the history
Create FINAL and LATEST symlinks for VPP files
  • Loading branch information
permcody committed Jan 22, 2019
2 parents 9e9a9c1 + 8084c33 commit 28c3afd
Show file tree
Hide file tree
Showing 9 changed files with 304 additions and 7 deletions.
16 changes: 16 additions & 0 deletions framework/include/outputs/CSV.h
Expand Up @@ -97,6 +97,22 @@ class CSV : public TableOutput


/// Flag indicating MOOSE is recovering via --recover command-line option /// Flag indicating MOOSE is recovering via --recover command-line option
bool _recovering; bool _recovering;

/// Flag for creating a _FINAL symlink
bool _create_final_symlink;

/// Flag for creating a _LATEST symlink
bool _create_latest_symlink;

/// Current list of VPP filenames for creating _LATEST/_FINAL symlinks
// The pair is composed of the complete filename (foo_variable_0001.csv) and the incomplete name
// (foo_variable) to which the _FINAL or _LATEST is to be applied.
std::vector<std::pair<std::string, std::string>> _latest_vpp_filenames;

/**
* Returns the filename without the time/timestep information.
*/
std::string getVectorPostprocessorFilePrefix(const std::string & vpp_name);
}; };


#endif /* CSV_H */ #endif /* CSV_H */
10 changes: 10 additions & 0 deletions framework/include/utils/MooseUtils.h
Expand Up @@ -517,6 +517,16 @@ template <>
unsigned long long int convert<unsigned long long int>(const std::string & str, unsigned long long int convert<unsigned long long int>(const std::string & str,
bool throw_on_failure); bool throw_on_failure);


/**
* Create a symbolic link, if the link already exists it is replaced.
*/
void createSymlink(const std::string & target, const std::string & link);

/**
* Remove a symbolic link, if the given filename is a link.
*/
void clearSymlink(const std::string & link);

/** /**
* Convert supplied string to upper case. * Convert supplied string to upper case.
* @params name The string to convert upper case. * @params name The string to convert upper case.
Expand Down
65 changes: 58 additions & 7 deletions framework/src/outputs/CSV.C
Expand Up @@ -30,6 +30,14 @@ validParams<CSV>()
"Align the outputted csv data by padding the numbers with trailing whitespace"); "Align the outputted csv data by padding the numbers with trailing whitespace");
params.addParam<std::string>("delimiter", ",", "Assign the delimiter (default is ','"); params.addParam<std::string>("delimiter", ",", "Assign the delimiter (default is ','");
params.addParam<unsigned int>("precision", 14, "Set the output precision"); params.addParam<unsigned int>("precision", 14, "Set the output precision");
params.addParam<bool>("create_final_symlink",
true,
"Enable/disable the creation of a _FINAL symlink for vector postprocessor "
"data with 'execute_on' includes 'FINAL'.");
params.addParam<bool>(
"create_latest_symlink",
true,
"Enable/disable the creation of a _LATEST symlink for vector postprocessor data.");


// Suppress unused parameters // Suppress unused parameters
params.suppressParameter<unsigned int>("padding"); params.suppressParameter<unsigned int>("padding");
Expand All @@ -46,7 +54,9 @@ CSV::CSV(const InputParameters & parameters)
_write_all_table(false), _write_all_table(false),
_write_vector_table(false), _write_vector_table(false),
_sort_columns(getParam<bool>("sort_columns")), _sort_columns(getParam<bool>("sort_columns")),
_recovering(_app.isRecovering()) _recovering(_app.isRecovering()),
_create_final_symlink(getParam<bool>("create_final_symlink")),
_create_latest_symlink(getParam<bool>("create_latest_symlink"))
{ {
} }


Expand All @@ -64,6 +74,20 @@ CSV::initialSetup()


if (_recovering) if (_recovering)
_all_data_table.append(true); _all_data_table.append(true);

// Clear any existing symbolic links to LATEST and/or FINAL
if (processor_id() == 0)
{
const std::set<std::string> & out = getVectorPostprocessorOutput();
for (const auto & vpp_name : out)
{
std::string short_name = MooseUtils::shortName(vpp_name);
std::string out_latest = _file_base + "_" + short_name + "_LATEST.csv";
std::string out_final = _file_base + "_" + short_name + "_FINAL.csv";
MooseUtils::clearSymlink(out_latest);
MooseUtils::clearSymlink(out_final);
}
}
} }


std::string std::string
Expand Down Expand Up @@ -130,27 +154,54 @@ CSV::output(const ExecFlagType & type)
// Output each VectorPostprocessor's data to a file // Output each VectorPostprocessor's data to a file
if (_write_vector_table && processor_id() == 0) if (_write_vector_table && processor_id() == 0)
{ {

// The VPP table will not write the same data twice, so to get the symlinks correct
// for EXEC_FINAL (when other flags exist) whenever files are written the names must
// be stored. These stored names are then used outside of this loop when the EXEC_FINAL call is
// made.
_latest_vpp_filenames.clear();

for (auto & it : _vector_postprocessor_tables) for (auto & it : _vector_postprocessor_tables)
{ {
auto vpp_name = it.first; const auto & vpp_name = it.first;
it.second.setDelimiter(_delimiter); it.second.setDelimiter(_delimiter);
it.second.setPrecision(_precision); it.second.setPrecision(_precision);
if (_sort_columns) if (_sort_columns)
it.second.sortColumns(); it.second.sortColumns();


auto include_time_suffix = !vpp_data.containsCompleteHistory(vpp_name); auto include_time_suffix = !vpp_data.containsCompleteHistory(vpp_name);
std::string fname = getVectorPostprocessorFileName(vpp_name, include_time_suffix);
std::string fprefix = getVectorPostprocessorFilePrefix(vpp_name);
_latest_vpp_filenames.emplace_back(fname, fprefix);
it.second.printCSV(fname, 1, _align);


it.second.printCSV(getVectorPostprocessorFileName(vpp_name, include_time_suffix), 1, _align); if (_create_latest_symlink)

if (_time_data)
{ {
std::string file_name = _file_base + '_' + MooseUtils::shortName(vpp_name) + "_time.csv"; std::string out_latest = fprefix + "_LATEST.csv";
_vector_postprocessor_time_tables[vpp_name].printCSV(file_name); MooseUtils::createSymlink(fname, out_latest);
} }

if (_time_data)
_vector_postprocessor_time_tables[vpp_name].printCSV(fprefix + "_time.csv");
}
}

if (type == EXEC_FINAL && _create_final_symlink && processor_id() == 0)
{
for (const auto & name_pair : _latest_vpp_filenames)
{
std::string out_final = name_pair.second + "_FINAL.csv";
MooseUtils::createSymlink(name_pair.first, out_final);
} }
} }


// Re-set write flags // Re-set write flags
_write_all_table = false; _write_all_table = false;
_write_vector_table = false; _write_vector_table = false;
} }

std::string
CSV::getVectorPostprocessorFilePrefix(const std::string & vpp_name)
{
return _file_base + "_" + MooseUtils::shortName(vpp_name);
}
21 changes: 21 additions & 0 deletions framework/src/utils/MooseUtils.C
Expand Up @@ -731,6 +731,7 @@ stringToInteger(const std::string & input, bool throw_on_failure)
} }


void void

linearPartitionItems(dof_id_type num_items, linearPartitionItems(dof_id_type num_items,
dof_id_type num_chunks, dof_id_type num_chunks,
dof_id_type chunk_id, dof_id_type chunk_id,
Expand Down Expand Up @@ -809,6 +810,26 @@ template std::string join<std::vector<MooseEnumItem>>(const std::vector<MooseEnu
template std::string join<std::set<MooseEnumItem>>(const std::set<MooseEnumItem> &, template std::string join<std::set<MooseEnumItem>>(const std::set<MooseEnumItem> &,
const std::string &); const std::string &);


void
createSymlink(const std::string & target, const std::string & link)
{
clearSymlink(link);
int err = symlink(target.c_str(), link.c_str());
if (err != 0)
mooseError("Failed to create symbolic link (via 'symlink') from ", target, " to ", link);
}

void
clearSymlink(const std::string & link)
{
struct stat sbuf;
if (lstat(link.c_str(), &sbuf) == 0)
{
int err = unlink(link.c_str());
if (err != 0)
mooseError("Failed to remove symbolic link (via 'unlink') to ", link);
}
}
} // MooseUtils namespace } // MooseUtils namespace


std::string std::string
Expand Down
68 changes: 68 additions & 0 deletions test/tests/outputs/csv_final_and_latest/final.i
@@ -0,0 +1,68 @@
[Mesh]
type = GeneratedMesh
dim = 2
nx = 10
ny = 10
[]

[Variables]
[./u]
[../]
[]

[Kernels]
[./diff]
type = CoefDiffusion
variable = u
coef = 0.1
[../]
[./time]
type = TimeDerivative
variable = u
[../]
[]

[BCs]
[./left]
type = DirichletBC
variable = u
boundary = left
value = 0
[../]
[./right]
type = DirichletBC
variable = u
boundary = right
value = 1
[../]
[]

[Executioner]
type = Transient
num_steps = 10
dt = 0.25
solve_type = PJFNK
petsc_options_iname = '-pc_type -pc_hypre_type'
petsc_options_value = 'hypre boomeramg'
[]

# Vector Postprocessor System
[VectorPostprocessors]
[./line_sample]
type = LineValueSampler
execute_on = 'timestep_end final'
variable = 'u'
start_point = '0 0.5 0'
end_point = '1 0.5 0'
num_points = 11
sort_by = id
[../]
[]

[Outputs]
[./out]
type = CSV
execute_on = 'TIMESTEP_END FINAL'
create_latest_symlink = false
[../]
[]
@@ -0,0 +1,12 @@
x,y,z,id,u
0,0.5,0,0,0
0.1,0.5,0,0.1,0.078663617984875
0.2,0.5,0,0.2,0.15936788915787
0.3,0.5,0,0.3,0.24397172256484
0.4,0.5,0,0.4,0.33398168255287
0.5,0.5,0,0.5,0.43040493832597
0.6,0.5,0,0.6,0.53363981759493
0.7,0.5,0,0.7,0.64341855974351
0.8,0.5,0,0.8,0.75881470939601
0.9,0.5,0,0.9,0.87832172561654
1,0.5,0,1,1
68 changes: 68 additions & 0 deletions test/tests/outputs/csv_final_and_latest/latest.i
@@ -0,0 +1,68 @@
[Mesh]
type = GeneratedMesh
dim = 2
nx = 10
ny = 10
[]

[Variables]
[./u]
[../]
[]

[Kernels]
[./diff]
type = CoefDiffusion
variable = u
coef = 0.1
[../]
[./time]
type = TimeDerivative
variable = u
[../]
[]

[BCs]
[./left]
type = DirichletBC
variable = u
boundary = left
value = 0
[../]
[./right]
type = DirichletBC
variable = u
boundary = right
value = 1
[../]
[]

[Executioner]
type = Transient
num_steps = 10
dt = 0.25
solve_type = PJFNK
petsc_options_iname = '-pc_type -pc_hypre_type'
petsc_options_value = 'hypre boomeramg'
[]

# Vector Postprocessor System
[VectorPostprocessors]
[./line_sample]
type = LineValueSampler
execute_on = 'timestep_end'
variable = 'u'
start_point = '0 0.5 0'
end_point = '1 0.5 0'
num_points = 11
sort_by = id
[../]
[]

[Outputs]
[./out]
type = CSV
execute_on = 'TIMESTEP_END'
create_final_symlink = false
[../]
[]
50 changes: 50 additions & 0 deletions test/tests/outputs/csv_final_and_latest/tests
@@ -0,0 +1,50 @@
[Tests]
issues = "#11087"
design = "/VectorPostprocessors/index.md /CSV.md"
[final]
type = CSVDiff
input = final.i
csvdiff = final_out_line_sample_FINAL.csv
requirement = "The CSV output object shall create a symlink to the final output with "
"with a '_FINAL' suffix for VectorPostprocessor data when there are execute flags "
"in addition to FINAL."
[]
[final_only]
type = CheckFiles
input = final.i
cli_args = Outputs/out/execute_on=FINAL
check_not_exists = 'final_out_line_sample_0009.csv'
check_files = 'final_out_line_sample_0011.csv final_out_line_sample_FINAL.csv'
requirement = "The CSV output object shall create a symlink to the final output with "
"with a '_FINAL' suffix for VectorPostprocessor data when the execute flag is "
"set to FINAL."
prereq = final
[]
[no_link]
type = CheckFiles
input = final.i
cli_args = Outputs/out/create_final_symlink=false
delete_output_before_running = false
check_not_exists = 'final_out_line_sample_FINAL.csv'
requirement = "The CSV output object 'create_final_symlink' parameter shall be able to disable "
"the creation of the final symlink."
prereq = final_only
[]
[latest]
type = CSVDiff
input = latest.i
csvdiff = latest_out_line_sample_LATEST.csv
requirement = "The CSV output object shall create a symlink to the most recent output with "
"with a '_LATEST' suffix for VectorPostprocessor data."
[]
[no_latest]
type = CheckFiles
input = latest.i
delete_output_before_running = false
cli_args = Outputs/out/create_latest_symlink=false
check_not_exists = 'latest_out_line_sample_LATEST.csv'
requirement = "The CSV output object 'create_latest_symlink' parameter shall be able to disable "
"the creation of the latest symlink."
prereq = latest
[]
[]

0 comments on commit 28c3afd

Please sign in to comment.