| /* Copyright 2015. Los Alamos National Security, LLC. This material was produced |
| * under U.S. Government contract DE-AC52-06NA25396 for Los Alamos National |
| * Laboratory (LANL), which is operated by Los Alamos National Security, LLC |
| * for the U.S. Department of Energy. The U.S. Government has rights to use, |
| * reproduce, and distribute this software. NEITHER THE GOVERNMENT NOR LOS |
| * ALAMOS NATIONAL SECURITY, LLC MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR |
| * ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified |
| * to produce derivative works, such modified software should be clearly marked, |
| * so as not to confuse it with the version available from LANL. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy |
| * of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed |
| * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR |
| * CONDITIONS OF ANY KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations under the License. |
| * |
| * Under this license, it is required to include a reference to this work. We |
| * request that each derivative work contain a reference to LANL Copyright |
| * Disclosure C15076/LA-CC-15-054 so that this work's impact can be roughly |
| * measured. |
| * |
| * This is LANL Copyright Disclosure C15076/LA-CC-15-054 |
| */ |
| |
| /* |
| * PowerParser is a general purpose input file parser for software applications. |
| * |
| * Authors: Chuck Wingate XCP-2 caw@lanl.gov |
| * Robert Robey XCP-2 brobey@lanl.gov |
| */ |
| |
| // *************************************************************************** |
| // *************************************************************************** |
| // Restart Blocks |
| // Run the code until a restart block condition is satisfied. Set the restart |
| // block as active, write a restart dump, stop the code, and restart. |
| // *************************************************************************** |
| // *************************************************************************** |
| #include <iostream> |
| #include <iomanip> |
| #include <fstream> |
| #include <string> |
| #include <vector> |
| #include <deque> |
| #include <sstream> |
| #include <map> |
| #include <math.h> |
| |
| #include "Variable.hh" |
| #include "Function.hh" |
| #include "Word.hh" |
| #include "Parser_math.hh" |
| #include "Cmd.hh" |
| #include "Restartblock.hh" |
| |
| namespace PP |
| { |
| using std::cout; |
| using std::endl; |
| using std::string; |
| using std::deque; |
| using std::vector; |
| using std::stringstream; |
| using std::pair; |
| using std::ifstream; |
| using std::ios; |
| |
| |
| // =========================================================================== |
| // Default constructor. |
| // =========================================================================== |
| Restartblock::Restartblock() |
| { |
| active = -1; |
| } |
| |
| // =========================================================================== |
| // Usual constructor. |
| // restart_block name (time .eq. 50) then |
| // =========================================================================== |
| Restartblock::Restartblock(int &nrb, Cmd &cmdi, bool &skiprb, |
| bool &single_line_rb, |
| deque<string> &bnames_on_dump, |
| deque<bool> &baflags_on_dump, |
| deque<int> &rbsatprb_on_dump, |
| deque<bool> &rbsat_on_dump, |
| stringstream &serr, int &ierr) |
| { |
| //cout << "&&&&&cw ********** Restartblock.cc, Enter Constructor **********" << endl; |
| active = -1; |
| nrb += 1; |
| skiprb = true; |
| single_line_rb = false; |
| int nwords = cmdi.get_nwords(); |
| |
| // &&&&&cw |
| //stringstream ssprint; |
| //cmdi.print_using_words(ssprint); |
| //cout << ssprint.str() << endl; |
| |
| if (nwords < 8) { |
| cmdi.fatal_error(0, serr, ierr); |
| serr << "A restart block line must have at least 8 words on it (the " |
| << endl |
| << "opening and closing parentheses each count as a word)" |
| << endl; |
| serr << "This restart block command only has " << nwords << |
| " words on it." << endl; |
| serr << "Expected something like (this has 8 words):" << endl; |
| serr << " restart_block after5 (time .gt. 5) then" << endl; |
| serr << "Or perhaps a single line restart block like (this has 13 words):" |
| << endl; |
| serr << " restart_block after5 (time .gt. 5) sizemat(2) = 0.005" << endl; |
| ierr = 2; |
| return; |
| } |
| |
| string p2 = cmdi.get_string(2); |
| if (p2 != "(") { |
| cmdi.fatal_error(2, serr, ierr); |
| serr << "Expected an open parentheses following the restart block name.." |
| << endl; |
| serr << "Instead found " << p2 << " following the restart block name." |
| << endl; |
| serr << "The restart block command should be something like:" << endl; |
| serr << " restart_block t_is_gt_5 (time .gt. 5) then" << endl; |
| serr << "Or perhaps a single line restart block like:" << endl; |
| serr << " restart_block t_is_gt_5 (time .gt. 5) sizemat(2) = 0.005" << endl; |
| ierr = 2; |
| return; |
| } |
| |
| |
| for (int i=1; i<nwords-1; i++) { |
| string t1 = cmdi.get_string(i); |
| if (t1 == "then") { |
| cmdi.fatal_error(i, serr, ierr); |
| serr << "Found a then keyword embedded in the restart_block command." |
| << endl; |
| serr << "If a then keyword is present it must be the last " |
| "word on the line." << endl; |
| serr << "The restart_block command should be something like:" << endl; |
| serr << " restart_block t_is_gt_5 (time .gt. 5) then" << endl; |
| serr << "Or perhaps a single line restart block like:" << endl; |
| serr << " restart_block t_is_gt_5 (time .gt. 5) sizemat(2) = 0.005" << endl; |
| ierr = 2; |
| return; |
| } |
| } |
| |
| |
| // Find the closing parenthesis |
| int close_paren_dex = -1; |
| for (int i=2; i<nwords; i++) { |
| string pi = cmdi.get_string(i); |
| if (pi == "then") break; |
| if (pi == ")") { |
| close_paren_dex = i; |
| break; |
| } |
| } |
| |
| if (close_paren_dex == -1) { |
| cmdi.fatal_error(0, serr, ierr); |
| serr << "Expected a close parentheses following the condition." |
| << endl; |
| serr << "Did not find a close parentheses." << endl; |
| serr << "The restart_block command should be something like:" << endl; |
| serr << " restart_block t_is_gt_5 (time .gt. 5) then" << endl; |
| serr << "Or perhaps a single line restart block like:" << endl; |
| serr << " restart_block t_is_gt_5 (time .gt. 5) sizemat(2) = 0.005" << endl; |
| ierr = 2; |
| return; |
| } |
| |
| |
| int nw = close_paren_dex - 3; |
| if ((nw+1)%4 != 0) { |
| cmdi.fatal_error(0, serr, ierr); |
| serr << "Wrong number of words in the restart_block condition." |
| << endl; |
| serr << "The number of words in this condition is " << nw << endl; |
| serr << "The number of words + 1 should be a multiple of 4." << endl; |
| serr << "The condition should be something like:" << endl; |
| serr << " time .gt. 5" << endl; |
| serr << "This has 3 words and 3+1 is a multiple of 4." << endl; |
| serr << "Or the following is valid" << endl; |
| serr << " time .gt. 5 .and. ncycle .ge. 10" << endl; |
| serr << "This has 7 words and 7+1 is a multiple of 4." << endl; |
| ierr = 2; |
| return; |
| } |
| |
| |
| |
| |
| for (int i=3; i<close_paren_dex; i+=4) { |
| add_word(cmdi, i, varname); |
| add_word(cmdi, i+1, relation); |
| add_word(cmdi, i+2, value); |
| |
| if (i+3 < close_paren_dex) add_word(cmdi, i+3, logop); |
| else add_word(cmdi, i+3, logop, "none"); |
| |
| satisfied.push_back("false"); |
| } |
| |
| |
| // Check to make sure that the relation is valid. |
| for (int n=0; n<(int)varname.size(); n++) { |
| bool valid_relation = false; |
| if (relation[n].get_string() == ".hglt.") valid_relation = true; |
| if (relation[n].get_string() == ".hgle.") valid_relation = true; |
| if (relation[n].get_string() == ".hgeq.") valid_relation = true; |
| if (relation[n].get_string() == ".hgne.") valid_relation = true; |
| if (relation[n].get_string() == ".hggt.") valid_relation = true; |
| if (relation[n].get_string() == ".hgge.") valid_relation = true; |
| if (relation[n].get_string() == ".lt.") valid_relation = true; |
| if (relation[n].get_string() == ".le.") valid_relation = true; |
| if (relation[n].get_string() == ".eq.") valid_relation = true; |
| if (relation[n].get_string() == ".ne.") valid_relation = true; |
| if (relation[n].get_string() == ".gt.") valid_relation = true; |
| if (relation[n].get_string() == ".ge.") valid_relation = true; |
| |
| if (!valid_relation) { |
| relation[n].fatal_error(serr, ierr); |
| serr << "Invalid restart_block relation." << endl; |
| serr << "Expected .lt., .le., .eq., .ne., .gt., .ge." << endl; |
| serr << "Also could be .hglt., .hgle., .hgeq., .hgne., .hggt., .hgge." << endl; |
| serr << "Instead found relation: " << relation[n].get_string() << endl; |
| ierr = 2; |
| return; |
| } |
| } |
| |
| // The name of the restart block is the second word on the |
| // restart_block command. |
| name = cmdi.get_string(1); |
| //cout << "&&&&&cw Restartblock.cc, name1 = " << name << endl; |
| //cout << "&&&&&cw Restartblock.cc, satsize = " << satisfied.size() << endl; |
| |
| // If this is a restart, then restart block names and active flags |
| // could be stored on the restart dump. If this restart block matches |
| // any stored on the dump, then set the active flag to what is on |
| // the dump. |
| for (int i=0; i<(int)bnames_on_dump.size(); i++) { |
| //cout << "&&&&&cw Restartblock.cc, name = " << name << endl; |
| //cout << "&&&&&cw Restartblock.cc, bnames_on_dump = " << |
| // bnames_on_dump[i] << endl; |
| //cout << "&&&&&cw Restartblock.cc, baflags_on_dump = " << |
| // baflags_on_dump[i] << endl; |
| if (name == bnames_on_dump[i]) { |
| active = 0; |
| if (baflags_on_dump[i]) active = 1; |
| |
| int satdex = 0; |
| for (int j=0; j<i; j++) { |
| satdex += rbsatprb_on_dump[j]; |
| } |
| for (int j=satdex; j<satdex+rbsatprb_on_dump[i]; j++) { |
| string s = "false"; |
| if (rbsat_on_dump[j]) s = "true"; |
| satisfied[j-satdex] = s; |
| } |
| |
| break; |
| } |
| } |
| //cout << "&&&&&cw Restartblock.cc, after set restart" << endl; |
| |
| |
| // If this restart block is active, that means we want to process |
| // the commands in the block, therefore we set the skip flag to false. |
| if (active == 1) skiprb = false; |
| |
| |
| // Set the has gotten to flags. |
| for (int n=0; n<(int)varname.size(); n++) { |
| bool hg = false; |
| |
| if (relation[n].get_string() == ".hglt.") { |
| hg = true; |
| relation[n].set_value(".lt."); |
| } |
| else if (relation[n].get_string() == ".hgle.") { |
| hg = true; |
| relation[n].set_value(".le."); |
| } |
| else if (relation[n].get_string() == ".hgeq.") { |
| hg = true; |
| relation[n].set_value(".eq."); |
| } |
| else if (relation[n].get_string() == ".hgne.") { |
| hg = true; |
| relation[n].set_value(".ne."); |
| } |
| else if (relation[n].get_string() == ".hggt.") { |
| hg = true; |
| relation[n].set_value(".gt."); |
| } |
| else if (relation[n].get_string() == ".hgge.") { |
| hg = true; |
| relation[n].set_value(".ge."); |
| } |
| |
| |
| has_got.push_back(hg); |
| } |
| |
| |
| // Handle single line restart_block |
| if (cmdi.get_string(nwords-1) != "then") { |
| single_line_rb = true; |
| cmdi.delete_words(0, 5); |
| cmdi.reset_name_type(); |
| } |
| //cout << "&&&&&cw ********** Restartblock.cc, Exit Constructor **********" << endl; |
| } |
| |
| |
| // =========================================================================== |
| // Add word to the deque wq. |
| // =========================================================================== |
| void Restartblock::add_word(Cmd &cmdi, int idex, deque<Word> &wq) |
| { |
| int ln = cmdi.get_line_number(idex); |
| int file_ln = cmdi.get_file_line_number(idex); |
| string fname = cmdi.get_filename(idex); |
| deque<string> *lines = cmdi.get_lines(); |
| Word w(cmdi.get_string(idex), ln, file_ln, fname, lines); |
| wq.push_back(w); |
| } |
| |
| void Restartblock::add_word(Cmd &cmdi, int idex, deque<Word> &wq, string sadd) |
| { |
| int ln = cmdi.get_line_number(idex); |
| int file_ln = cmdi.get_file_line_number(idex); |
| string fname = cmdi.get_filename(idex); |
| deque<string> *lines = cmdi.get_lines(); |
| Word w(sadd, ln, file_ln, fname, lines); |
| wq.push_back(w); |
| } |
| |
| |
| // =========================================================================== |
| // This is the check for when the condition is satisfied. |
| // =========================================================================== |
| void Restartblock::check_rb(vector<string> &code_varnames, |
| vector<string> &code_values, |
| vector<int> &vv_active, int *rbci, |
| stringstream &serr, int &ierr) |
| { |
| *rbci = 0; |
| //if (active==1) return; |
| |
| Parser_math pmath; |
| |
| deque<Word> wordsf; |
| |
| bool skip_sat = false; |
| int num_sub_cond = (int)varname.size(); |
| for (int n=0; n<num_sub_cond; n++) { |
| deque<Word> words; |
| |
| if (satisfied[n] == "true") { |
| int ln = varname[n].get_line_number(); |
| int file_ln = varname[n].get_file_line_number(); |
| string fname = varname[n].get_filename(); |
| deque<string> *lines = varname[n].get_lines(); |
| Word w("true", ln, file_ln, fname, lines); |
| words.push_back(w); |
| } |
| else { |
| words.push_back(varname[n]); |
| words.push_back(relation[n]); |
| words.push_back(value[n]); |
| |
| process_words(words, code_varnames, code_values, vv_active, |
| serr, ierr); |
| |
| if (has_got[n]) { |
| if (words[0].get_bool(serr, ierr)) { |
| bool doit = true; |
| if (n > 0) { |
| if (logop[n-1].get_string() == ".andthen." && skip_sat) { |
| doit = false; |
| } |
| } |
| if (doit) satisfied[n] = "true"; |
| } |
| else { |
| skip_sat = true; |
| } |
| } |
| } |
| |
| wordsf.push_back(words[0]); |
| if (logop[n].get_string() == "none") break; |
| else wordsf.push_back(logop[n]); |
| } |
| |
| process_words(wordsf, code_varnames, code_values, vv_active, |
| serr, ierr); |
| |
| // rbci is an output flag telling the code to write a dump and end |
| // the calculation or not. rbci=0 means do not end the calc, |
| // rbci=1 tells the code to end the calc. |
| // Basically, if the condition changes from its previous value, then |
| // set rbci to 1. |
| |
| // This is the current value of the condition that was calculated above. |
| bool b = wordsf[0].get_bool(serr, ierr); |
| |
| // *rcbi is the key output result from this function |
| // *rbci = 0 Calling code does nothing |
| // *rbci = 1 Calling code stops calculation, normally does restart |
| *rbci = 0; |
| |
| // Here we check to see if the condition has changed, i.e. is b different |
| // from the active flag. If so, then we end the calculation. |
| // When the restart block is first created, the active flag is set to -1, |
| // this is for runs from scratch. |
| // If this is a restart, then the active flag will come from the dump and |
| // be either 0 or 1. |
| // So if active is -1 and the condition is true, then we end the calculation |
| // right away (this should not be common, but could happen). |
| // |
| // Changed on 7/2/10 - The original idea for restart blocks was that they |
| // would trigger when the condition changed from false to true. But they |
| // would also trigger when the condition changed back from true to false. |
| // This causes problems for the users when the restart block would |
| // repeatedly trigger because the condition oscillates between true and |
| // false. Therefore, change the restart blocks so they trigger once and |
| // only once (which happens when the condition first becomes true). If |
| // the users ever need a restart block that also triggers when the |
| // condition changes from true to false, then some |
| // sort of option could be put in to allow this. |
| if (b && active == -1) { *rbci = 1; active = 1; return; } // Trigger |
| if (b && active == 0) { *rbci = 1; active = 1; return; } // Trigger |
| if (b && active == 1) { *rbci = 0; return; } // Do nothing |
| if (!b && active == -1) { *rbci = 0; active = 0; return; } // Do nothing |
| if (!b && active == 0) { *rbci = 0; return; } // Do nothing |
| |
| // This is the true to false trigger that causes problems. |
| //if (!b && active == 1) { *rbci = 1; active = 0; return; } // Trigger |
| } |
| |
| |
| |
| // =========================================================================== |
| // Given a deque of words, go through them evaluating relational and logical |
| // operators. The words should evaluate to one final word. |
| // =========================================================================== |
| void Restartblock::process_words(deque <Word> &words, vector<string> &code_varnames, |
| vector<string> &code_values, |
| vector<int> &vv_active, |
| stringstream &serr, int &ierr) |
| { |
| Parser_math pmath; |
| |
| // Replace any code vars with their values. |
| int i2 = (int)words.size(); |
| for (int i=0; i<i2; i++) { |
| for (int j=0; j<(int)code_varnames.size(); j++) { |
| if (words[i].get_string() == code_varnames[j]) { |
| int ln = words[i].get_line_number(); |
| int file_ln = words[i].get_file_line_number(); |
| string fname = words[i].get_filename(); |
| deque<string> *lines = words[i].get_lines(); |
| if (vv_active[j] == 0) { |
| Word wj("false", ln, file_ln, fname, lines); |
| replace_words(i, i+2, words, wj); |
| i2 -= 2; |
| break; |
| } |
| else { |
| Word wj(code_values[j], ln, file_ln, fname, lines); |
| words[i] = wj; |
| } |
| } |
| } |
| } |
| |
| int i1 = 0; |
| i2 = (int)words.size() - 1; |
| for (int level=6; level>=0; level--) { |
| for (int i=i1; i<=i2; i+=1) { |
| if (words[i].is_operator(level)) { |
| int ln = words[i].get_line_number(); |
| int file_ln = words[i].get_file_line_number(); |
| string fname = words[i].get_filename(); |
| deque<string> *lines = words[i].get_lines(); |
| Word w("", ln, file_ln, fname, lines); |
| |
| string op_type = words[i].get_op_type(); |
| |
| if (op_type == "relational") { |
| pmath.do_op_relational(i-1, i, i+1, words, w, serr, ierr); |
| } |
| |
| if (op_type == "logical" && level == 2) // .not. is unary |
| pmath.do_op_not(i, i+1, words, w, serr, ierr); |
| |
| if (op_type == "logical" && level != 2) |
| pmath.do_op_logical(i-1, i, i+1, words, w, serr, ierr); |
| |
| // level 2, .not., is unary and is handled differently. |
| if (level == 2) { |
| replace_words(i, i+1, words, w); |
| i2 -= 1; |
| } |
| else { |
| replace_words(i-1, i+1, words, w); |
| i2 -= 2; |
| i -= 1; |
| } |
| continue; |
| } |
| } |
| } |
| |
| // The condition has to evaluate to a single boolean value. |
| if ((int)words.size() != 1) { |
| words[0].fatal_error(serr, ierr); |
| serr << "restart_block condition did not evaluate to a single boolean value." |
| << endl; |
| serr << "Fix the restart_block condition" << endl; |
| ierr = 2; |
| } |
| } |
| |
| |
| // =========================================================================== |
| // List the condition for this restart block to a stringstream. |
| // This is done to let the user indentify this restart block. It is |
| // also useful for debugging. |
| // =========================================================================== |
| void Restartblock::list_condition(string offset1, string offset2, |
| stringstream &ssc) |
| { |
| for (int n=0; n<(int)varname.size(); n++) { |
| |
| string relstr = relation[n].get_string(); |
| string rstr = relstr; |
| if (has_got[n]) { |
| if (relstr == ".lt.") rstr = ".hglt."; |
| if (relstr == ".le.") rstr = ".hgle."; |
| if (relstr == ".eq.") rstr = ".hgeq."; |
| if (relstr == ".ne.") rstr = ".hgne."; |
| if (relstr == ".gt.") rstr = ".hggt."; |
| if (relstr == ".ge.") rstr = ".hgge."; |
| } |
| relstr = rstr; |
| |
| string offset = offset1; |
| if (n > 0) offset = offset2; |
| |
| ssc << offset << varname[n].get_string() << " " |
| << relstr << " " << value[n].get_string(); |
| |
| if (logop[n].get_string() == "none") break; |
| ssc << " " << logop[n].get_string(); |
| ssc << endl; |
| } |
| } |
| |
| |
| // =========================================================================== |
| // Delete words i1 through i2 inclusive from the deque. |
| // =========================================================================== |
| void Restartblock::delete_words(int i1, int i2, deque <Word> &words) |
| { |
| deque<Word>::iterator p = words.begin(); |
| words.erase(p + i1, p + i2 + 1); |
| } |
| |
| |
| // =========================================================================== |
| // Replace words i1 through i2 inclusive with word w. |
| // =========================================================================== |
| void Restartblock::replace_words(int i1, int i2, deque <Word> &words, Word &w) |
| { |
| delete_words(i1, i2, words); |
| deque<Word>::iterator p = words.begin(); |
| words.insert(p + i1, w); |
| } |
| |
| |
| |
| } // End of the PP namespace |
| |
| |
| |