Fix ct2ctml when string passed as 'source' argument

Make ct2ctml create a temp cti file for a large source argument

Fixes #416
This commit is contained in:
Santosh Shanbhogue 2016-12-23 22:31:56 -05:00 committed by Ray Speth
parent e515afd9e1
commit 3593fad14a
2 changed files with 83 additions and 0 deletions

View file

@ -459,3 +459,33 @@ class CtmlConverterTest(utilities.CanteraTest):
self.assertNear(R.orders.get('OH'), 0.15)
self.assertTrue(R.allow_negative_orders)
self.assertNear(R.orders.get('H2'), -0.25)
def test_long_source_input(self):
"""
Here we are testing if passing a very long string will result in a
Solution object. This should result in a temp file creation in most OS's
"""
gas = ct.Solution(pjoin(self.test_data_dir, 'pdep-test.cti'))
with open(pjoin(self.test_data_dir, 'pdep-test.cti'), 'r') as f:
data = f.read()
data_size_2048kB = data + ' '*2048*1024
gas2 = ct.Solution(source=data_size_2048kB)
self.assertEqual(gas.n_reactions, gas2.n_reactions)
def test_short_source_input(self):
"""
Here we are testing if passing a short string will result in a Solution
object. This should not result in a temp file creation in most OS's
"""
gas = ct.Solution(pjoin(self.test_data_dir, 'pdep-test.cti'))
with open(pjoin(self.test_data_dir, 'pdep-test.cti'), 'r') as f:
data = f.read()
data_size_32kB = data + ' '*18000
gas2 = ct.Solution(source=data_size_32kB)
self.assertEqual(gas.n_reactions, gas2.n_reactions)

View file

@ -11,12 +11,15 @@
#include "cantera/base/stringUtils.h"
#include "../../ext/libexecstream/exec-stream.h"
#include <cstdio>
#include <fstream>
#include <sstream>
#include <functional>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
using namespace std;
@ -79,6 +82,9 @@ void ct2ctml(const char* file, const int debug)
static std::string call_ctml_writer(const std::string& text, bool isfile)
{
std::string file, arg;
bool temp_file_created = false;
std::string temp_cti_file_name = std::tmpnam(nullptr);
if (isfile) {
file = text;
arg = "r'" + text + "'";
@ -87,6 +93,44 @@ static std::string call_ctml_writer(const std::string& text, bool isfile)
arg = "text=r'''" + text + "'''";
}
// If the user wants to convert a mechanism using a text passed via the
// source="""..."""
// argument in python, then we have to make sure that it is short enough
// to fit in the command line when routed to python as:
// python -c ...
// statement downstream in the code
// So, check the max size of a string that can be passed on the command line
// This is OS Specific. *nix systems have the sysconf() function that tells
// us the largest argument we can pass. Since such a function does not exist
// for Windows, we set a safe limit of 32 kB
#ifdef _WIN32
long int max_argv_size = 32768;
#else
long int max_argv_size = sysconf(_SC_ARG_MAX);
#endif
if (text.size() > static_cast<size_t>(max_argv_size) - 500) {
// If the file is too big to be passed as a command line argument later
// in the file, then create a temporary file and execute this function
// as though an input file was specified as the source.
// We assume the text passed + 500 chars = total size of argv
ofstream temp_cti_file(temp_cti_file_name);
if (temp_cti_file) {
temp_cti_file << text;
file = temp_cti_file_name;
arg = "r'" + file + "'";
temp_file_created = true;
} else {
// If we are here, then a temp file could not be created
throw CanteraError("call_ctml_writer", "Very long source argument. "
"Error creating temporary file");
}
}
#ifdef HAS_NO_PYTHON
//! Section to bomb out if python is not present in the computation
//! environment.
@ -170,6 +214,15 @@ static std::string call_ctml_writer(const std::string& text, bool isfile)
message << "--------------- end of converter log ---------------\n";
writelog(message.str());
}
if (temp_file_created) {
// A temp file was created and has to be removed
bool status = std::remove(temp_cti_file_name.c_str());
if (status) {
writelog("WARNING: Error removing tmp file {}\n", temp_cti_file_name);
}
}
return python_output;
}