4
\$\begingroup\$

This is a function from a program I am working on for my internship, which involves research in computational science. I have some text files with reference data in a directory data/ with name e.g. AR00.1 that look like this:

90.000 73.629
120.00 98.338
150.00 123.00
180.00 147.64
210.00 172.28
...

The function works, with output as expected. I am fairly new to C++ and programming in general (had a little less than a year of C before I started with C++.) Am I doing things efficiently (i.e. loading all of the data into memory at once and calling it when needed) or is there a better way to access this data? The part that seems messiest to me is the nested vectors, though I can't think of another way to do them as I've no formal education in data structures.

In any case, here is a minimum example of the function with an arbitrary main function to show my usage. Thank you for taking the time to read and review this!

//includes go here

struct reference_data
{
    double temperature,
           volume_l_mol,
           volume_m3,
           compressibility;
};  

using namespace std;

vector<vector<vector<reference_data>>> read_reference_data(vector<string> species, vector<string> pressure_strings);

int main()
{
    vector<string> species = {"AR","CH4","CO2","H2","HE","KR","N2","NE","XE"};
    vector<string> pressures = {"00.1", "001", "005", "010", "020", "030"};

    vector<vector<vector<reference_data>>> NIST_data = read_reference_data(species,pressures);
    cout << NIST_data[0][0][0].temperature //90 for above data example
    return 0;
}

vector<vector<vector<reference_data>>> read_reference_data(vector<string> species, vector<string> pressure_strings)
    {
        vector<double> pressure_floats = {00.1, 001, 005, 010, 020, 030};
       // ALL->SPECIES->PRES->STATE POINTS
        vector<vector<vector<reference_data>>> NIST_data;
        for(unsigned long species_ind = 0;species_ind<species.size();species_ind++)
        {
            //SPECIES->PRES->STATE POINTS
            vector<vector<reference_data>> this_species;
            string a_line;
            for(auto pressure_ind= 0; pressure_ind < pressure_strings.size(); pressure_ind++)
            {
                string file_name = "data/";
                file_name += species[species_ind];
                file_name += pressure_strings[pressure_ind];
                ifstream input(file_name);
                if(input.is_open())
                {
                    //PRES->STATE POINTS
                    vector<reference_data> this_pressure;
                    while (getline(input, a_line))
                    {
                        vector<string> this_line;
                        istringstream iss(a_line);
                        //copy the numbers of interest from the line into the vector this_line (thanks Doug / SO !)
                        copy(
                                istream_iterator<string>(iss),
                                istream_iterator<string>(),
                                back_inserter(this_line)
                        );
                        if(this_line.size())
                        {
                            //STATE_POINT (we made it!)
                            reference_data this_point;
                            this_point.temperature = atof(this_line[0].c_str());
                            this_point.volume_l_mol = atof(this_line[1].c_str());
                            double liters = this_point.volume_l_mol*MOLES;
                            this_point.volume_m3 = liters / 1000.0;
                            this_point.compressibility = get_compressibility(this_point.temperature,
                                                                             pressure_floats[pressure_ind],
                                                                             this_point.volume_m3);
                            this_pressure.push_back(this_point);//add current temperature to the pressure it corresponds to
                        }
                        else
                        {
                            cerr << "Data line in read_reference_data() is empty--check your input file"
                                 << endl;
                        }
                    }
                    this_species.push_back(this_pressure);//add current pressure to species it corresponds to
                }
                else
                {
                    cerr << "Error in opening file " << file_name
                         << " in read_reference_data()" << endl;
                }
                input.close();
            }
            NIST_data.push_back(this_species);//add current species to all data
        }
        return NIST_data;
    }
\$\endgroup\$
4
\$\begingroup\$

Instead of a 3D vector, I think you'll find a 2D map to be easier to use and maintain map<string,map<string,vector<reference_data>>>. This way the data is indexed by the actual strings you're using instead of generic index numbers where you'll need a legend to know which index applies to specific data.

Instead of getline when dealing with numeric data, it is much cleaner and simpler to use the extraction operator(>>), which will cast the data to the specific type of the variable you use:

double temperature = 0.0;
double volume = 0.0;
while(input >> temperature >> volume)
{
    //process data
}

The loop will run until EOF.

The whole algorithm might look something like this:

map<string, map<string, vector<reference_data>>> data;
vector<string> species = {
    "AR",
    "CH4",
    "CO2",
    "H2",
    "HE",
    "KR",
    "N2",
    "NE",
    "XE"
};
map<string, double> pressures ={
    {"00.1", 0.1},
    {"001", 1.0},
    {"005", 5.0},
    {"010", 10.0},
    {"020", 20.0},
    {"030", 30.0}
};
for (string s : species)
{
    for (auto p : pressures)
    {
        string fileName = "data/" + s + p.first;
        ifstream file(fileName);
        if (file.is_open())
        {
            double temperature = 0.0;
            double volume = 0.0;
            while (file >> temperature >> volume)
            {
                reference_data this_point;
                this_point.temperature = temperature;
                this_point.volume_l_mol = volume;
                double liters = this_point.volume_l_mol*MOLES;
                this_point.volume_m3 = liters / 1000.0;
                this_point.compressibility = get_compressibility(this_point.temperature,
                                                                 p.second,
                                                                 this_point.volume_m3);
                data[s][p.first].emplace_back(this_point);
            }
        }

    }
}
\$\endgroup\$
  • \$\begingroup\$ I think that cast is slightly rough word to use in this case. May be convert would be better? \$\endgroup\$ – Incomputable Jun 15 '17 at 21:41
  • \$\begingroup\$ In my experience cast generally applies more to operators, whereas convert applies more to specific functions. In this case since the conversion is done implicitly, I feel that cast is a more proper term for it. \$\endgroup\$ – tinstaafl Jun 15 '17 at 22:29
  • \$\begingroup\$ good point. How about extract then? :) \$\endgroup\$ – Incomputable Jun 15 '17 at 22:31
  • \$\begingroup\$ Seems kind of redundant to say that the extraction operator extracts data and doesn't make it plain that that data is actually changed by the operator, especially for someone who may not be familiar with how the extraction operator works. \$\endgroup\$ – tinstaafl Jun 15 '17 at 22:42
  • \$\begingroup\$ fair enough, upvoted. \$\endgroup\$ – Incomputable Jun 15 '17 at 22:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.