Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions Printers/include/gambit/Printers/printers/asciiprinter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,13 @@ namespace Gambit
// write the printer buffer to file
void dump_buffer(bool force=false);

// retrieve the name of the main output file (used by auxilliary printers to match the names)
// actual on-disk path of the main output file (includes any per-rank suffix)
std::string get_output_filename();

// output filename before the per-rank MPI suffix; queried by
// auxiliary printers to avoid double-appending the rank.
std::string get_base_output_filename();

// retrieve the bufferlength (used by auxilliary printers to match the primary printer)
int get_bufferlength();

Expand Down Expand Up @@ -170,9 +174,12 @@ namespace Gambit
void template_print_vec(std::vector<T> const&, const std::string&, const int, const uint, const ulong);

private:
/// Output file
/// Output file (on-disk path; includes the per-rank suffix under MPI)
std::string output_file;

/// Output filename before the per-rank MPI suffix
std::string base_output_file;

/// Info file (describes contents of output file, i.e. contents of columns)
std::string info_file;

Expand Down Expand Up @@ -214,8 +221,8 @@ namespace Gambit
uint mpiSize;
#endif

/// Number of digits of precision to use in output columns
int precision = 10;
/// Number of digits of precision to use in output columns (YAML 'precision', default 10)
int precision;

/// Full buffer of output to be printed
// Key is <int rank, int pointID>; value is a Record (for a single model point)
Expand All @@ -234,6 +241,10 @@ namespace Gambit
std::map<int,std::vector<std::string>> label_record; //the 'int' here is the vertex ID. Could make a typedef to make this safer.
bool info_file_written = false; // Flag to let us know that the info file has been written

/// Write a '#'-prefixed shorthand-label header line at the top of
/// the data file (YAML 'write_header', default false)
bool write_header = false;
bool header_written = false;
};

// Register printer so it can be constructed via inifile instructions
Expand Down
106 changes: 98 additions & 8 deletions Printers/src/printers/asciiprinter/asciiprinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ namespace Gambit
// Common constructor tasks
void asciiPrinter::common_constructor(const Options& options)
{
// Pick up the resume flag set by PrinterManager. BasePrinter does not
// initialise BaseBasePrinter::resume, so without this any later call
// to get_resume() would read uninitialised memory.
set_resume(options.getValue<bool>("resume"));

if( this->is_auxilliary_printer() ) // check if this is an auxilliary printer
{

Expand All @@ -93,9 +98,10 @@ namespace Gambit
// Get primary printer (need to cast from BasePrinter type to asciiPrinter)
asciiPrinter* primary = dynamic_cast<asciiPrinter*>(this->get_primary_printer());

// Name files based on the primary printer filenames
// Use the primary's pre-rank filename, otherwise the MPI block
// below would append the rank a second time.
std::ostringstream f;
f << primary->get_output_filename() << "_" << printer_name;
f << primary->get_base_output_filename() << "_" << printer_name;
output_file = Utils::ensure_path_exists(options.getValueOrDef<std::string>(f.str(),"output_file"));

// Match the buffer length to the primary printer, or use a user-supplied option
Expand All @@ -120,6 +126,9 @@ namespace Gambit
bufferlength = options.getValueOrDef<uint>(100,"buffer_length");
}

// Snapshot the pre-rank-suffix name for auxiliary printers to query.
base_output_file = output_file;

// Name "info" file to match "output" file
std::ostringstream finfo;
finfo<< output_file <<"_info";
Expand All @@ -144,6 +153,22 @@ namespace Gambit
info_file = finfo2.str();
#endif

// Refuse to overwrite a pre-existing output or info file unless the
// user has set 'delete_file_on_restart: true', or we are resuming.
bool overwrite_file = options.getValueOrDef<bool>(false,"delete_file_on_restart");
for(const std::string& f : {output_file, info_file})
{
if(Utils::file_exists(f) and not overwrite_file and not get_resume())
{
std::ostringstream errmsg;
errmsg << "Refusing to overwrite pre-existing asciiPrinter output file '"<<f<<"'. Please take one of the following actions:" << std::endl
<< " 1. Set 'delete_file_on_restart: true' in the Printer options of your input YAML file to give GAMBIT permission to automatically overwrite this file;" << std::endl
<< " 2. Choose a different output filename via the 'output_file' printer option;" << std::endl
<< " 3. Manually move or delete the existing file '"<<f<<"'.";
printer_error().raise(LOCAL_INFO, errmsg.str());
}
}

// Erase contents of output_file and info_file if they already exist
std::ofstream output;
open_output_file(output, output_file, std::ofstream::trunc);
Expand All @@ -158,6 +183,7 @@ namespace Gambit
asciiPrinter::asciiPrinter(const Options& options, BasePrinter* const primary)
: BasePrinter(primary,options.getValueOrDef<bool>(false,"auxilliary"))
, output_file("")
, base_output_file("")
, info_file("")
, bufferlength(100)
, global(false)
Expand All @@ -166,13 +192,27 @@ namespace Gambit
, myComm() // attaches to MPI_COMM_WORLD, beware collisions with e.g. scanning algorithms.
, mpiSize(1)
#endif
, precision(10)
, lastPointID(nullpoint)
{
common_constructor(options);

// Choose whether or not to print invalid and suspicious point codes
print_suspicious_point_code = options.getValueOrDef<bool>(true,"print_suspicious_point_code");
print_invalidation_code = options.getValueOrDef<bool>(true,"print_invalidation_code");

// Number of digits of precision to use in output columns
precision = options.getValueOrDef<int>(10,"precision");
if(precision < 0)
{
std::ostringstream errmsg;
errmsg << "Invalid value for asciiPrinter option 'precision': " << precision
<< ". Must be a non-negative integer (number of digits to use for std::setprecision).";
printer_error().raise(LOCAL_INFO, errmsg.str());
}

// Optional '#'-prefixed shorthand-label header line at top of data file.
write_header = options.getValueOrDef<bool>(false,"write_header");
}


Expand Down Expand Up @@ -243,8 +283,9 @@ namespace Gambit
}

// getters for internal variables
std::string asciiPrinter::get_output_filename() { return output_file; }
int asciiPrinter::get_bufferlength() { return bufferlength; }
std::string asciiPrinter::get_output_filename() { return output_file; }
std::string asciiPrinter::get_base_output_filename() { return base_output_file; }
int asciiPrinter::get_bufferlength() { return bufferlength; }

// add results to printer buffer
void asciiPrinter::addtobuffer(const std::vector<double>& functor_data, const std::vector<std::string>& functor_labels, const int vID, const int rank, const int pointID)
Expand Down Expand Up @@ -453,6 +494,24 @@ namespace Gambit
printer_error().raise(LOCAL_INFO,errmsg.str());
}

// Single column width, shared by the header line and data rows.
const int colwidth = precision + 13;

// Shorthand column name: substring after the last "::" (or the whole
// label), with whitespace replaced by '_' and prefixed with the
// column number to disambiguate collisions.
auto shorthand = [](const std::string& full, int col_index_1based)
{
std::string suffix;
std::size_t pos = full.rfind("::");
if(pos != std::string::npos) suffix = full.substr(pos + 2);
else suffix = full;
for(char& c : suffix) if(c == ' ' || c == '\t') c = '_';
std::ostringstream out;
out << col_index_1based << "_" << suffix;
return out.str();
};

// Write the file explaining what is in each column of the output file
if (info_file_written==false)
{
Expand All @@ -467,7 +526,7 @@ namespace Gambit
{
int vID = it->first;
int length = it->second; // slots reserved in output file for these results

for (int i=0; i<length; i++)
{
AP_DBUG( std::cout<<"Column "<<column_index<<": "<<label_record.at(vID)[i]<<std::endl; )
Expand All @@ -479,6 +538,32 @@ namespace Gambit
info_file_written=true;
}

// Optional shorthand-label header line. The '#' marks it as a comment
// for numpy/pandas/gnuplot. A space prefix (or the '#' for the first
// column) is emitted before each label so columns stay whitespace-
// separated even when a label is wider than colwidth.
if (write_header && !header_written)
{
AP_DBUG( std::cout << "asciiPrinter: Writing data-file header line..." << std::endl; )
my_fstream << "#";
int column_index = 1;
for (std::map<int,int>::iterator
it = lineindexrecord.begin(); it != lineindexrecord.end(); it++)
{
int vID = it->first;
int length = it->second;
for (int i = 0; i < length; i++)
{
if (column_index > 1) my_fstream << " ";
my_fstream << std::setw(colwidth - 1)
<< shorthand(label_record.at(vID)[i], column_index);
column_index++;
}
}
my_fstream << std::endl;
header_written = true;
}

// Actual dump of buffer to file
for (Buffer::iterator
bufentry = buffer.begin(); bufentry != buffer.end(); /* Will increment in loop */ )
Expand Down Expand Up @@ -520,8 +605,8 @@ namespace Gambit
default_value = stream.str();
}

// Print to the fstream!
int colwidth = precision + 8; // Just kind of guessing here; tweak as needed
// Print to the fstream. Same field width for numeric values and
// 'none'/default placeholders so columns align across rows.
for (uint j=0;j<length;j++)
{
if(j>=results->size())
Expand All @@ -532,7 +617,7 @@ namespace Gambit
else
{
// print an entry from the results vector
my_fstream<<std::setw(colwidth+5)<<std::scientific<<(*results)[j];
my_fstream<<std::setw(colwidth)<<std::scientific<<(*results)[j];
}
}
}
Expand All @@ -559,6 +644,11 @@ namespace Gambit
// Print metadata info to file
void asciiPrinter::_print_metadata(map_str_str metadata)
{
// Only rank 0 writes; metadata_file is not rank-suffixed, so writes
// from other ranks would interleave. Setup metadata is identical
// across ranks, and lastPointID is only used by (unimplemented) resume.
if(getRank() != 0) return;

// Open metadata file in append mode
std::ofstream metadata_fstream;
open_output_file(metadata_fstream, metadata_file, std::ofstream::app);
Expand Down
5 changes: 3 additions & 2 deletions yaml_files/spartan.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ Printer:
# options:
# output_file: "results.dat"
# buffer_length: 10
# precision: 10
# write_header: true
# delete_file_on_restart: true
# print_debug_data: true

# printer: none

Expand Down Expand Up @@ -423,7 +424,7 @@ KeyValues:
generator: ranlux48
seed: -1

print_timing_data: true
print_timing_data: false

print_unitcube: true

Expand Down
Loading