Skip to content

Commit

Permalink
Fix ct2ctml when string passed as 'source' argument
Browse files Browse the repository at this point in the history
Make ct2ctml create a temp cti file for a large source argument

Fixes #416
  • Loading branch information
santoshshanbhogue authored and speth committed Dec 30, 2016
1 parent e515afd commit 3593fad
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 0 deletions.
30 changes: 30 additions & 0 deletions interfaces/cython/cantera/test/test_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,3 +459,33 @@ def test_reaction_orders(self):
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)
53 changes: 53 additions & 0 deletions src/base/ct2ctml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 + "'";
Expand All @@ -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.
Expand Down Expand Up @@ -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;
}

Expand Down

0 comments on commit 3593fad

Please sign in to comment.