diff --git a/phreeqcpp/ChartHandler.cpp b/phreeqcpp/ChartHandler.cpp new file mode 100644 index 00000000..edee12d4 --- /dev/null +++ b/phreeqcpp/ChartHandler.cpp @@ -0,0 +1,227 @@ +// ChartHandler.cpp: implementation of the ChartHandler class. +// +////////////////////////////////////////////////////////////////////// +#if defined MULTICHART +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include "ChartHandler.h" +#include "phreeqc.h" +#include + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +ChartHandler::ChartHandler(PHRQ_io *io) +: +PHRQ_base(io) + // + // default constructor for ChartHandler + // +{ + current_chart = NULL; + current_chart_n_user = -1000; + u_g_defined = false; + timer = true; + active_charts = 0; +} + +ChartHandler::~ChartHandler() +{ + std::map::iterator it; + for (it = this->chart_map.begin(); it != chart_map.end(); it++) + { + delete it->second; + } +} +void +ChartHandler::Punch_user_graph(Phreeqc * phreeqc_ptr) +{ + std::map::iterator it = this->chart_map.begin(); + for ( ; it != chart_map.end(); it++) + { + if (it->second->Get_active()) + { +#if defined(__cplusplus_cli) + while (0 != System::Threading::Interlocked::CompareExchange(it->second->usingResource, 4, 0)) + { + System::Threading::Thread::Sleep(5); + } +#endif + try + { + this->current_chart = it->second; + phreeqc_ptr-> punch_user_graph(); + } + catch (...) + { +#if defined(__cplusplus_cli) + int n = System::Threading::Interlocked::Exchange(it->second->usingResource, 0); + assert(n == 4); +#endif + throw; + } +#if defined(__cplusplus_cli) + System::Threading::Interlocked::Exchange(it->second->usingResource, 0); +#endif + } + } +} + +bool +ChartHandler::Read(Phreeqc * phreeqc_ptr, CParser &parser) +{ + int n_user; + std::string token; + + // reads line, next character is after keyword + parser.check_line("ChartHandler", true, false, true, false); + + std::istringstream iss(parser.line()); + // keyword + iss >> token; + // number + if (!(iss >> n_user)) + { + n_user = 1; + } + + // makes new ChartObject if necessary + std::map::iterator it = this->chart_map.find(n_user); + if (it == this->chart_map.end()) + { + chart_map[n_user] = new ChartObject(this->Get_io()); + it = this->chart_map.find(n_user); + it->second->Set_phreeqc(phreeqc_ptr); + } + + // Read/update ChartObject +#if defined(__cplusplus_cli) + while (0 != System::Threading::Interlocked::CompareExchange(it->second->usingResource, 5, 0)) + { + System::Threading::Thread::Sleep(5); + } +#endif + try + { + { + it->second->Read(parser); + current_chart_n_user = n_user; + current_chart = it->second; + u_g_defined = true; + } + + // if detached, set timer_end and free + if (it->second->Get_detach() && it->second->Get_form_started()) + { + it->second->Set_end_timer(true); + it->second->Rate_free(); + } + } + catch(...) + { +#if defined(__cplusplus_cli) + // Release lock + int n = System::Threading::Interlocked::Exchange(it->second->usingResource, 0); + assert(n == 5); + throw; +#endif + } +#if defined(__cplusplus_cli) + // Release lock + int n = System::Threading::Interlocked::Exchange(it->second->usingResource, 0); + assert(n == 5); +#endif + + // if detached, wait for thread to acknowledge and then erase chart + if (it->second->Get_detach()) + { + while (it->second->Get_form_started() && it->second->Get_done() != true) + { +#if defined(__cplusplus_cli) + System::Threading::Thread::Sleep(5); +#endif + } + delete it->second; + this->chart_map.erase(it); + } + return true; +} +bool +ChartHandler::End_timer() +{ + + size_t max_tries = 6000; // 1 h, but not used + std::map::iterator it = this->chart_map.begin(); + if (chart_map.size() > 0) + { + screen_msg("Detaching charts..."); + if (io != NULL) + { + io->error_flush(); + } + } + size_t i(0), i2(0); + for ( ; it != chart_map.end(); it++) + { + i = 0; + it->second->Rate_free(); + if (it->second->Get_form_started()) + { +#if defined(__cplusplus_cli) + while (0 != System::Threading::Interlocked::CompareExchange(it->second->usingResource, 6, 0)) + { + //if (i > max_tries) break; + i++; + System::Threading::Thread::Sleep(60); + } +#endif + it->second->Set_end_timer(true); + //it->second->Set_phreeqc(NULL); +#if defined(__cplusplus_cli) + int n = System::Threading::Interlocked::Exchange(it->second->usingResource, 0); + assert(n == 6); +#endif + + i2 = 0; + while (it->second->Get_done() != true) + { + //if (i2 > max_tries) break; + i2++; +#if defined(__cplusplus_cli) + System::Threading::Thread::Sleep(60); +#endif + } + //if (i >= max_tries || i2 >= max_tries) + //{ + // error_msg("\nChart did not respond.", CONTINUE); + //} + } + } + if (chart_map.size() > 0) + { + screen_msg("\rCharts detached. \n"); + if (io != NULL) + { + io->error_flush(); + } + } + this->timer = false; + + return true; +} +bool +ChartHandler::dump(std::ostream & oss, unsigned int indent) +{ + std::map::iterator it = this->chart_map.begin(); + for ( ; it != chart_map.end(); it++) + { + size_t i = 0; + it->second->dump(oss, indent); + } + return true; +} +#endif + diff --git a/phreeqcpp/ChartHandler.h b/phreeqcpp/ChartHandler.h new file mode 100644 index 00000000..f0ca405b --- /dev/null +++ b/phreeqcpp/ChartHandler.h @@ -0,0 +1,59 @@ +#if !defined(CHARTHANDLER_H_INCLUDED) +#define CHARTHANDLER_H_INCLUDED +#if defined MULTICHART +#include +#include +#include +#include "Parser.h" +#include "ChartObject.h" +#include "PHRQ_base.h" + +class ChartHandler: public PHRQ_base +{ + +public: + ChartHandler(PHRQ_io *io = NULL); + virtual ~ChartHandler(); + + size_t Get_chart_count()const + { + return this->chart_map.size(); + } + ChartObject * Get_current_chart() + { + return this->current_chart; + } + const ChartObject * Get_current_chart()const + { + return this->current_chart; + } + bool Get_timer() + { + return timer; + } + int Get_active_charts() {return this->active_charts;} + void Increment_active_charts() + { + System::Threading::Interlocked::Increment(this->active_charts); + } + void Decrement_active_charts() + { + System::Threading::Interlocked::Decrement(this->active_charts); + } + bool Read(Phreeqc * phreeqc_ptr, CParser &parser); + void Punch_user_graph(Phreeqc * phreeqc_ptr); + bool End_timer(); + bool dump(std::ostream & oss, unsigned int indent); +protected: + std::map chart_map; + int current_chart_n_user; + ChartObject * current_chart; + bool u_g_defined; + bool timer; + int active_charts; + +public: + +}; +#endif // MULTICHART +#endif // !defined(CHARTHANDLER_H_INCLUDED) diff --git a/phreeqcpp/ChartObject.cpp b/phreeqcpp/ChartObject.cpp new file mode 100644 index 00000000..510a667d --- /dev/null +++ b/phreeqcpp/ChartObject.cpp @@ -0,0 +1,1390 @@ +// ChartObject.cpp: implementation of the ChartObject class. +// +////////////////////////////////////////////////////////////////////// +#ifdef MULTICHART +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include "Utils.h" +#include +#include "ChartObject.h" +#include "Parser.h" +#include +#include +#include +#include "Phreeqc.h" +#include "phqalloc.h" + +#include "Form1.h" +using namespace zdg_ui2; + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +ChartObject::ChartObject(PHRQ_io *io) + // + // default constructor for cxxExchComp + // + : +cxxNumKeyword(io) +{ + new_ug = false; + FirstCallToUSER_GRAPH = true; + + update_time_chart = 150; + PanelHeight = 510; + PanelWidth = 640; + + Symbol_map["Square"] = 0; + Symbol_map["Diamond"] = 1; + Symbol_map["Triangle"] = 2; + Symbol_map["Circle"] = 3; + Symbol_map["XCross"] = 4; + Symbol_map["Plus"] = 5; + Symbol_map["Star"] = 6; + Symbol_map["TriangleDown"] = 7; + Symbol_map["HDash"] = 8; + Symbol_map["VDash"] = 9; + Symbol_map["None"] = 10; + + Color_vector.push_back("Red"); + Color_vector.push_back("Green"); + Color_vector.push_back("Blue"); + Color_vector.push_back("Orange"); + Color_vector.push_back("Magenta"); +// Color_vector.push_back("Yellow"); + Color_vector.push_back("Black"); + Color_vector.push_back("Cyan"); + Color_vector.push_back("Brown"); + Color_vector.push_back("Lime"); + Color_vector.push_back("Gray"); + + chart_title.clear(); + axis_titles.clear(); + + int i; + for (i = 0; i < 5; i++) + { + axis_scale_x[i] = NA; + axis_scale_y[i] = NA; + axis_scale_y2[i] = NA; + } + + chart_type = 0; + graph_initial_solutions = false; + shifts_as_points = false; + connect_simulations = true; + colnr = 0; + ColumnOffset = 0; + prev_advection_step = 0; + prev_transport_step = 0; + prev_sim_no = 0; + end_timer = false; + done = false; + + curve_added = false; + point_added = false; + + user_graph = new rate; + user_graph->commands = NULL; + user_graph->name = NULL; + user_graph->new_def = 0; + user_graph->linebase = user_graph->loopbase = user_graph->varbase = NULL; + + default_symbol = 0; + default_symbol_csv = 0; + default_color = 0; + default_color_csv = 0; + + graph_x = NA; + graph_y.clear(); + secondary_y.clear(); + + usingResource = 0; + form_started = false; + active = true; + detach = false; + + batch_background = true; + batch_grid = true; + batch = ChO_NO_BATCH; +} + +ChartObject::~ChartObject() +{ + while (0 != System::Threading::Interlocked::CompareExchange(this->usingResource, 7, 0)) + { + System::Threading::Thread::Sleep(5); + } + this->Rate_free(); + delete this->user_graph; + + std::vector::iterator it; + for (it = this->CurvesCSV.begin(); it != CurvesCSV.end(); it++) + { + delete *it; + } + + for (it = this->Curves.begin(); it != Curves.end(); it++) + { + delete *it; + } +} + +bool +ChartObject::Set_axis_scale(CParser & parser) +{ + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + LDBLE *scale_ptr = NULL; + std::vector string_vector; + size_t j = 0; + + // rereads option + parser.copy_token(token, next_char); + + // reads parameters + while ((j < 6) && (parser.copy_token(token, next_char)) != CParser::TT_EMPTY) + { + string_vector.push_back(token); + j++; + } + if (string_vector.size() == 0) + { + error_msg("No axis defined for scales", PHRQ_io::OT_CONTINUE); + return false; + } + std::string str = string_vector[0]; + std::string type; + Utilities::str_tolower(str); + if (str.substr(0,2) == "y2" || str.substr(0,1) == "s") + { + type = "sy"; + scale_ptr = this->axis_scale_y2; + } + else if (str.substr(0,1) == "y") + { + type = "y"; + scale_ptr = this->axis_scale_y; + } + else if (str.substr(0,1) == "x") + { + type = "x"; + scale_ptr = this->axis_scale_x; + } + else + { + std::ostringstream estream; + estream << "Found " << str; + estream << ", but expect axis type \'x\', \'y\', \'y2\'or \'sy\'."; + estream << "\n"; + error_msg(estream.str().c_str(), PHRQ_io::OT_CONTINUE); + return false; + } + + for (j = 1; j < string_vector.size() && j < 5; j++) + { + std::string s = string_vector[j]; + if (s[0] == 'a' || s[0] == 'A') + { + scale_ptr[j - 1] = NA; + } + else if (CParser::token_type(s) == CParser::TT_DIGIT) + { + scale_ptr[j - 1] = atof(s.c_str()); + } + else + { + std::ostringstream estream; + estream << "Found " << s; + estream << ", but expect number or 'a(uto)'."; + estream << "\n"; + error_msg(estream.str().c_str(), CONTINUE); + return false; + } + } + if (string_vector.size() == 6) + { + std::string s = string_vector[5]; + if (s[0] == 't' || s[0] == 'T' || s[0] == 'l' || s[0] == 'L') + { + scale_ptr[4] = 10.0; + if (((fabs(scale_ptr[0] - NA) > 1e-3) && scale_ptr[0] <=0) || + ((fabs(scale_ptr[1] - NA) > 1e-3) && scale_ptr[1] <=0)) + { + std::ostringstream estream; + estream << "MIN and MAX must be > 0 for log " << type << "-scale."; + estream << "\n"; + error_msg(estream.str().c_str(), CONTINUE); + return false; + } + + } + } + if ((fabs(scale_ptr[0] - NA) > 1e-3) && (fabs(scale_ptr[1] - NA) > 1e-3)) + { + if (scale_ptr[0] > scale_ptr[1]) + { + std::ostringstream estream; + estream << "Maximum must be larger than minimum of axis_scale " << type << "-scale." << "\n"; + estream << "Switching values for MIN and MAX. " << "\n"; + warning_msg(estream.str().c_str()); + LDBLE t; + t = scale_ptr[0]; + scale_ptr[0] = scale_ptr[1]; + scale_ptr[1] = scale_ptr[0]; + return false; + } + } + return true; +} + +bool +ChartObject::Read(CParser & parser) +{ + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + bool useLastLine(false); + bool new_command_lines(false); + // if no definitions in USER_GRAPH data block, deactivate plot + bool no_def(true); + if (this->FirstCallToUSER_GRAPH) + { + this->new_ug = true; + } + else + { + this->ColumnOffset = (int) this->Curves.size(); + this->new_ug = true; + } + this->new_plotxy_curves.clear(); + + // Read number and description + { + this->read_number_description(parser); + } + + opt_save = CParser::OPT_DEFAULT; + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + opt = parser.getOptionFromLastLine(vopts, next_char, true); + } + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + error_msg("Unknown input in USER_GRAPH keyword.", CONTINUE); + error_msg(parser.line().c_str(), CONTINUE); + useLastLine = false; + break; + case 0: /* start */ + opt_save = CParser::OPT_DEFAULT; + break; + case 1: /* end */ + opt_save = CParser::OPT_DEFAULT; + break; + case 2: /* headings */ + case 3: /* heading */ + this->new_headings.clear(); + while (parser.copy_token(token, next_char) != CParser::TT_EMPTY) + { + this->new_headings.push_back(token); + } + this->headings_original = this->new_headings; + break; + case 4: /* chart title */ + { + std::string tok; + parser.get_rest_of_line(tok); + std::string::iterator b = tok.begin(); + std::string::iterator e = tok.end(); + CParser::copy_title(this->chart_title, b, e); + } + break; + case 5: /* axis titles */ + { + this->axis_titles.clear(); + std::string l; + parser.get_rest_of_line(l); + std::string tok; + std::string::iterator b = l.begin(); + std::string::iterator e = l.end(); + this->axis_titles.clear(); + while(parser.copy_title(tok, b, e) != CParser::TT_EMPTY) + { + this->axis_titles.push_back(tok); + } + } + while (this->axis_titles.size() < 3) this->axis_titles.push_back(""); + break; + case 6: /* axis scales */ + { + this->Set_axis_scale(parser); + } + break; + case 7: /* initial_solutions */ + this->graph_initial_solutions = parser.get_true_false(next_char, FALSE); + break; + case 8: /* plot_concentration_vs */ + parser.copy_token(token, next_char); + Utilities::str_tolower(token); + if (token[0] == 'x' || token[0] == 'd') + chart_type = 0; + else if (token[0] == 't') + chart_type = 1; + else + { + std::ostringstream estream; + estream << "Found " << token << ", but expect plot type: (\'x\' or \'dist\') for distance, (\'t\') for time."; + error_msg(estream.str().c_str(), CONTINUE); + } + break; + case 9: /* shifts_as_points */ + this->shifts_as_points = parser.get_true_false(next_char, true); + if (this->shifts_as_points) + this->chart_type = 0; + else + this->chart_type = 1; + break; + case 10: /* grid_offset */ +#ifdef PHREEQ98 + /* + i = copy_token(token, &next_char, &l); + str_tolower(token); + if (i == DIGIT) + sscanf(token, "%d", &RowOffset); + i = copy_token(token, &next_char, &l); + str_tolower(token); + if (i == DIGIT) + sscanf(token, "%d", &ColumnOffset); + */ +#endif + break; + case 11: /* connect_simulations */ + this->connect_simulations = parser.get_true_false(next_char, true); + break; + case 12: /* plot_csv_file */ + case 17: /* plot_tsv_file */ + { + std::string file_name; + parser.get_rest_of_line(file_name); + file_name = trim(file_name); + this->OpenCSVFile(file_name); + this->csv_file_names.push_back(file_name); + } + break; + case 13: /* clear */ + case 14: /* detach */ + this->detach = true; + break; + /* End of modifications */ + case 15: /* active */ + this->active = parser.get_true_false(next_char, true); + if (this->active && this->Get_connect_simulations()) + { + this->ColumnOffset = 0;//this->Curves.size(); + this->new_ug = false; + } + break; + /* End of modifications */ + case 16: /* batch */ + { + this->batch = ChartObject::ChO_BATCH_ONLY; + std::string rest_of_line, lc_rest_of_line; + parser.get_rest_of_line(rest_of_line); + lc_rest_of_line = rest_of_line; + Utilities::str_tolower(lc_rest_of_line); + + std::vector file_types; + file_types.push_back(".emf"); + file_types.push_back(".png"); + file_types.push_back(".jpg"); + file_types.push_back(".gif"); + file_types.push_back(".tiff"); + file_types.push_back(".bmp"); + file_types.push_back(".jpeg"); + + size_t first, last; + size_t i; + for (i = 0; i < file_types.size(); i++) + { + first = lc_rest_of_line.rfind(file_types[i].c_str()); + if (first != std::string::npos) + { + break; + } + } + if (i >= file_types.size()) + { + std::ostringstream estream; + estream << "Batch file name must have suffix emf, png, jpg, jpeg, gif, tiff, or bmp."; + error_msg(estream.str().c_str(), CONTINUE); + break; + } + switch (i) + { + case 0: + this->batch = ChartObject::ChO_EMF; + last = first + 4; + break; + case 1: + this->batch = ChartObject::ChO_PNG; + last = first + 4; + break; + case 2: + this->batch = ChartObject::ChO_JPG; + last = first + 4; + break; + case 3: + this->batch = ChartObject::ChO_GIF; + last = first + 4; + break; + case 4: + this->batch = ChartObject::ChO_TIFF; + last = first + 5; + break; + case 5: + this->batch = ChartObject::ChO_BMP; + last = first + 4; + break; + case 6: + this->batch = ChartObject::ChO_JPG; + last = first + 5; + break; + } + + this->batch_fn = rest_of_line.substr(0, last); + if (last+1 < rest_of_line.size()) + { + token = rest_of_line.substr(last); + token = trim(token); + std::string tf; + std::string::iterator b = token.begin(); + std::string::iterator e = token.end(); + + CParser::copy_token(tf, b, e); + if (tf.size() > 0) + { + Utilities::str_tolower(tf); + if (tf[0] == 'f') + { + this->batch_background = false; + } + } + CParser::copy_token(tf, b, e); + if (tf.size() > 0) + { + Utilities::str_tolower(tf); + if (tf[0] == 'f') + { + this->batch_grid = false; + } + } + } + } + break; + case CParser::OPT_DEFAULT: // Read Basic commands + { + if (!new_command_lines) + { + this->rate_command_list.clear(); + this->rate_command_list_original.clear(); + new_command_lines = true; + } + this->rate_new_def = true; + /* read command */ + std::string cmd(parser.line()); + this->rate_command_list_original.push_back(cmd); + std::string cmd_lower = cmd; + Utilities::str_tolower(cmd_lower); + if ((cmd_lower.find("graph_y") != std::string::npos) || + (cmd_lower.find("graph_sy") != std::string::npos)) + { + //Number of curves not known here + //Curves are created in Basic cmds + } + if (cmd_lower.find("plot_xy") != std::string::npos) + { + //Curves are created in Basic cmds + CurveObject new_curve = ExtractCurveInfo(cmd); // truncates cmd + // Add to list of new plotxy curves + this->new_plotxy_curves.push_back(new_curve); + } + this->rate_command_list.push_back(cmd); + } + opt_save = CParser::OPT_DEFAULT; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + no_def = false; + } + + // install new plotxy commands + // disable this user_graph if USER_GRAPH block is empty + //if (new_command_lines || no_def) this->Set_rate_struct(); + if (new_command_lines) this->Set_rate_struct(); + if (no_def) this->Set_active(false); + return true; +} +bool +ChartObject::OpenCSVFile(std::string file_name) +{ + std::string token; + + std::ifstream f_csv(file_name.c_str(), std::ifstream::in); + + if (!f_csv.is_open()) + { + std::ostringstream estream; + estream << "Could not open tsv file for USER_GRAPH " << file_name << "\n Please, give the full path + filename."; + error_msg(estream.str().c_str(), CONTINUE); + return false; + } + + CParser parser(f_csv, this->Get_io()); + parser.set_echo_file(CParser::EO_NONE); + + /* Get lines */ + int linenr = 0; + + // temporary storage for new CSV curves + std::vector headings; + std::vector csv_curves; + + // Read file line by line into temporary curves + while (parser.check_line("cvs file", false, true, true, false) != PHRQ_io::LT_EOF) + { + // Headings line + if (linenr == 0) + { + // Skip x in 1st column + parser.get_iss() >> token; + + // Read rest of headings + while (parser.get_iss() >> token) + { + headings.push_back(token); + } + // add curves to temporary csv_curves + size_t i; + for (i = 0; i < headings.size(); i++) + { + CurveObject *c = new CurveObject; + c->Set_id(headings[i]); + c->Set_line_w(0); + std::string sym = ""; + this->Get_legal_symbol_csv(sym); + c->Set_symbol(sym); + csv_curves.push_back(c); + } + + linenr++; + continue; + } + + token = parser.line(); + std::string tok1; + CParser::TOKEN_TYPE tt = CParser::parse_delimited(token, tok1, "\t"); + + // Curve properties lines + if (linenr < 6 && tt != CParser::TT_DIGIT) + { + Utilities::str_tolower(tok1); + std::string tok2; + size_t i=0; + while (token.size() != 0 && i < csv_curves.size()) + { + CParser::parse_delimited(token, tok2, "\t"); + tok2 = trim(tok2); + if (!strncmp(tok1.c_str(), "color", 5)) + { + csv_curves[i]->Set_color(tok2); + } + else if (!strncmp(tok1.c_str(), "symbol", 5) && (strstr(tok1.c_str(), "_si") == NULL) + && (strstr(tok1.c_str(), "-si") == NULL)) + { + csv_curves[i]->Set_symbol(tok2); + } + else if (!strncmp(tok1.c_str(), "symbol_size", 8) || !strncmp(tok1.c_str(), "symbol-size", 8)) + { + if (tok2.size() > 0) + { + csv_curves[i]->Set_symbol_size(atof(tok2.c_str())); + } + } + else if (!strncmp(tok1.c_str(), "line_w", 5) || !strncmp(tok1.c_str(), "line-w", 5)) + { + csv_curves[i]->Set_line_w(atof(tok2.c_str())); + } + else if (!strncmp(tok1.c_str(), "y_axis", 5) || !strncmp(tok1.c_str(), "y-axis", 5)) + { + if (tok2.size() > 0) + { + csv_curves[i]->Set_y_axis(atoi(tok2.c_str())); + } + } + i++; + } + linenr++; + continue; + } + + // Curve data + if (linenr < 6) linenr = 6; + if (tt != CParser::TT_DIGIT) + { + linenr++; + continue; + } + + // x value for all curves + LDBLE x_value = atof(tok1.c_str()); + + // y values for curves + std::string tok2; + size_t i=0; + while (token.size() != 0 && i < csv_curves.size()) + { + CParser::parse_delimited(token, tok2, "\t"); + Utilities::squeeze_white(tok2); + if (tok2.size() == 0) + { + //csv_curves[i].Get_x().push_back(NA); + //csv_curves[i].Get_y().push_back(NA); + } + else + { + csv_curves[i]->Get_x().push_back(x_value); + csv_curves[i]->Get_y().push_back(atof(tok2.c_str())); + } + i++; + } + linenr++; + } + + // Append new curves + + std::vector::iterator it = csv_curves.begin(); + for (; it != csv_curves.end(); it++) + { + if ((*it)->Get_x().size() > 0) + { + std::string col = (*it)->Get_color(); + this->Get_color_string_csv(col); + (*it)->Set_color(col); + this->CurvesCSV.push_back(*it); + this->Set_curve_added(true); + } + } + return true; +} + +CurveObject +ChartObject::ExtractCurveInfo(std::string & cmd_line) +{ + /* plot_xy x, tot("Cl"), color = Red, symbol = Circle, symbol_size = 0.0, line_w = 1.0, y_axis = 2 */ + + // Make copy of cmd_line + //int curvenr = (int) this->Curves.size(); + std::string str_line = cmd_line; + + // Massage line + while (Utilities::replace(" ,",",",str_line)); + while (Utilities::replace("\t,",",",str_line)); + while (Utilities::replace(",","#",str_line)); + while (Utilities::replace("#",", ", str_line)); + + int sel; + std::string token, tok1, tok2; + std::string revised_line; + std::string::iterator b = str_line.begin(); + std::string::iterator e = str_line.end(); + // new curve + CurveObject new_curve; + + while (CParser::copy_token(token, b, e) != CParser::TT_EMPTY) + { + sel = -1; + std::string token_save = token; + Utilities::str_tolower(token); + tok1 = token; + if (!strncmp(tok1.c_str(), "color", 5)) + { + sel = 0; + } + else if (!strncmp(tok1.c_str(), "symbol", 5) && (strstr(tok1.c_str(), "_si") == NULL) + && (strstr(tok1.c_str(), "-si") == NULL)) + { + sel = 1; + } + else if (!strncmp(tok1.c_str(), "symbol_size", 8) || !strncmp(tok1.c_str(), "symbol-size", 8)) + { + sel = 2; + } + else if (!strncmp(tok1.c_str(), "line_w", 5) || !strncmp(tok1.c_str(), "line-w", 5)) + { + sel = 3; + } + else if (!strncmp(tok1.c_str(), "y_axis", 5) || !strncmp(tok1.c_str(), "y-axis", 5)) + { + sel = 4; + } + + // Append to command line + if (sel < 0) + { + revised_line.append(token_save); + revised_line.append(" "); + } + // Parse plot_xy pair + else + { + // remove preceding comma + std::string comma = revised_line.substr(revised_line.size() - 2); + if (comma == ", ") + { + revised_line = revised_line.substr(0, revised_line.size() - 2); + revised_line.append(" "); + } + + token = token_save; + size_t p1 = token.find("="); + std::string tok2; + // "=" found + if(p1 != std::string::npos) + { + if (p1 != token.size() - 1) + { + // color=Red + tok2 = token.substr(p1 + 1); + } + else + { + // color= Red + CParser::copy_token(tok2, b, e); + } + } + else + { + // no "=" found + CParser::copy_token(tok2, b, e); + p1 = tok2.find("="); + if (tok2 == "=") + { + // color = Red + CParser::copy_token(tok2, b, e); + } + else if (p1 != std::string::npos) + { + // color =Red + tok2 = tok2.substr(p1 + 1); + } + else + { + // color Red + tok2 = tok2; + } + } + // remove any commas + while(Utilities::replace(",","",tok1)); + while(Utilities::replace(",","",tok2)); + + // tok1 is name, tok2 is value + + switch (sel) + { + case 0: + new_curve.Set_color(tok2); + break; + case 1: + new_curve.Set_symbol(tok2); + break; + case 2: + new_curve.Set_symbol_size(atof(tok2.c_str())); + break; + case 3: + new_curve.Set_line_w(atof(tok2.c_str())); + break; + case 4: + new_curve.Set_y_axis(atoi(tok2.c_str())); + break; + } + } + } + + cmd_line = revised_line; + return new_curve; +} +void +ChartObject::Set_rate_struct(void) +{ + + if (rate_command_list.size() == 0) return; + std::list::iterator it = rate_command_list.begin(); + std::ostringstream oss; + for (; it != rate_command_list.end(); it++) + { + oss << *it << "\n"; + } + this->Rate_free(); + if (this->phreeqc_ptr) + { + this->user_graph->commands = (char *) phreeqc_ptr-> PHRQ_malloc((oss.str().size()) + 100 * sizeof(char)); + } + ::strcpy(this->user_graph->commands, oss.str().c_str()); + this->user_graph->new_def = this->rate_new_def; + this->user_graph->loopbase = NULL; + this->user_graph->varbase = NULL; + this->user_graph->linebase = NULL; + this->user_graph->name = NULL; +} +void +ChartObject::Get_legal_symbol(std::string &sym) +{ + std::map::iterator it; + if ((it = this->Symbol_map.find(sym)) == this->Symbol_map.end()) + { + sym = "Default"; + for (it = this->Symbol_map.begin(); it != this->Symbol_map.end(); it++) + { + if (default_symbol == it->second) + { + sym = it->first; + break; + } + } + default_symbol++; + default_symbol = default_symbol % this->Symbol_map.size(); + } + return; +} +void +ChartObject::Get_legal_symbol_csv(std::string &sym) +{ + std::map::iterator it; + if ((it = this->Symbol_map.find(sym)) == this->Symbol_map.end()) + { + sym = "Default"; + for (it = this->Symbol_map.begin(); it != this->Symbol_map.end(); it++) + { + if (this->default_symbol_csv == it->second) + { + sym = it->first; + break; + } + } + default_symbol_csv++; + default_symbol_csv = default_symbol_csv % this->Symbol_map.size(); + } + return; +} +void +ChartObject::Get_color_string(std::string &color) +{ + if (color.size() == 0) + { + color = Color_vector[this->default_color]; + default_color++; + default_color = default_color % this->Color_vector.size(); + } + return; +} +void +ChartObject::Get_color_string_csv(std::string &color) +{ + if (color.size() == 0) + { + color = Color_vector[this->default_color_csv]; + default_color_csv++; + default_color_csv = default_color_csv % this->Color_vector.size(); + } + return; +} +ZedGraph::SymbolType +ChartObject::Return_SymbolType(const std::string sym) +{ + int i; + std::map::iterator it; + if ((it = this->Symbol_map.find(sym)) != this->Symbol_map.end()) + { + i = it->second; + } + else + { + i = default_symbol++; + default_symbol = default_symbol % this->Symbol_map.size(); + } + + switch (i) + { + case 0: + return SymbolType::Square; + break; + case 1: + return SymbolType::Diamond; + break; + case 2: + return SymbolType::Triangle; + break; + case 3: + return SymbolType::Circle; + break; + case 4: + return SymbolType::XCross; + break; + case 5: + return SymbolType::Plus; + break; + case 6: + return SymbolType::Star; + break; + case 7: + return SymbolType::TriangleDown; + break; + case 8: + return SymbolType::HDash; + break; + case 9: + return SymbolType::VDash; + break; + case 10: + return SymbolType::None; + break; + default: + return SymbolType::Default; + break; + } +} +void +ChartObject::SaveCurvesToFile(std::string &file_name) +{ + // reimplemented in Form + // This version not currently used + std::ofstream f_out(file_name.c_str(), std::ifstream::out); + + if (!f_out.is_open()) + { + std::ostringstream estream; + estream << "Could not open csv file for USER_GRAPH " << file_name; + error_msg(estream.str().c_str(), CONTINUE); + return; + } + + // list of curves + std::vector all_curves; + size_t i; + for (i = 0; i < this->CurvesCSV.size(); i++) + { + all_curves.push_back(this->CurvesCSV[i]); + } + for (i = 0; i < this->Curves.size(); i++) + { + all_curves.push_back(Curves[i]); + } + // write headings + size_t max_points = 0; + std::vector::iterator it = all_curves.begin(); + f_out.precision(4); + i = 0; + for ( ; it != all_curves.end(); it++) + { + f_out.width(12); + f_out << "x" << "\t"; + f_out.width(12); + if ((*it)->Get_id().size() > 0) + { + f_out << (*it)->Get_id() << "\t"; + } + else + { + f_out << "y" << "\t";; + } + if ((*it)->Get_x().size() > max_points) + max_points = (*it)->Get_x().size(); + } + f_out << "\n"; + + // write data + size_t i2 = 0; + f_out << std::scientific; + f_out.precision(4); + while (i2 < max_points) + { + for (it = all_curves.begin(); it != all_curves.end(); it++) + { + if (i2 < (*it)->Get_x().size()) + { + f_out.width(12); + f_out << (*it)->Get_x()[i2] << "\t"; + f_out.width(12); + f_out << (*it)->Get_y()[i2] << "\t"; + } + else if (i2 < max_points) + { + f_out.width(13); + f_out << "\t"; + f_out.width(13); + f_out << "\t"; + } + } + f_out << "\n"; + i2++; + } + f_out.close(); + return; +} +// file only used with MULTICHART +bool +ChartObject::start_chart(void) +{ + Application::EnableVisualStyles(); + Application::SetCompatibleTextRenderingDefault(true); + + // needed to send ChartObject pointer to thread + Thread ^t = gcnew Thread( + gcnew ParameterizedThreadStart(Form1::ThreadForm)); + + t->SetApartmentState(ApartmentState::STA); + t->IsBackground = false; + t->Priority = ThreadPriority::Normal; + + ChartObj ^p = gcnew ChartObj(this); + t->Start(p); + this->form_started = true; + + //Thread::Sleep( 1 ); /* this when debugging... */ + //_beginthread(void (Form1::ThreadForm), 0, NULL); + return true; +} + +void +ChartObject::Rate_free(void) +{ + + + if (this->phreeqc_ptr) + { + user_graph->commands = (char *) phreeqc_ptr-> free_check_null(user_graph->commands); + } + if (user_graph->linebase != NULL) + { + char cmd[] = "new; quit"; + if (this->phreeqc_ptr) + { + phreeqc_ptr-> basic_run(cmd, user_graph->linebase, user_graph->varbase, user_graph->loopbase); + } + user_graph->linebase = NULL; + user_graph->varbase = NULL; + user_graph->loopbase = NULL; + } +} +void ChartObject::Initialize_graph_pts(void) +{ + graph_x = NA; + graph_y.clear(); + secondary_y.clear(); +} + +void +ChartObject::Finalize_graph_pts(void) +{ + if (graph_x != NA) + { + std::map::iterator it; + for (it = graph_y.begin(); it != graph_y.end(); it++) + { + Curves[it->first]->Get_x().push_back(graph_x); + Curves[it->first]->Get_y().push_back(it->second); + // Mark added curve for first point, might have been invisible in DefineCurves + if (Curves[it->first]->Get_x().size() == 1) + this->Set_curve_added(true); + } + } + if (graph_x != NA) + { + std::map::iterator it; + for (it = secondary_y.begin(); it != secondary_y.end(); it++) + { + Curves[it->first]->Set_y_axis(2); + } + } +} +void +ChartObject::Add_new_series(void) +{ + std::vector Curves; + size_t i; + size_t e = this->Curves.size(); + for (i = this->ColumnOffset; i < e; i++) + { + this->Add_curve(false, + this->Curves[i]->Get_id(), + this->Curves[i]->Get_line_w(), + "", + this->Curves[i]->Get_symbol_size(), + this->Curves[i]->Get_y_axis(), + ""); + } + this->ColumnOffset = (int) e; + this->curve_added = true; +} +void +ChartObject::Add_curve(bool plotxy, std::string id, + LDBLE line_width, + std::string symbol, + LDBLE symbol_size, + int y_axis, + std::string color) +{ + CurveObject *c = new CurveObject; + c->Set_id(id); + c->Set_line_w(line_width); + this->Get_legal_symbol(symbol); + c->Set_symbol(symbol); + c->Set_symbol_size(symbol_size); + if (this->CurvesCSV.size() > this->Curves.size() && !plotxy) + { + c->Set_symbol_size(0.0); + } + c->Set_y_axis(y_axis); + this->Get_color_string(color); + c->Set_color(color); + + this->Curves.push_back(c); +} +void +ChartObject::Set_rate_new_def(bool tf) +{ + this->rate_new_def = tf; + if (this->user_graph != NULL) + { + if (tf) + { + this->user_graph->new_def = 1; + } + else + { + this->user_graph->new_def = 0; + } + } +} +void +ChartObject::dump(std::ostream & oss, unsigned int indent) +{ + size_t i; + oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + oss << indent0 << "USER_GRAPH " << this->n_user << " " << this->description << "\n"; + + // chart title + oss << indent1 << "-chart_title \"" << this->chart_title << "\"" << "\n"; + + // axis titles + if (this->axis_titles.size() > 0) + { + oss << indent1 << "-axis_titles "; + + for (i = 0; i < this->axis_titles.size(); i++) + { + oss << "\"" << axis_titles[i] << "\" "; + } + oss << "\n"; + } + + // axis_scale_x + LDBLE *scale_ptr = this->axis_scale_x; + { + oss << indent1 << "-axis_scale x_axis "; + for (i = 0; i < 4; i++) + { + if (scale_ptr[i] == NA) + { + oss << " auto"; + } + else + { + oss << " " << scale_ptr[i]; + } + } + if (scale_ptr[4] == 10.0) + { + oss << " log"; + } + oss << "\n"; + } + // axis_scale_y + scale_ptr = this->axis_scale_y; + { + oss << indent1 << "-axis_scale y_axis "; + for (i = 0; i < 4; i++) + { + if (scale_ptr[i] == NA) + { + oss << " auto"; + } + else + { + oss << " " << scale_ptr[i]; + } + } + if (scale_ptr[4] == 10.0) + { + oss << " log"; + } + oss << "\n"; + } + // axis_scale_sy + scale_ptr = this->axis_scale_y2; + { + oss << indent1 << "-axis_scale sy_axis "; + for (i = 0; i < 4; i++) + { + if (scale_ptr[i] == NA) + { + oss << " auto"; + } + else + { + oss << " " << scale_ptr[i]; + } + } + if (scale_ptr[4] == 10.0) + { + oss << " log"; + } + oss << "\n"; + } + // chart type + if (this->chart_type == 0) + { + oss << indent1 << "-plot_concentration_vs x" << "\n"; + } + else + { + oss << indent1 << "-plot_concentration_vs t" << "\n"; + } + // graph_initial_solutions + if (this->graph_initial_solutions) + { + oss << indent1 << "-initial_solutions true" << "\n"; + } + else + { + oss << indent1 << "-initial_solutions false" << "\n"; + } + // connect_simulations + if (this->connect_simulations) + { + oss << indent1 << "-connect_simulations true" << "\n"; + } + else + { + oss << indent1 << "-connect_simulations false" << "\n"; + } + // csv files + for (i = 0; i < this->csv_file_names.size(); i++) + { + oss << indent1 << "-plot_tsv_file " << this->csv_file_names[i] << "\n"; + } + + // headings + if (this->headings_original.size() > 0) + { + oss << indent1 << "-headings "; + for (i = 0; i < this->headings_original.size(); i++) + { + oss << this->headings_original[i] << " "; + } + oss << "\n"; + } + + // commands + oss << indent1 << "-start" << "\n"; + std::list::iterator it = rate_command_list_original.begin(); + for (; it != rate_command_list_original.end(); it++) + { + oss << *it << "\n"; + } + oss << indent1 << "-end" << "\n"; + + /* + struct rate *user_graph; + // C++ for rate struct + std::string rate_name; + std::list rate_command_list; + bool rate_new_def; + + int default_symbol; + int default_symbol_csv; + int default_color; + int default_color_csv; + + // temporary storage before stored graph_x/y/sy data are stored in curves + // Initialize_graph_pts and Finalize_graph_pts use this storage. + LDBLE graph_x; + std::map graph_y; + std::map secondary_y; + + // temporary plotxy curve definitions before stored in curves + // a plotxy curve is copied to Curves when cmdplotxy is encountered + // this keeps order correct between plotxy and graph_x/y/sy + std::vector new_plotxy_curves; + + // temporary headings until stored during basic_run + std::vector new_headings; + bool active; + bool detach; + bool form_started; + */ +} + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("start"), // 0 + std::vector< std::string >::value_type("end"), // 1 + std::vector< std::string >::value_type("heading"), // 2 + std::vector< std::string >::value_type("headings"), // 3 + std::vector< std::string >::value_type("chart_title"), // 4 + std::vector< std::string >::value_type("axis_titles"), // 5 + std::vector< std::string >::value_type("axis_scale"), // 6 + std::vector< std::string >::value_type("initial_solutions"), // 7 + std::vector< std::string >::value_type("plot_concentration_vs"), // 8 + std::vector< std::string >::value_type("shifts_as_points"), // 9 + std::vector< std::string >::value_type("grid_offset"), // 10 + std::vector< std::string >::value_type("connect_simulations"), // 11 + std::vector< std::string >::value_type("plot_csv_file"), // 12 + std::vector< std::string >::value_type("clear"), // 13 + std::vector< std::string >::value_type("detach"), // 14 + std::vector< std::string >::value_type("active"), // 15 + std::vector< std::string >::value_type("batch"), // 16 + std::vector< std::string >::value_type("plot_tsv_file") // 17 +}; +const std::vector< std::string > ChartObject::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); + + +#endif // MULTICHART \ No newline at end of file diff --git a/phreeqcpp/ChartObject.h b/phreeqcpp/ChartObject.h new file mode 100644 index 00000000..5e7e2eb0 --- /dev/null +++ b/phreeqcpp/ChartObject.h @@ -0,0 +1,444 @@ +#if !defined(CHARTOBJECT_H_INCLUDED) +#define CHARTOBJECT_H_INCLUDED +#if defined MULTICHART +#include +#include +#include +#include +#include "CurveObject.h" +#include "NumKeyword.h" + +#include +class Phreeqc; + +class ChartObject:public cxxNumKeyword +{ + + public: + ChartObject(PHRQ_io *io=NULL); + ChartObject(int i, PHRQ_io *io=NULL); + ~ChartObject(); + + enum chart_batch_type + { ChO_NO_BATCH = -1, + ChO_BATCH_ONLY = 0, + ChO_EMF = 1, + ChO_PNG = 2, + ChO_JPG = 3, + ChO_GIF = 4, + ChO_TIFF = 5, + ChO_BMP = 6 + }; + + bool Get_new_ug()const + { + return this->new_ug; + } + void Set_new_ug(bool b) + { + this->new_ug = b; + } + void Set_FirstCallToUSER_GRAPH(bool b) + { + this->FirstCallToUSER_GRAPH = b; + } + bool Get_FirstCallToUSER_GRAPH()const + { + return this->FirstCallToUSER_GRAPH; + } + int Get_update_time_chart()const + { + return (this->update_time_chart); + } + int Get_PanelHeight()const + { + return (this->PanelHeight); + } + int Get_PanelWidth()const + { + return (this->PanelWidth); + } + std::string &Get_chart_title() + { + return this->chart_title; + } + const std::string &Get_chart_title()const + { + return this->chart_title; + } + std::string Get_batch_fn() + { + return this->batch_fn; + } + void Set_batch_fn(std::string fn) + { + this->batch_fn = fn; + } + std::vector &Get_axis_titles() + { + return this->axis_titles; + } + const std::vector &Get_axis_titles()const + { + return this->axis_titles; + } + LDBLE *Get_axis_scale_x() + { + return this->axis_scale_x; + } + const LDBLE *Get_axis_scale_x()const + { + return this->axis_scale_x; + } + LDBLE *Get_axis_scale_y() + { + return this->axis_scale_y; + } + const LDBLE *Get_axis_scale_y()const + { + return this->axis_scale_y; + } + LDBLE *Get_axis_scale_y2() + { + return this->axis_scale_y2; + } + const LDBLE *Get_axis_scale_y2()const + { + return this->axis_scale_y2; + } + int Get_chart_type()const + { + return this->chart_type; + } + bool Get_graph_initial_solutions()const + { + return this->graph_initial_solutions; + } + void Set_graph_initial_solutions(bool val) + { + this->graph_initial_solutions = val; + } + bool Get_connect_simulations()const + { + return this->connect_simulations; + } + void Set_connect_simulations(bool val) + { + this->connect_simulations = val; + } + void Set_colnr(int i) + { + this->colnr = i; + } + int Get_colnr()const + { + return (this->colnr); + } + void Set_ColumnOffset(int i) + { + this->ColumnOffset = i; + } + int Get_ColumnOffset()const + { + return (this->ColumnOffset); + } + void Set_AddSeries(bool b) + { + this->AddSeries = b; + } + bool Get_AddSeries()const + { + return this->AddSeries; + } + std::vector< std::string > Get_csv_file_names()const + { + return this->csv_file_names; + } + void Get_csv_file_names(std::vector< std::string > names) + { + this->csv_file_names = names; + } + void Set_prev_advection_step(int i) + { + this->prev_advection_step = i; + } + int Get_prev_advection_step()const + { + return (this->prev_advection_step); + } + void Set_prev_transport_step(int i) + { + this->prev_transport_step = i; + } + int Get_prev_transport_step()const + { + return (this->prev_transport_step); + } + void Set_prev_sim_no(int i) + { + this->prev_sim_no = i; + } + int Get_prev_sim_no(void)const + { + return this->prev_sim_no; + } + void Set_end_timer(bool b) + { + this->end_timer = b; + } + bool Get_end_timer()const + { + return this->end_timer; + } + void Set_done(bool b) + { + this->done = b; + } + bool Get_done()const + { + return this->done; + } + std::vector &Get_CurvesCSV() + { + return this->CurvesCSV; + } + const std::vector &Get_CurvesCSV()const + { + return this->CurvesCSV; + } + std::vector &Get_Curves() + { + return this->Curves; + } + const std::vector &Get_Curves()const + { + return this->Curves; + } + void Set_curve_added(bool tf) + { + this->curve_added = tf; + } + bool Get_curve_added()const + { + return this->curve_added; + } + void Set_point_added(bool tf) + { + this->point_added = tf; + } + bool Get_point_added()const + { + return this->point_added; + } + struct rate *Get_user_graph() + { + return this->user_graph; + } + const struct rate *Get_user_graph()const + { + return this->user_graph; + } + std::list &Get_rate_command_list() + { + return this->rate_command_list; + } + const std::list &Get_rate_command_list()const + { + return this->rate_command_list; + } + void Set_rate_new_def(bool tf); + + bool Get_rate_new_def()const + { + return this->rate_new_def; + } + void Set_graph_x(LDBLE d) + { + this->graph_x = d; + } + LDBLE Get_graph_x()const + { + return this->graph_x; + } + std::map &Get_graph_y() + { + return this->graph_y; + } + const std::map &Get_graph_y()const + { + return this->graph_y; + } + std::map &Get_secondary_y() + { + return this->secondary_y; + } + const std::map &Get_secondary_y()const + { + return this->secondary_y; + } + std::vector &Get_new_plotxy_curves() + { + return this->new_plotxy_curves; + } + const std::vector &Get_new_plotxy_curves()const + { + return this->new_plotxy_curves; + } + std::vector &Get_new_headings() + { + return this->new_headings; + } + const std::vector &Get_new_headings()const + { + return this->new_headings; + } + void Set_active(bool tf) + { + this->active = tf; + } + bool Get_active()const + { + return this->active; + } + void Set_detach(bool tf) + { + this->detach = tf; + } + bool Get_detach()const + { + return this->detach; + } + bool Get_form_started()const + { + return this->form_started; + } + void Set_phreeqc(Phreeqc * ptr) + { + this->phreeqc_ptr = ptr; + } + Phreeqc * Get_phreeqc() + { + return this->phreeqc_ptr; + } + const Phreeqc * Get_phreeqc()const + { + return this->phreeqc_ptr; + } + const std::list& Get_rate_command_list_original()const + { + return this->rate_command_list_original; + } + bool Set_axis_scale(std::vector, std::vector types, std::ostringstream &); + bool Set_axis_scale(CParser & parser); + bool Read(CParser & parser); + bool OpenCSVFile(std::string fn); + static CurveObject ExtractCurveInfo(std::string & str_line); + void Set_rate_struct(void); + void PlotXY(std::string x, std::string y); + bool start_chart(void); + ZedGraph::SymbolType Return_SymbolType(std::string); + void SaveCurvesToFile(std::string &); + void Rate_free(void); + void Initialize_graph_pts(void); + void Finalize_graph_pts(void); + void Get_legal_symbol(std::string &sym); + void Get_legal_symbol_csv(std::string &sym); + void Get_color_string(std::string &color); + void Get_color_string_csv(std::string &color); + void Add_new_series(void); + void Add_curve(bool plotxy, std::string id = "", + LDBLE line_width = 1.0, + std::string symbol = "", + LDBLE symbol_size = 6.0, + int y_axis = 1, + std::string color = ""); + void dump(std::ostream & s_oss, unsigned int indent); + + chart_batch_type Get_batch(void) {return this->batch;} + void Set_batch(chart_batch_type bt) {this->batch = bt;} + bool Get_batch_background(void) {return this->batch_background;} + void Set_batch_background(bool tf) {this->batch_background = tf;} + + bool Get_batch_grid(void) {return this->batch_grid;} + void Set_batch_grid(bool tf) {this->batch_grid = tf;} + protected: + + bool new_ug; + bool FirstCallToUSER_GRAPH; + + int update_time_chart; /* milliseconds, maybe read */ + int PanelHeight; + int PanelWidth; + std::map Symbol_map; + std::vector Color_vector; + std::string chart_title; + std::vector axis_titles; + LDBLE axis_scale_x[5]; + LDBLE axis_scale_y[5]; + LDBLE axis_scale_y2[5]; + + int chart_type; + bool graph_initial_solutions; + bool connect_simulations; + int shifts_as_points; + int colnr; + int ColumnOffset; + bool AddSeries; + + int prev_advection_step; + int prev_transport_step; + + int prev_sim_no; + + bool end_timer; + bool done; + + std::vector csv_file_names; + std::vector CurvesCSV; + std::vector Curves; + bool curve_added; + bool point_added; + + struct rate *user_graph; + // C++ for rate struct + std::string rate_name; + std::list rate_command_list; + std::list rate_command_list_original; + bool rate_new_def; + + int default_symbol; + int default_symbol_csv; + int default_color; + int default_color_csv; + + // temporary storage before stored graph_x/y/sy data are stored in curves + // Initialize_graph_pts and Finalize_graph_pts use this storage. + LDBLE graph_x; + std::map graph_y; + std::map secondary_y; + + // temporary plotxy curve definitions before stored in curves + // a plotxy curve is copied to Curves when cmdplotxy is encountered + // this keeps order correct between plotxy and graph_x/y/sy + std::vector new_plotxy_curves; + + // temporary headings until stored during basic_run + std::vector new_headings; + std::vector headings_original; + bool active; + bool detach; + bool form_started; + class Phreeqc * phreeqc_ptr; + + bool batch_background; + bool batch_grid; + std::string batch_fn; + chart_batch_type batch; + const static std::vector < std::string > vopts; + + public: + int usingResource; + +}; +#endif // MULTICHART +#endif // !defined(CHARTOBJECT_H_INCLUDED) diff --git a/phreeqcpp/CurveObject.cpp b/phreeqcpp/CurveObject.cpp new file mode 100644 index 00000000..42710b99 --- /dev/null +++ b/phreeqcpp/CurveObject.cpp @@ -0,0 +1,35 @@ +// CurveObject.cpp: implementation of the CurveObject class. +// +////////////////////////////////////////////////////////////////////// +#ifdef MULTICHART +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include "CurveObject.h" + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CurveObject::CurveObject() + // + // default constructor for cxxExchComp + // +{ + x.clear(); + y.clear(); + this->id = ""; + this->symbol = ""; + this->color = ""; + this->y_axis = 1; + this->line_w = 1.0; + this->symbol_size = 6.0; +} + + +CurveObject::~CurveObject() +{ +} + +#endif // MULTICHART \ No newline at end of file diff --git a/phreeqcpp/CurveObject.h b/phreeqcpp/CurveObject.h new file mode 100644 index 00000000..d491fd36 --- /dev/null +++ b/phreeqcpp/CurveObject.h @@ -0,0 +1,79 @@ +#if !defined(CURVEOBJECT_H_INCLUDED) +#define CURVEOBJECT_H_INCLUDED +#include +#include +#include "phrqtype.h" +class CurveObject +{ + +public: + CurveObject(); + ~CurveObject(); + void Set_id(std::string s) + { + this->id = s; + } + std::string &Get_id(void) + { + return this->id; + } + void Set_color(std::string s) + { + this->color = s; + } + std::string &Get_color(void) + { + return this->color; + } + void Set_symbol(std::string s) + { + this->symbol = s; + } + std::string &Get_symbol(void) + { + return this->symbol; + } + void Set_symbol_size(LDBLE f) + { + this->symbol_size = f; + } + LDBLE Get_symbol_size(void) + { + return this->symbol_size; + } + void Set_line_w(LDBLE f) + { + this->line_w = f; + } + LDBLE Get_line_w(void) + { + return this->line_w; + } + void Set_y_axis(int f) + { + this->y_axis = f; + } + std::vector & Get_x() + { + return this->x; + } + std::vector & Get_y() + { + return this->y; + } + int Get_y_axis() + { + return this->y_axis; + } + +protected: + std::vector x, y; + std::string id, color, symbol; + int y_axis; + LDBLE line_w, symbol_size; + +public: + +}; + +#endif // !defined(CURVEOBJECT_H_INCLUDED) diff --git a/phreeqcpp/Dictionary.cpp b/phreeqcpp/Dictionary.cpp new file mode 100644 index 00000000..bd5348d8 --- /dev/null +++ b/phreeqcpp/Dictionary.cpp @@ -0,0 +1,32 @@ +#include "Dictionary.h" +Dictionary::Dictionary(void) +{ +} + +Dictionary::Dictionary(std::string & words_string) +{ + std::istringstream words_stream(words_string); + char str[256]; + while (words_stream.getline(str,256)) + { + this->Find(str); + } +} +Dictionary::~Dictionary(void) +{ +} + +int +Dictionary::Find(std::string str) +{ + std::map::iterator it = this->dictionary_map.find(str); + if (it != this->dictionary_map.end()) + { + return it->second; + } + int i = this->MapSize(); + this->dictionary_map[str] = i; + this->words.push_back(str); + this->dictionary_oss << str << "\n"; + return i; +} diff --git a/phreeqcpp/Dictionary.h b/phreeqcpp/Dictionary.h new file mode 100644 index 00000000..ffe86fc5 --- /dev/null +++ b/phreeqcpp/Dictionary.h @@ -0,0 +1,28 @@ +#if !defined(DICTIONARY_H_INCLUDED) +#define DICTIONARY_H_INCLUDED +#include +#include +#include +#include +class Phreeqc; +class Dictionary +{ +public: + Dictionary(void); + Dictionary(std::string & words_string); + ~Dictionary(void); + int Find(std::string str); + int MapSize() {return (int) this->dictionary_map.size();} + int OssSize() {return (int) this->dictionary_oss.str().size();} + std::ostringstream &GetDictionaryOss() {return this->dictionary_oss;} + std::vector &GetWords() {return this->words;} + +protected: + std::map dictionary_map; + std::vector words; + std::ostringstream dictionary_oss; + +}; + +#endif // !defined(DICTIONARY_H_INCLUDED) + diff --git a/phreeqcpp/ExchComp.cxx b/phreeqcpp/ExchComp.cxx new file mode 100644 index 00000000..5af520b7 --- /dev/null +++ b/phreeqcpp/ExchComp.cxx @@ -0,0 +1,450 @@ +// ExchComp.cxx: implementation of the cxxExchComp class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // std::cout std::cerr +#include // assert +#include // std::sort +#include + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "ExchComp.h" +#include "phqalloc.h" +#include "Dictionary.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxExchComp::cxxExchComp(PHRQ_io *io) + // + // default constructor for cxxExchComp + // + : PHRQ_base(io) +{ + totals.type = cxxNameDouble::ND_ELT_MOLES; + la = 0.0; + charge_balance = 0.0; + phase_proportion = 0.0; + formula_z = 0.0; +} +#ifdef SKIP +cxxExchComp::cxxExchComp(std::vector < cxxExchComp > &ec_vector, + std::vector < LDBLE >&f_vector) + // + // constructor for cxxExchComp from mixing + // +{ + if (ec_vector.size() <= 0) + return; + // + // check consistency + // + std::vector < LDBLE >::iterator it_f; + std::vector < cxxExchComp >::iterator it_ec; + // set fixed variables + it_ec = ec_vector.begin(); + this->formula = it_ec->formula; + this->formula_totals = it_ec->formula_totals; + this->formula_z = it_ec->formula_z; + this->phase_name = it_ec->phase_name; + this->rate_name = it_ec->rate_name; + it_ec++; + for (; it_ec != ec_vector.end(); it_ec++) + { + if (it_ec->formula != this->formula || + it_ec->formula_z != this->formula_z || + it_ec->phase_name != this->phase_name || + this->rate_name != this->rate_name) + { + error_msg + ("Mixing exchange components. Formula, z, phase_name, or rate_name did not match", + STOP); + } + } + // calculate sum of extensive factors + LDBLE sum_extensive = 0; + for (it_f = f_vector.begin(); it_f != f_vector.end(); it_f++) + { + sum_extensive += *it_f; + } + this->moles = 0; + this->la = 0; + this->charge_balance = 0; + this->phase_proportion = 0; + this->totals.clear(); + this->totals.type = cxxNameDouble::ND_ELT_MOLES; + it_ec = ec_vector.begin(); + it_f = f_vector.begin(); + for (; it_ec != ec_vector.end();) + { + LDBLE extensive = *it_f; + LDBLE intensive = extensive / sum_extensive; + this->moles += it_ec->moles * extensive; + this->la += it_ec->la * intensive; + this->charge_balance += it_ec->charge_balance * extensive; + this->phase_proportion += it_ec->phase_proportion * intensive; + this->totals.add_extensive(it_ec->totals, extensive); + it_ec++; + it_f++; + } +} +#endif +cxxExchComp::~cxxExchComp() +{ +} + +void +cxxExchComp::dump_xml(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Exch_Comp element and attributes + + s_oss << indent0 << "formula=\"" << this->formula << "\"" << "\n"; + s_oss << indent0 << "formula_z=\"" << this-> + formula_z << "\"" << "\n"; + s_oss << indent0 << "la=\"" << this->la << "\"" << "\n"; + s_oss << indent0 << "charge_balance=\"" << this-> + charge_balance << "\"" << "\n"; + if (this->phase_name.size() != 0) + { + s_oss << indent0 << "phase_name=\"" << this->phase_name << "\"" << "\n"; + } + if (this->rate_name.size() != 0) + { + s_oss << indent0 << "rate_name=\"" << this-> + rate_name << "\"" << "\n"; + } + s_oss << indent0 << "phase_proportion=\"" << this-> + phase_proportion << "\"" << "\n"; + + // totals + s_oss << indent0; + s_oss << "totals.dump_xml(s_oss, indent + 1); +} + +void +cxxExchComp::dump_raw(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Exch_Comp element and attributes + //s_oss << indent1 << "# critical values" << "\n"; + + // totals + s_oss << indent0 << "# EXCHANGE_MODIFY candidate identifiers #\n"; + s_oss << indent0; + s_oss << "-totals" << "\n"; + this->totals.dump_raw(s_oss, indent + 1); + + s_oss << indent0 << "-charge_balance " << this->charge_balance << "\n"; + + //s_oss << indent1 << "# Noncritical values" << "\n"; + s_oss << indent0 << "-la " << this->la << "\n"; + + if (this->phase_name.size() != 0) + { + s_oss << indent0 << "-phase_name " << this->phase_name << "\n"; + } + if (this->rate_name.size() != 0) + { + s_oss << indent0 << "-rate_name " << this->rate_name << "\n"; + } + s_oss << indent0 << "-phase_proportion " << this->phase_proportion << "\n"; + s_oss << indent0 << "-formula_z " << this->formula_z << "\n"; +} + +void +cxxExchComp::read_raw(CParser & parser, bool check) +{ + std::string str; + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + opt_save = CParser::OPT_ERROR; + bool la_defined(false); + bool charge_balance_defined(false); + bool formula_z_defined(false); + + for (;;) + { + int opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_KEYWORD; + // Allow return to Exchange for more processing + break; + + case 0: // formula + warning_msg("-formula ignored. Defined with -component."); + break; + + case 1: // moles + parser.warning_msg("-moles is an obsolete identifier"); + break; + + case 2: // la + if (!(parser.get_iss() >> this->la)) + { + this->la = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for la.", + PHRQ_io::OT_CONTINUE); + } + la_defined = true; + break; + + case 3: // charge_balance + if (!(parser.get_iss() >> this->charge_balance)) + { + this->charge_balance = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for charge_balance.", + PHRQ_io::OT_CONTINUE); + } + charge_balance_defined = true; + break; + + case 4: // phase_name + if (!(parser.get_iss() >> str)) + { + this->phase_name.clear(); + parser.incr_input_error(); + parser.error_msg("Expected string value for phase_name.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->phase_name = str; + } + break; + + case 5: // rate_name + if (!(parser.get_iss() >> str)) + { + this->rate_name.clear(); + parser.incr_input_error(); + parser.error_msg("Expected string value for rate_name.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->rate_name = str; + } + break; + + case 6: // formula_z + if (!(parser.get_iss() >> this->formula_z)) + { + this->formula_z = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for formula_z.", + PHRQ_io::OT_CONTINUE); + } + formula_z_defined = true; + break; + + case 7: // phase_proportion + if (!(parser.get_iss() >> this->phase_proportion)) + { + this->phase_proportion = 0; + parser.incr_input_error(); + parser. + error_msg("Expected numeric value for phase_proportion.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 8: // totals + if (this->totals.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected element name and molality for ExchComp totals.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 8; + break; + + case 9: // formula_totals + parser.warning_msg("-formula_totals is an obsolete identifier"); + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (check) + { + // members that must be defined + if (la_defined == false) + { + parser.incr_input_error(); + parser.error_msg("La not defined for ExchComp input.", + PHRQ_io::OT_CONTINUE); + } + if (charge_balance_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Charge_balance not defined for ExchComp input.", + PHRQ_io::OT_CONTINUE); + } + if (formula_z_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Formula_z not defined for ExchComp input.", + PHRQ_io::OT_CONTINUE); + } + } +} +void +cxxExchComp::add(const cxxExchComp & addee, LDBLE extensive) +{ + LDBLE f1, f2; + if (extensive == 0.0) + return; + if (addee.formula.size() == 0) + return; + // this and addee must have same formula + // otherwise generate a new exchcomp with multiply + f1 = 0.5; + f2 = 0.5; + + if (this->formula.size() == 0 && addee.formula.size() == 0) + { + return; + } + assert(this->formula == addee.formula); + assert(this->formula_z == addee.formula_z); + if (this->formula.size() == 0 && addee.formula.size() != 0) + { + this->formula = addee.formula; + } + this->totals.add_extensive(addee.totals, extensive); + this->la = f1 * this->la + f2 * addee.la; + this->charge_balance += addee.charge_balance * extensive; + + if (this->phase_name != addee.phase_name) + { + std::ostringstream oss; + oss << + "Cannot mix two exchange components with same formula and different related phases, " + << this->formula; + error_msg(oss.str().c_str(), CONTINUE); + return; + } + else if (this->phase_name.size() != 0) + { + this->phase_proportion = + this->phase_proportion * f1 + addee.phase_proportion * f2; + } + if (this->rate_name != addee.rate_name) + { + std::ostringstream oss; + oss << + "Cannot mix two exchange components with same formula and different related kinetics, " + << this->formula; + error_msg(oss.str().c_str(), CONTINUE); + return; + } + else if (this->rate_name.size() != 0) + { + this->phase_proportion = + this->phase_proportion * f1 + addee.phase_proportion * f2; + } + if ((this->rate_name.size() != 0 && addee.phase_name.size() != 0) || + (this->phase_name.size() != 0 && addee.rate_name.size() != 0)) + { + std::ostringstream oss; + oss << + "Cannot mix exchange components related to phase with exchange components related to kinetics, " + << this->formula; + error_msg(oss.str().c_str(), CONTINUE); + return; + } +} +void +cxxExchComp::multiply(LDBLE extensive) +{ + this->totals.multiply(extensive); + this->charge_balance *= extensive; + this->phase_proportion *= extensive; +} + +void +cxxExchComp::Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles) +{ + ints.push_back(dictionary.Find(this->formula)); + this->totals.Serialize(dictionary, ints, doubles); + doubles.push_back(this->la); + doubles.push_back(this->charge_balance); + ints.push_back(dictionary.Find(this->phase_name)); + doubles.push_back(this->phase_proportion); + ints.push_back(dictionary.Find(this->rate_name)); + doubles.push_back(this->formula_z); +} + +void +cxxExchComp::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +{ + this->formula = dictionary.GetWords()[ints[ii++]]; + this->totals.Deserialize(dictionary, ints, doubles, ii, dd); + this->la = doubles[dd++]; + this->charge_balance = doubles[dd++]; + this->phase_name = dictionary.GetWords()[ints[ii++]]; + this->phase_proportion = doubles[dd++]; + this->rate_name = dictionary.GetWords()[ints[ii++]]; + this->formula_z = doubles[dd++]; +} + + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("formula"), // 0 + std::vector< std::string >::value_type("moles"), // 1 + std::vector< std::string >::value_type("la"), // 2 + std::vector< std::string >::value_type("charge_balance"), // 3 + std::vector< std::string >::value_type("phase_name"), // 4 + std::vector< std::string >::value_type("rate_name"), // 5 + std::vector< std::string >::value_type("formula_z"), // 6 + std::vector< std::string >::value_type("phase_proportion"), // 7 + std::vector< std::string >::value_type("totals"), // 8 + std::vector< std::string >::value_type("formula_totals") // 9 +}; +const std::vector< std::string > cxxExchComp::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); \ No newline at end of file diff --git a/phreeqcpp/ExchComp.h b/phreeqcpp/ExchComp.h new file mode 100644 index 00000000..c2846f7a --- /dev/null +++ b/phreeqcpp/ExchComp.h @@ -0,0 +1,125 @@ +#if !defined(EXCHCOMP_H_INCLUDED) +#define EXCHCOMP_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NameDouble.h" + +class cxxExchComp: public PHRQ_base +{ + + public: + cxxExchComp(PHRQ_io *io=NULL); + virtual ~cxxExchComp(); + + void dump_xml(std::ostream & os, unsigned int indent = 0) const; + + void dump_raw(std::ostream & s_oss, unsigned int indent) const; + + void read_raw(CParser & parser, bool check=true); + + const std::string &Get_formula() const + { + return this->formula; + } + void Set_formula(const char *cstring) + { + if (cstring != NULL) + this->formula = std::string(cstring); + else + this->formula.clear(); + } + LDBLE Get_la() const + { + return this->la; + } + void Set_la(LDBLE d) + { + this->la = d; + } + LDBLE Get_charge_balance() const + { + return this->charge_balance; + } + void Set_charge_balance(LDBLE d) + { + this->charge_balance = d; + } + const std::string &Get_phase_name() const + { + return this->phase_name; + } + void Set_phase_name(const char *cstring) + { + if (cstring != NULL) + this->phase_name = std::string(cstring); + else + this->phase_name.clear(); + } + LDBLE Get_phase_proportion() const + { + return this->phase_proportion; + } + void Set_phase_proportion(LDBLE d) + { + this->phase_proportion = d; + } + const std::string &Get_rate_name() const + { + return this->rate_name; + } + void Set_rate_name(const char *cstring) + { + if (cstring != NULL) + this->rate_name = std::string(cstring); + else + this->rate_name.clear(); + } + LDBLE Get_formula_z() const + { + return this->formula_z; + } + void Set_formula_z(LDBLE d) + { + this->formula_z = d; + } + void Set_totals(struct elt_list *e_l, int count) + { + this->totals = cxxNameDouble(e_l, count); + } + void Set_totals(struct elt_list *e_l) + { + this->totals = cxxNameDouble(e_l); + } + void Set_totals(cxxNameDouble nd) + { + this->totals = nd; + } + cxxNameDouble & Get_totals() {return (this->totals);} + const cxxNameDouble & Get_totals()const {return (this->totals);} + + void add(const cxxExchComp & comp, LDBLE extensive); + void multiply(LDBLE extensive); + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + std::string formula; + // EXCHANGE_MODIFY candidates + cxxNameDouble totals; + LDBLE la; + LDBLE charge_balance; + std::string phase_name; + LDBLE phase_proportion; + std::string rate_name; + LDBLE formula_z; // charge on formula + const static std::vector < std::string > vopts; + public: + +}; + +#endif // !defined(EXCHCOMP_H_INCLUDED) diff --git a/phreeqcpp/Exchange.cxx b/phreeqcpp/Exchange.cxx new file mode 100644 index 00000000..117123dd --- /dev/null +++ b/phreeqcpp/Exchange.cxx @@ -0,0 +1,492 @@ +// Exchange.cxx: implementation of the cxxExchange class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // std::cout std::cerr +#include // assert +#include // std::sort + +#include "Utils.h" // define first + +#include "Phreeqc.h" +#include "cxxMix.h" +#include "Exchange.h" +#include "phqalloc.h" + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxExchange::cxxExchange(PHRQ_io *io) + // + // default constructor for cxxExchange + // +: cxxNumKeyword(io) +{ + new_def = false; + solution_equilibria = false; + n_solution = -999; + pitzer_exchange_gammas = true; +} +cxxExchange::cxxExchange(const std::map < int, cxxExchange > &entities, + cxxMix & mix, int l_n_user, PHRQ_io *io): +cxxNumKeyword(io) +{ + this->n_user = this->n_user_end = l_n_user; + this->pitzer_exchange_gammas = true; + this->new_def = false; + this->n_solution = -999; +// +// Mix exchangers +// + const std::map < int, LDBLE >&mixcomps = mix.Get_mixComps(); + std::map < int, LDBLE >::const_iterator it; + for (it = mixcomps.begin(); it != mixcomps.end(); it++) + { + if (entities.find(it->first) != entities.end()) + { + const cxxExchange *entity_ptr = + &(entities.find(it->first)->second); + this->add(*entity_ptr, it->second); + this->pitzer_exchange_gammas = entity_ptr->pitzer_exchange_gammas; + } + } +} + + +cxxExchange::~cxxExchange() +{ +} +bool +cxxExchange::Get_related_phases() const +{ + for (size_t i = 0; i < this->exchange_comps.size(); i++) + { + if (this->exchange_comps[i].Get_phase_name().size() > 0) + { + return (true); + } + } + return (false); +} + +bool +cxxExchange::Get_related_rate() const +{ + for (size_t i = 0; i < this->exchange_comps.size(); i++) + { + if (this->exchange_comps[i].Get_rate_name().size() > 0) + { + return (true); + } + } + return (false); +} + +void +cxxExchange::dump_xml(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Exchange element and attributes + s_oss << indent0; + s_oss << " + pitzer_exchange_gammas << "\"" << "\n"; + + // components + s_oss << indent1; + s_oss << "exchange_comps.size(); j++) + { + this->exchange_comps[j].dump_xml(s_oss, indent + 2); + } + + return; +} +#ifdef SKIP +void +cxxExchange::dump_xml(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Exchange element and attributes + s_oss << indent0; + s_oss << " + pitzer_exchange_gammas << "\"" << "\n"; + + // components + s_oss << indent1; + s_oss << "::const_iterator it = exchComps.begin(); + it != exchComps.end(); ++it) + { + (*it).second.dump_xml(s_oss, indent + 2); + } + + return; +} +#endif +void +cxxExchange::dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Exchange element and attributes + s_oss << indent0; + int n_user_local = (n_out != NULL) ? *n_out : this->n_user; + s_oss << "EXCHANGE_RAW " << n_user_local << " " << this->description << "\n"; + + s_oss << indent1 << "# EXCHANGE_MODIFY candidate identifiers #\n"; + s_oss << indent1; + s_oss << "-exchange_gammas " << (this->pitzer_exchange_gammas ? 1 : 0) << "\n"; + // exchComps + for (size_t i = 0; i < this->exchange_comps.size(); i++) + { + s_oss << indent1; + s_oss << "-component " << this->exchange_comps[i].Get_formula() << "\n"; + this->exchange_comps[i].dump_raw(s_oss, indent + 2); + } + + s_oss << indent1 << "# EXCHANGE_MODIFY candidates with new_def=true #\n"; + s_oss << indent1; + s_oss << "-new_def " << 0 << "\n"; + s_oss << indent1; + s_oss << "-solution_equilibria " << 0 << "\n"; + s_oss << indent1; + s_oss << "-n_solution " << this->n_solution << "\n"; + + s_oss << indent1 << "# Exchange workspace variables #\n"; + s_oss << indent1; + s_oss << "-totals" << "\n"; + this->totals.dump_raw(s_oss, indent + 1); + + return; +} +void +cxxExchange::read_raw(CParser & parser, bool check) +{ + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + bool useLastLine(false); + + // Read exchange number and description + this->read_number_description(parser); + this->Set_new_def(false); + + bool pitzer_exchange_gammas_defined(false); + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + opt = parser.getOptionFromLastLine(vopts, next_char, true); + } + useLastLine = false; + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input in EXCH_COMP_RAW keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + break; + + case 0: // pitzer_exchange_gammas + case 2: // exchange_gammas + if (!(parser.get_iss() >> this->pitzer_exchange_gammas)) + { + this->pitzer_exchange_gammas = false; + parser.incr_input_error(); + parser. + error_msg + ("Expected boolean value for pitzer_exchange_gammas.", + PHRQ_io::OT_CONTINUE); + } + pitzer_exchange_gammas_defined = true; + break; + case 1: // component + { + std::string str; + if (!(parser.get_iss() >> str)) + { + parser.incr_input_error(); + parser.error_msg("Expected string value for component name.", + PHRQ_io::OT_CONTINUE); + } + else + { + cxxExchComp temp_comp(this->io); + temp_comp.Set_formula(str.c_str()); + cxxExchComp *comp_ptr = this->Find_comp(str); + if (comp_ptr) + { + temp_comp = *comp_ptr; + } + temp_comp.read_raw(parser, check); + if (comp_ptr) + { + for (size_t j = 0; j < this->exchange_comps.size(); j++) + { + if (Utilities::strcmp_nocase(this->exchange_comps[j].Get_formula().c_str(), str.c_str()) == 0) + { + this->exchange_comps[j] = temp_comp; + } + } + } + else + { + this->exchange_comps.push_back(temp_comp); + } + useLastLine = true; + } + } + break; + case 3: // new_def + if (!(parser.get_iss() >> this->new_def)) + { + this->new_def = false; + parser.incr_input_error(); + parser. + error_msg + ("Expected boolean value for new_def.", + PHRQ_io::OT_CONTINUE); + } + break; + case 4: // solution_equilibria + if (!(parser.get_iss() >> this->solution_equilibria)) + { + this->solution_equilibria = false; + parser.incr_input_error(); + parser. + error_msg + ("Expected boolean value for solution_equilibria.", + PHRQ_io::OT_CONTINUE); + } + break; + case 5: // n_solution + if (!(parser.get_iss() >> this->n_solution)) + { + this->n_solution = -999; + parser.incr_input_error(); + parser. + error_msg + ("Expected integer value for n_solution.", + PHRQ_io::OT_CONTINUE); + } + break; + case 6: // totals + if (this->totals.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected element name and molality for Exchange totals.", + PHRQ_io::OT_CONTINUE); + } + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + // members that must be defined + if (check) + { + if (pitzer_exchange_gammas_defined == false) + { + parser.incr_input_error(); + parser. + error_msg + ("Pitzer_exchange_gammsa not defined for EXCHANGE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + } + this->Sort_comps(); +} +void +cxxExchange::add(const cxxExchange & addee, LDBLE extensive) + // + // Add existing exchange to "this" exchange + // +{ + // exchComps + if (extensive == 0.0) + return; + for (size_t i = 0; i < addee.exchange_comps.size(); i++) + { + size_t j; + for (j = 0; j < this->Get_exchange_comps().size(); j++) + if (addee.exchange_comps[i].Get_formula() == this->exchange_comps[j].Get_formula()) + { + this->exchange_comps[j].add(addee.exchange_comps[i], extensive); + break; + } + if (j == this->exchange_comps.size()) + { + cxxExchComp exc = addee.exchange_comps[i]; + exc.multiply(extensive); + this->exchange_comps.push_back(exc); + } + } + this->pitzer_exchange_gammas = addee.pitzer_exchange_gammas; +} +void +cxxExchange::totalize() +{ + this->totals.clear(); + // component structures + for (size_t i = 0; i < this->exchange_comps.size(); i++) + { + this->totals.add_extensive(this->exchange_comps[i].Get_totals(), 1.0); + this->totals.add("Charge", this->exchange_comps[i].Get_charge_balance()); + } + return; +} +bool +cxxExchange::Get_pitzer_exchange_gammas() const +{ + return this->pitzer_exchange_gammas; +} +void +cxxExchange::Set_pitzer_exchange_gammas(bool b) +{ + this->pitzer_exchange_gammas = b; +} +const cxxNameDouble & +cxxExchange::Get_totals() const +{ + return totals; +} +cxxExchComp *cxxExchange::Find_comp(std::string s) +{ + for (size_t i = 0; i < this->exchange_comps.size(); i++) + { + cxxNameDouble nd(this->exchange_comps[i].Get_totals()); + cxxNameDouble::iterator nd_it; + for (nd_it = nd.begin(); nd_it != nd.end(); nd_it++) + { + if(nd_it->first == s) + { + return (&(this->exchange_comps[i])); + } + } + } + return NULL; +} +void cxxExchange:: +Sort_comps(void) +{ + // sort comps + { + std::map comp_map; + for (size_t i = 0; i < this->exchange_comps.size(); i++) + { + comp_map[this->exchange_comps[i].Get_formula()] = this->exchange_comps[i]; + } + this->exchange_comps.clear(); + std::map::iterator it; + for (it = comp_map.begin(); it != comp_map.end(); it++) + { + this->exchange_comps.push_back(it->second); + } + } +} +/* ---------------------------------------------------------------------- */ +void +cxxExchange::Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles) +/* ---------------------------------------------------------------------- */ +{ + ints.push_back(this->n_user); + ints.push_back((int) this->exchange_comps.size()); + for (size_t i = 0; i < this->exchange_comps.size(); i++) + { + exchange_comps[i].Serialize(dictionary, ints, doubles); + } + ints.push_back(this->pitzer_exchange_gammas ? 1 : 0); + ints.push_back(this->new_def ? 1 : 0); + ints.push_back(this->solution_equilibria ? 1 : 0); + ints.push_back(this->n_solution); + this->totals.Serialize(dictionary, ints, doubles); + +} + +/* ---------------------------------------------------------------------- */ +void +cxxExchange::Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd) +/* ---------------------------------------------------------------------- */ +{ + this->n_user = ints[ii++]; + this->n_user_end = this->n_user; + this->description = " "; + + int count = ints[ii++]; + this->exchange_comps.clear(); + for (int n = 0; n < count; n++) + { + cxxExchComp ec; + ec.Deserialize(dictionary, ints, doubles, ii, dd); + this->exchange_comps.push_back(ec); + } + this->pitzer_exchange_gammas = (ints[ii++] != 0); + this->new_def = (ints[ii++] != 0); + this->solution_equilibria = (ints[ii++] != 0); + this->n_solution = ints[ii++]; + this->totals.Deserialize(dictionary, ints, doubles, ii, dd); + +} + + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("pitzer_exchange_gammas"), // 0 + std::vector< std::string >::value_type("component"), // 1 + std::vector< std::string >::value_type("exchange_gammas"), // 2 + std::vector< std::string >::value_type("new_def"), // 3 + std::vector< std::string >::value_type("solution_equilibria"), // 4 + std::vector< std::string >::value_type("n_solution"), // 5 + std::vector< std::string >::value_type("totals") // 6 +}; +const std::vector< std::string > cxxExchange::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/Exchange.h b/phreeqcpp/Exchange.h new file mode 100644 index 00000000..f387f2fe --- /dev/null +++ b/phreeqcpp/Exchange.h @@ -0,0 +1,74 @@ +#if !defined(EXCHANGE_H_INCLUDED) +#define EXCHANGE_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include "NumKeyword.h" +#include "ExchComp.h" +#include "NameDouble.h" +#include "PHRQ_base.h" +class cxxMix; + +class cxxExchange:public cxxNumKeyword +{ + +public: + cxxExchange(PHRQ_io *io=NULL); + cxxExchange(const std::map < int, cxxExchange > &exchange_map, + cxxMix & mx, int n_user, PHRQ_io *io=NULL); + ~cxxExchange(); + + void dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out=NULL) const; + + void read_raw(CParser & parser, bool check = true); + + bool Get_related_phases(void) const; + + bool Get_related_rate(void) const; + + bool Get_pitzer_exchange_gammas() const; + void Set_pitzer_exchange_gammas(bool b); + + bool Get_new_def(void) const {return this->new_def;} + void Set_new_def(bool tf) {this->new_def = tf;} + bool Get_solution_equilibria(void) const {return this->solution_equilibria;} + void Set_solution_equilibria(bool tf) {this->solution_equilibria = tf;} + int Get_n_solution(void) const {return this->n_solution;} + void Set_n_solution(int i) {this->n_solution = i;} + cxxExchComp *Find_comp(std::string s); + std::vector & Get_exchange_comps(void) {return this->exchange_comps;} + const std::vector & Get_exchange_comps(void)const {return this->exchange_comps;} + void Set_exchange_comps(std::vector &c) {this->exchange_comps = c;} + + void Sort_comps(void); + void totalize(); + + const cxxNameDouble & Get_totals() const; + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + void add(const cxxExchange & addee, LDBLE extensive); + // not written + void dump_xml(std::ostream & os, unsigned int indent = 0) const; + +protected: + // EXCHANGE_MODIFY candidates + std::vector exchange_comps; + bool pitzer_exchange_gammas; + + // EXCHANGE_MODIFY candidates with new_def=true + bool new_def; + bool solution_equilibria; + int n_solution; + + // exchange workspace variables + cxxNameDouble totals; + const static std::vector < std::string > vopts; +public: + +}; + +#endif // !defined(EXCHANGE_H_INCLUDED) diff --git a/phreeqcpp/Form1.2005.resX b/phreeqcpp/Form1.2005.resX new file mode 100644 index 00000000..fc36b375 --- /dev/null +++ b/phreeqcpp/Form1.2005.resX @@ -0,0 +1,33 @@ + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + + + AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAP8AMzOaAGgymgA0Mc0AmWVoAGdlmwBjZssAy5kvAP+ZNQDLmmMAzpmVAP/MLQDLzWUA/8xjAP3/ + mAAAAAAAiIiIiIiIiIiIgAAIhIiIhHAAAAB3KXcXcAeQAAeTk5fQDd0ADdoK3b27uwALugm7u7u7AAtz + k9vbuwAAu2u7Xbu7AAva29u5zMzAAMzMzMzu7u4ADu7szOAO7gAO7u7u4ADuAA7u7u7uAAAA7u7u7u7g + AA7u7u7u7u7u7u7u7u4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA + + + \ No newline at end of file diff --git a/phreeqcpp/Form1.h b/phreeqcpp/Form1.h new file mode 100644 index 00000000..6a17674a --- /dev/null +++ b/phreeqcpp/Form1.h @@ -0,0 +1,1151 @@ +#pragma once +#include +#include + +namespace zdg_ui2 { + using namespace System; + //using namespace System::ComponentModel; + using namespace System::Resources; + using namespace System::Windows::Forms; + using namespace System::Drawing; + using namespace System::Drawing::Imaging; + using namespace System::Threading; + using namespace ZedGraph; + //using namespace System::Runtime::InteropServices; + //[DllImport("gdi32.dll")] + ////extern IntPtr CopyEnhMetaFileA(IntPtr hemfSrc, System::Text::StringBuilder* hNULL); + //extern IntPtr CopyEnhMetaFileA(IntPtr hemfSrc, String^ hNULL); +// Form1 is only used with MULTICHART + public ref class ChartObj : public System::Object + { + public: ChartObject* chartobject_ptr; + public: ChartObj(ChartObject* ptr) + { + this->chartobject_ptr = ptr; + } + }; + + public ref class Form1 : public System::Windows::Forms::Form + { + public: long int tickStart; + public: Form1(ChartObject *ptr) + { + this->chartobject_ptr = ptr; + if (Phreeqc* ptr = this->chartobject_ptr->Get_phreeqc()) + { + ptr->Get_chart_handler().Increment_active_charts(); + } + InitializeComponent(); + col_use = 0; + symbol_use = 0; + Y2 = false; + phreeqc_done = false; + Y2show = false; + background = true; + hints = true; + grid = true; + } + static void ThreadForm(Object^ data) + { + ChartObject *ptr = ((ChartObj^)(data))->chartobject_ptr; + Form1 ^myForm = gcnew Form1(ptr); + myForm->ShowDialog(); + myForm->~Form1(); + } + private: bool phreeqc_done; + + private: void SetSize() + { + zg1->Location = Point( 0, 0 ); + // Leave a small margin around the outside of the control + zg1->Size = System::Drawing::Size( ClientRectangle.Width - 0, + ClientRectangle.Height - 0 ); + } + + System::Void MyFormClosingEventHandler( + System::Object^ sender, + System::Windows::Forms::FormClosingEventArgs ^e) + { + ChartObject *chart = this->chartobject_ptr; + if (chart != NULL) + { + chart->Set_done(true); + System::Threading::Interlocked::Exchange(this->chartobject_ptr->usingResource, 0); + if (Phreeqc* ptr = chart->Get_phreeqc()) + { + ptr->Get_chart_handler().Decrement_active_charts(); + //chart->Set_phreeqc(NULL); + } + } + this->chartobject_ptr = NULL; + } + + System::Void Form1_Load(System::Object ^sender, System::EventArgs ^e) + { + CreateGraph( zg1 ); + SetSize(); + } + + System::Void Form1_Resize(System::Object ^sender, System::EventArgs ^e) + { + SetSize(); + } + + //static bool LogX, LogY, LogY2; + bool LogX, LogY, LogY2; + private: bool check_neg_log( int i, int i2) + { + ChartObject *chart = this->chartobject_ptr; + if (chart == NULL) return false; + std::vector &Curves = chart->Get_Curves(); + if (LogX && chart->Get_axis_scale_x()[4] == 10.0 && + Curves[i]->Get_x()[i2] <= 0) + { + if (Phreeqc* ptr = chart->Get_phreeqc()) + { + ptr->warning_msg("Obtained x_value <= 0, removing point..."); + } + //axis_scale_x[4] = NA; /* if reverting to linear... */ + //LogX = false; + return true; + } + if (Curves[i]->Get_y()[i2] <= 0 && + (chart->Get_axis_scale_y()[4] == 10.0 || + chart->Get_axis_scale_y2()[4] == 10.0)) + { + if (Curves[i]->Get_y_axis() == 2 && LogY2) + { + if (Phreeqc* ptr = chart->Get_phreeqc()) + { + ptr->warning_msg("Obtained sy_value <= 0, removing point......"); + } + //axis_scale_y2[4] = NA; + //LogY2 = false; + return true; + } + else if (LogY) + { + if (Phreeqc* ptr = chart->Get_phreeqc()) + { + ptr->warning_msg("Obtained y_value <= 0, removing point......"); + } + //axis_scale_y[4] = NA; + //LogY = false; + return true; + } + } + return false; + } + + private: PointPairList ^list; + int col_use, symbol_use; + bool Y2, Y2show; + // static cli::array ^ColorList = {"Red", "Green", "Blue", "Orange", "Magenta", "Yellow", "Black", "Cyan", "Brown", "Lime", "Gray" }; + static cli::array ^ColorList = {"Red", "Green", "Blue", "Orange", "Magenta", "Black", "Cyan", "Brown", "Lime", "Gray" }; + bool background, hints, grid; + + ZedGraph::GraphObjList ^GOL_no_hints; + ZedGraph::GraphObjList ^GOL_hints; + + void DefineCurves(GraphPane ^myPane, int init) + { + ChartObject *chart = this->chartobject_ptr; + if (chart == NULL) + { + return; + } + std::vector Curves; + size_t i; + for (i = 0; i < chart->Get_CurvesCSV().size(); i++) + { + Curves.push_back(chart->Get_CurvesCSV()[i]); + } + for (i = 0; i < chart->Get_Curves().size(); i++) + { + Curves.push_back(chart->Get_Curves()[i]); + } + + chart->Set_curve_added(false); + + // Set the titles and axis labels + myPane->Title->Text = gcnew String(chart->Get_chart_title().c_str()); + if (chart->Get_axis_titles().size() > 0) + myPane->XAxis->Title->Text = gcnew String(chart->Get_axis_titles()[0].c_str()); + if (chart->Get_axis_titles().size() > 1) + myPane->YAxis->Title->Text = gcnew String(chart->Get_axis_titles()[1].c_str()); + if (chart->Get_axis_titles().size() > 2) + myPane->Y2Axis->Title->Text = gcnew String(chart->Get_axis_titles()[2].c_str()); + + LineItem ^myCurve; + + Color col; + + String ^s_t; + if (chart->Get_axis_scale_x()[4] == 10.0) LogX = true; + else LogX = false; + if (chart->Get_axis_scale_y()[4] == 10.0) LogY = true; + else LogY = false; + if (chart->Get_axis_scale_y2()[4] == 10.0) LogY2 = true; + else LogY2 = false; + + //Rewrite all curves + zg1->GraphPane->CurveList->Clear(); + for (size_t i = 0; i < Curves.size(); i++) + { + // even curves with no data + //if (Curves[i]->Get_x().size() == 0) continue; + list = gcnew PointPairList(); + if (Curves[i]->Get_y_axis() == 2) + { + Y2 = true; + Y2show = true; + } + else + Y2 = false; + for (int i2 = 0; (i2 < (int) Curves[i]->Get_x().size()); i2++) + { + if ((LogX && Curves[i]->Get_x()[i2] <=0) + || (LogY && !Y2 && Curves[i]->Get_y()[i2] <=0) + || (LogY2 && Y2 && Curves[i]->Get_y()[i2] <=0)) + continue; + else + list->Add( Curves[i]->Get_x()[i2], Curves[i]->Get_y()[i2] ); + } + + col = Color::FromName(gcnew String(Curves[i]->Get_color().c_str())); + if (!col.IsKnownColor) + { + col = Color::FromName(ColorList[col_use]); + std::string newcol; + ToString(col.ToString(), newcol); + Utilities::replace("Color [","",newcol); + Utilities::replace("]","",newcol); + Curves[i]->Set_color(newcol); + + } + if (++col_use > 6) col_use = 0; + + SymbolType symb = chart->Return_SymbolType + (Curves[i]->Get_symbol()); + + // id + s_t = gcnew String(Curves[i]->Get_id().c_str()); + + + // Add curve to chart + myCurve = myPane->AddCurve( s_t, list, col, symb ); + + + // Curve with no points is invisible + if (Curves[i]->Get_x().size() == 0) + { + myCurve->IsVisible = false; + myCurve->Label->IsVisible = false; + } + + if (Curves[i]->Get_line_w() > 0.0) + myCurve->Line->Width = (float) Curves[i]->Get_line_w(); + else + myCurve->Line->IsVisible = false; + /* hmm... dash/dot don`t display well */ + // myCurve->Line->Style = System::Drawing::Drawing2D::DashStyle::Dot; + myCurve->Symbol->Fill = gcnew Fill( Color::FromName("White") ); + if (Curves[i]->Get_symbol_size() > 0.0) + myCurve->Symbol->Size = (float) Curves[i]->Get_symbol_size(); + else + myCurve->Symbol->IsVisible = false; + myCurve->Symbol->Border->Width = (float) Curves[i]->Get_line_w(); + if (Y2) + myCurve->IsY2Axis = true; + delete list; + } + + if (Y2show) + myPane->Legend->Position = ZedGraph::LegendPos::TopCenter; + else + myPane->Legend->Position = ZedGraph::LegendPos::Right; + myPane->Legend->FontSpec->Size = 12; + myPane->Legend->FontSpec->IsBold = false; + + // Show the x axis grid + myPane->XAxis->MajorGrid->IsVisible = true; + if (fabs(chart->Get_axis_scale_x()[0] - NA) > 1e-3) + myPane->XAxis->Scale->Min = chart->Get_axis_scale_x()[0]; + else + myPane->XAxis->Scale->MinAuto = true; + if (fabs(chart->Get_axis_scale_x()[1] - NA) > 1e-3) + myPane->XAxis->Scale->Max = chart->Get_axis_scale_x()[1]; + else + myPane->XAxis->Scale->MaxAuto = true; + if (fabs(chart->Get_axis_scale_x()[2] - NA) > 1e-3) + myPane->XAxis->Scale->MajorStep = chart->Get_axis_scale_x()[2]; + else + myPane->XAxis->Scale->MajorStepAuto = true; + if (fabs(chart->Get_axis_scale_x()[3] - NA) > 1e-3) + { + myPane->XAxis->Scale->MinorStep = chart->Get_axis_scale_x()[3]; + if (chart->Get_axis_scale_x()[3] == 0.0) + // remove minor tics + myPane->XAxis->MinorTic->Size = 0; + } + else + myPane->XAxis->Scale->MinorStepAuto = true; + if (chart->Get_axis_scale_x()[4] == 10.0) + myPane->XAxis->Type = AxisType::Log; + + // Make the Y axis scale red + // myPane->YAxis->Scale->FontSpec->FontColor = Color::Red; + // myPane->YAxis->Title->FontSpec->FontColor = Color::Red; + // turn off the opposite tics so the Y tics don`t show up on the Y2 axis + if (Y2show) + { + myPane->YAxis->MajorTic->IsOpposite = false; + myPane->YAxis->MinorTic->IsOpposite = false; + } + // Don`t display the Y zero line + myPane->YAxis->MajorGrid->IsZeroLine = false; + // Align the Y axis labels so they are flush to the axis + myPane->YAxis->Scale->Align = AlignP::Inside; + myPane->YAxis->MajorGrid->IsVisible = true; + if (fabs(chart->Get_axis_scale_y()[0] - NA) > 1e-3) + myPane->YAxis->Scale->Min = chart->Get_axis_scale_y()[0]; + else + myPane->YAxis->Scale->MinAuto = true; + if (fabs(chart->Get_axis_scale_y()[1] - NA) > 1e-3) + myPane->YAxis->Scale->Max = chart->Get_axis_scale_y()[1]; + else + myPane->YAxis->Scale->MaxAuto = true; + if (fabs(chart->Get_axis_scale_y()[2] - NA) > 1e-3) + myPane->YAxis->Scale->MajorStep = chart->Get_axis_scale_y()[2]; + else + myPane->YAxis->Scale->MajorStepAuto = true; + if (fabs(chart->Get_axis_scale_y()[3] - NA) > 1e-3) + { + myPane->YAxis->Scale->MinorStep = chart->Get_axis_scale_y()[3]; + if (chart->Get_axis_scale_y()[3] == 0.0) + // remove minor tics + myPane->YAxis->MinorTic->Size = 0; + } + else + myPane->YAxis->Scale->MinorStepAuto = true; + if (chart->Get_axis_scale_y()[4] == 10.0) + myPane->YAxis->Type = AxisType::Log; + + // Enable the Y2 axis display + if (Y2show) + { + myPane->Y2Axis->IsVisible = true; + // Make the Y2 axis scale blue + // myPane->Y2Axis->Scale->FontSpec->FontColor = Color::Blue; + // myPane->Y2Axis->Title->FontSpec->FontColor = Color::Blue; + // turn off the opposite tics so the Y2 tics don`t show up on the Y axis + myPane->Y2Axis->MajorTic->IsOpposite = false; + myPane->Y2Axis->MinorTic->IsOpposite = false; + // Don`t display the Y2 axis grid lines + myPane->Y2Axis->MajorGrid->IsVisible = false; + // Align the Y2 axis labels so they are flush to the axis + myPane->Y2Axis->Scale->Align = AlignP::Inside; + + if (fabs(chart->Get_axis_scale_y2()[0] - NA) > 1e-3) + myPane->Y2Axis->Scale->Min = chart->Get_axis_scale_y2()[0]; + else + myPane->Y2Axis->Scale->MinAuto = true; + if (fabs(chart->Get_axis_scale_y2()[1] - NA) > 1e-3) + myPane->Y2Axis->Scale->Max = chart->Get_axis_scale_y2()[1]; + else + myPane->Y2Axis->Scale->MaxAuto = true; + if (fabs(chart->Get_axis_scale_y2()[2] - NA) > 1e-3) + myPane->Y2Axis->Scale->MajorStep = chart->Get_axis_scale_y2()[2]; + else + myPane->Y2Axis->Scale->MajorStepAuto = true; + if (fabs(chart->Get_axis_scale_y2()[3] - NA) > 1e-3) + { + myPane->Y2Axis->Scale->MinorStep = chart->Get_axis_scale_y2()[3]; + if (chart->Get_axis_scale_y2()[3] == 0.0) + // remove minor tics + myPane->Y2Axis->MinorTic->Size = 0; + } + else + myPane->Y2Axis->Scale->MinorStepAuto = true; + if (chart->Get_axis_scale_y2()[4] == 10.0) + myPane->Y2Axis->Type = AxisType::Log; + } + + myPane->XAxis->MinorTic->IsOutside = false; + myPane->XAxis->MajorTic->IsOutside = false; + myPane->YAxis->MinorTic->IsOutside = false; + myPane->YAxis->MajorTic->IsOutside = false; + myPane->Y2Axis->MinorTic->IsOutside = false; + myPane->Y2Axis->MajorTic->IsOutside = false; + + // Fill the axis background with a gradient + //myPane->Chart->Fill = gcnew Fill( Color::White, Color::LightYellow, 45.0f ); /* FromArgb(255, 255, 224) */ + if (this->background) + { + myPane->Chart->Fill = gcnew Fill( Color::White, Color::FromArgb(255, 255, 230), 45.0f ); + } + else + { + myPane->Chart->Fill = gcnew Fill( Color::White, Color::White, 45.0f ); + } + if (this->grid) + { + myPane->XAxis->MajorGrid->IsVisible = true; + myPane->YAxis->MajorGrid->IsVisible = true; + } + else + { + myPane->XAxis->MajorGrid->IsVisible = false; + myPane->YAxis->MajorGrid->IsVisible = false; + } + // normalize pane size... + myPane->BaseDimension = 8.0F; + // increase bottom margin to accommodate text options... + myPane->Margin->Bottom = 15.0F; + + // Make sure auto scale, Refresh + zg1->AxisChange(); + zg1->Refresh(); + + } + + public: void CreateGraph( ZedGraphControl ^z1 ) { + // Get a reference to the GraphPane instance in the ZedGraphControl + GraphPane ^myPane = z1->GraphPane; + + // lock thread + while (0 != System::Threading::Interlocked::CompareExchange(this->chartobject_ptr->usingResource, 2, 0)) + { + System::Threading::Thread::Sleep(5); + } + + try + { + + DefineCurves(myPane, 0); + + // Add text boxes with instructions... + GOL_no_hints = gcnew ZedGraph::GraphObjList(myPane->GraphObjList); + + TextObj ^text; + text = gcnew TextObj( + L" Click right mouse for options... \0", + 0.01f, 0.99f, CoordType::PaneFraction, AlignH::Left, AlignV::Bottom ); + text->FontSpec->StringAlignment = StringAlignment::Near; + text->FontSpec->Size = 10; + text->FontSpec->FontColor = Color::Red; + text->ZOrder = ZOrder::H_BehindAll; + myPane->GraphObjList->Add( text ); + text = gcnew TextObj( + L" Press Alt + F4 to quit", + 0.81f, 0.99f, CoordType::PaneFraction, AlignH::Left, AlignV::Bottom ); + text->FontSpec->StringAlignment = StringAlignment::Near; + text->FontSpec->Size = 10; + text->FontSpec->FontColor = Color::Red; + text->ZOrder = ZOrder::H_BehindAll; + myPane->GraphObjList->Add( text ); + + GOL_hints = gcnew ZedGraph::GraphObjList(myPane->GraphObjList); + if (this->hints) + { + myPane->GraphObjList = GOL_hints; + } + else + { + myPane->GraphObjList = GOL_no_hints; + } + + // Enable scrollbars if needed... + /*z1->IsShowHScrollBar = true; + z1->IsShowVScrollBar = true; + z1->IsAutoScrollRange = true; + z1->IsScrollY2 = true;*/ + + // OPTIONAL: Show tooltips when the mouse hovers over a point + z1->IsShowPointValues = false; + z1->PointValueEvent += gcnew ZedGraphControl::PointValueHandler( this, + &Form1::MyPointValueHandler ); + + // OPTIONAL: Add a custom context menu item + z1->ContextMenuBuilder += gcnew ZedGraphControl::ContextMenuBuilderEventHandler( + this, &Form1::MyContextMenuBuilder ); + + // OPTIONAL: Handle the Zoom Event + z1->ZoomEvent += gcnew ZedGraphControl::ZoomEventHandler( this, + &Form1::MyZoomEvent ); + + // Size the control to fit the window + SetSize(); + + // Tell ZedGraph to calculate the axis ranges + // Note that you MUST call this after enabling IsAutoScrollRange, since AxisChange() sets + // up the proper scrolling parameters + + z1->AxisChange(); + // Make sure the Graph gets redrawn + z1->Invalidate(); + timer1->Interval = this->chartobject_ptr->Get_update_time_chart(); + timer1->Enabled = true; + timer1->Start(); + + tickStart = Environment::TickCount; + } + catch (...) + { + //unlock thread + int n = System::Threading::Interlocked::Exchange(this->chartobject_ptr->usingResource, 0); + assert(n == 2); + throw; + } + + //unlock thread + int n = System::Threading::Interlocked::Exchange(this->chartobject_ptr->usingResource, 0); + assert(n == 2); + } + + /// + /// Display customized tooltips when the mouse hovers over a point + /// + System::String ^MyPointValueHandler( ZedGraphControl ^control, GraphPane ^pane, + CurveItem ^curve, int iPt ) { + // Get the PointPair that is under the mouse + PointPair pt = curve[iPt]; + return curve->Label->Text + " is " + pt.Y.ToString( "e3" ) + " units at X = " + pt.X.ToString( "e3" ); + } + + // Add some explanation to the menu.. + void MyContextMenuBuilder( ZedGraphControl ^control, + System::Windows::Forms::ContextMenuStrip ^menuStrip, + Point mousePt, + ZedGraphControl::ContextMenuObjectState objState ) { + + // removes Copy + menuStrip->Items->RemoveAt(0); + // removes Save Image As + menuStrip->Items->RemoveAt(0); + + ToolStripMenuItem ^item = gcnew ToolStripMenuItem(); + item->Text = L"Zoom: left mouse + drag\nPan: middle mouse + drag"; + menuStrip->Items->Insert(5, item ); + + ToolStripMenuItem ^item3 = gcnew ToolStripMenuItem(); + item3->Text = L"Chart options..."; + item3->Click += gcnew System::EventHandler(this, &zdg_ui2::Form1::SetChartOptions ); + menuStrip->Items->Insert(0, item3 ); + + //ToolStripMenuItem ^item5 = gcnew ToolStripMenuItem(); + //item5->Text = L"Toggle Hints"; + //item5->Click += gcnew System::EventHandler(this, &zdg_ui2::Form1::ToggleHints ); + //menuStrip->Items->Insert(0, item5 ); + + ToolStripMenuItem ^item2 = gcnew ToolStripMenuItem(); + item2->Text = L"Save Data to File..."; + item2->Click += gcnew System::EventHandler(this, &zdg_ui2::Form1::SaveCurves ); + menuStrip->Items->Insert(0, item2 ); + + ToolStripMenuItem ^item4 = gcnew ToolStripMenuItem(); + item4->Text = L"Save Image As..."; + item4->Click += gcnew System::EventHandler(this, &zdg_ui2::Form1::SaveImage ); + menuStrip->Items->Insert(0, item4 ); + + } + + void form_error_msg( std::string estring ) + { + if (this->chartobject_ptr != NULL) + { + if (Phreeqc* ptr = this->chartobject_ptr->Get_phreeqc()) + { + ptr->error_msg(estring.c_str(), CONTINUE); + } + } + else + { + std::cerr << "ERROR: " << estring << std::endl; + } + } + + void SaveCurves( System::Object ^sender, System::EventArgs ^e ) + { + SaveFileDialog^ saveFileDialog1 = gcnew SaveFileDialog; + //TCHAR dir[MAX_PATH]; + //::GetCurrentDirectory(MAX_PATH, dir); + //String ^d = gcnew String(dir); + //saveFileDialog1->InitialDirectory = d; + saveFileDialog1->FileName = "curves.u_g"; + saveFileDialog1->Filter = "User graph files (*.u_g)|*.u_g|txt files (*.txt)|*.txt|All files (*.*)|*.*"; + saveFileDialog1->FilterIndex = 1; + saveFileDialog1->RestoreDirectory = true; +#undef OK + if ( saveFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK ) + { + std::string file_name; + ToString(saveFileDialog1->FileName, file_name); + std::ofstream f_out(file_name.c_str(), std::ifstream::out); + + if (!f_out.is_open()) + { + std::ostringstream estream; + estream << "Could not open file to save curves for USER_GRAPH " << file_name; + form_error_msg(estream.str()); + return; + } + + // write headings + size_t max_points = 0; + f_out.precision(4); + for (int i = 0; i < zg1->GraphPane->CurveList->Count; i++) + { + LineItem ^curve; + curve = (LineItem ^) zg1->GraphPane->CurveList[i]; + // Get the PointPairList + IPointListEdit ^ip = (IPointListEdit^) curve->Points; + + // Calculate max_points + if ((size_t) ip->Count > max_points) + max_points = ip->Count; + + // write headers + std::string s_std; + ToString(curve->Label->Text, s_std); + f_out.width(12); + f_out << "x" << "\t"; + f_out.width(12); + if (s_std.size() > 0) + { + f_out << s_std << "\t"; + } + else + { + f_out << "y" << "\t"; + } + } + + f_out << std::endl; + + // write data + size_t i2 = 0; + f_out << std::scientific; + f_out.precision(4); + + while (i2 < max_points) + { + for (int i = 0; i < zg1->GraphPane->CurveList->Count; i++) + { + LineItem ^curve; + curve = (LineItem ^) zg1->GraphPane->CurveList[i]; + // Get the PointPairList + IPointListEdit ^ip = (IPointListEdit^) curve->Points; + if (i2 < (size_t) ip->Count) + { + //double x = ip[i]->X; + f_out.width(12); + f_out << ip[(int) i2]->X << "\t"; + f_out.width(12); + f_out << ip[(int) i2]->Y << "\t"; + } + else if (i2 < max_points) + { + f_out.width(13); + f_out << "\t"; + f_out.width(13); + f_out << "\t"; + } + } + f_out << std::endl; + i2++; + } + f_out.close(); + + + } + else + { + // no dialog + std::ostringstream estream; + estream << "Could not open dialog to save curves. "; + form_error_msg(estream.str()); + return; + } + return; + } + + // Respond to a Zoom Event + void MyZoomEvent( ZedGraphControl ^control, ZoomState ^oldState, ZoomState ^newState ) + { + // Here we get notification everytime the user zooms + } + void SetChartOptions( System::Object ^sender, System::EventArgs ^e ) + { + // Create form + Form ^graphOptions = gcnew Form; + graphOptions->Text = "Chart options"; + graphOptions->ShowIcon = false; + graphOptions->BringToFront(); + graphOptions->MinimumSize = System::Drawing::Size(255, 230); + graphOptions->MaximumSize = System::Drawing::Size(255, 230); + graphOptions->Size = System::Drawing::Size(255, 230); + graphOptions->StartPosition = System::Windows::Forms::FormStartPosition::CenterParent; + + // Check box for hints + CheckBox ^cb1 = gcnew CheckBox; + cb1->Appearance = Appearance::Normal; + cb1->ThreeState = false; + cb1->AutoCheck = true; + cb1->Location = System::Drawing::Point(5, 10); + if (this->hints) + { + cb1->CheckState = CheckState::Checked; + } + else + { + cb1->CheckState = CheckState::Unchecked; + } + cb1->Text = "Show hints"; + cb1->Visible = true; + graphOptions->Controls->Add(cb1); + + // Check box for background color + CheckBox ^cb2 = gcnew CheckBox; + cb2->Appearance = Appearance::Normal; + cb2->ThreeState = false; + cb2->AutoCheck = true; + cb2->Location = System::Drawing::Point(5, 60); + + if (this->background) + { + cb2->CheckState = CheckState::Checked; + } + else + { + cb2->CheckState = CheckState::Unchecked; + } + cb2->Text = "Show colored background"; + cb2->AutoSize = true; + cb2->Visible = true; + graphOptions->Controls->Add(cb2); + + // checkbox for grid + CheckBox ^cb3 = gcnew CheckBox; + cb3->Appearance = Appearance::Normal; + cb3->ThreeState = false; + cb3->AutoCheck = true; + cb3->Location = System::Drawing::Point(5, 110); + + if (this->grid) + { + cb3->CheckState = CheckState::Checked; + } + else + { + cb3->CheckState = CheckState::Unchecked; + } + cb3->Text = "Show grid lines"; + cb3->Visible = true; + graphOptions->Controls->Add(cb3); + + // done button for Form + Button^ button1 = gcnew Button; + button1->DialogResult = System::Windows::Forms::DialogResult::OK; + button1->Text = "Done"; + button1->Location = System::Drawing::Point(75, 160); + graphOptions->Controls->Add(button1); + graphOptions->AcceptButton = button1; + + // cancel button for Form + Button^ button2 = gcnew Button; + button2->DialogResult = System::Windows::Forms::DialogResult::Cancel; + button2->Text = "Cancel"; + button2->Location = System::Drawing::Point(155, 160); + graphOptions->Controls->Add(button2); + graphOptions->CancelButton = button2; + + + if (graphOptions->ShowDialog() == System::Windows::Forms::DialogResult::OK) + { + this->hints = (cb1->CheckState == CheckState::Checked); + this->background = (cb2->CheckState == CheckState::Checked); + this->grid = (cb3->CheckState == CheckState::Checked); + } + + if (this->background) + { + zg1->GraphPane->Chart->Fill = gcnew Fill( Color::White, Color::FromArgb(255, 255, 230), 45.0f ); + } + else + { + zg1->GraphPane->Chart->Fill = gcnew Fill( Color::White, Color::White, 45.0f ); + } + if (this->grid) + { + zg1->GraphPane->XAxis->MajorGrid->IsVisible = true; + zg1->GraphPane->YAxis->MajorGrid->IsVisible = true; + } + else + { + zg1->GraphPane->XAxis->MajorGrid->IsVisible = false; + zg1->GraphPane->YAxis->MajorGrid->IsVisible = false; + } + if (this->hints) + { + zg1->GraphPane->GraphObjList = GOL_hints; + } + else + { + zg1->GraphPane->GraphObjList = GOL_no_hints; + } + zg1->Refresh(); + } + void SaveImage( System::Object ^sender, System::EventArgs ^e ) + { + ZedGraph::GraphObjList ^copy = gcnew ZedGraph::GraphObjList(zg1->GraphPane->GraphObjList); + zg1->GraphPane->GraphObjList = GOL_no_hints; + zg1->SaveAs(); + zg1->GraphPane->GraphObjList = copy; + } + void BatchSaveImage( ) + { + ChartObject *chart = this->chartobject_ptr; + assert(chart->Get_batch() > 0); + // Save GraphObjList + ZedGraph::GraphObjList ^GOL_copy = gcnew ZedGraph::GraphObjList(zg1->GraphPane->GraphObjList); + + // Don`t write red hint boxes + zg1->GraphPane->GraphObjList = GOL_no_hints; + + // Set background + if (chart->Get_batch_background()) + { + zg1->GraphPane->Chart->Fill = gcnew Fill( Color::White, Color::FromArgb(255, 255, 230), 45.0f ); + } + else + { + zg1->GraphPane->Chart->Fill = gcnew Fill( Color::White, Color::White, 45.0f ); + } + // Set grid + if (chart->Get_batch_grid()) + { + zg1->GraphPane->XAxis->MajorGrid->IsVisible = true; + zg1->GraphPane->YAxis->MajorGrid->IsVisible = true; + } + else + { + zg1->GraphPane->XAxis->MajorGrid->IsVisible = false; + zg1->GraphPane->YAxis->MajorGrid->IsVisible = false; + } + // Save the graph + if (this->zg1) + { + ImageFormat ^format = ImageFormat::Png; + switch (chart->Get_batch()) + { + case 2: + format = ImageFormat::Png; + break; + case 3: + format = ImageFormat::Gif; + break; + case 4: + format = ImageFormat::Jpeg; + break; + case 5: + format = ImageFormat::Tiff; + break; + case 6: + format = ImageFormat::Bmp; + break; + default: + break; + } + switch (chart->Get_batch()) + { + case 1: // emf + { + System::String ^fn = gcnew System::String(chart->Get_batch_fn().c_str()); + System::Drawing::Imaging::Metafile ^metaFile = this->zg1->MasterPane->GetMetafile(); + metaFile->Save(fn); + } + break; + case 2: // bitmaps + case 3: + case 4: + case 5: + case 6: + { + System::String ^fn = gcnew System::String(chart->Get_batch_fn().c_str()); + System::IO::FileStream ^myStream = gcnew System::IO::FileStream(fn, System::IO::FileMode::Create); + zg1->MasterPane->GetImage()->Save( myStream, format); + myStream->Close(); + } + default: + break; + } + } + + } + private: void timer1_Tick(System::Object ^sender, System::EventArgs ^e ) + { + LineItem ^curve; + ChartObject *chart = this->chartobject_ptr; + if (chart == NULL) return; + + //lock for thread + while (0 != System::Threading::Interlocked::CompareExchange(chart->usingResource, 3, 0)) + { + System::Threading::Thread::Sleep(5); + } + + try + { + if (this->chartobject_ptr->Get_curve_added()) + { + DefineCurves(zg1->GraphPane, zg1->GraphPane->CurveList->Count); + } + else if (this->chartobject_ptr->Get_point_added()) + { + + // Make list of curves + std::vector Curves; + size_t j; + for (j = 0; j < chart->Get_CurvesCSV().size(); j++) + { + Curves.push_back(chart->Get_CurvesCSV()[j]); + } + for (j = 0; j < chart->Get_Curves().size(); j++) + { + Curves.push_back(chart->Get_Curves()[j]); + } + // Add points to curves ... + for (int i = 0; i < zg1->GraphPane->CurveList->Count; i++) + { + curve = (LineItem ^) zg1->GraphPane->CurveList[i]; + // Get the PointPairList + IPointListEdit ^ip = (IPointListEdit^) curve->Points; + if ((size_t) ip->Count < Curves[i]->Get_x().size()) + { + if (Curves[i]->Get_y_axis() == 2) + Y2 = true; + else + Y2 = false; + for ( size_t i2 = ip->Count; i2 < Curves[i]->Get_x().size(); i2++ ) + { + if ((LogX && Curves[i]->Get_x()[i2] <=0) + || (LogY && !Y2 && Curves[i]->Get_y()[i2] <=0) + || (LogY2 && Y2 && Curves[i]->Get_y()[i2] <=0)) + continue; + else + ip->Add(Curves[i]->Get_x()[i2], Curves[i]->Get_y()[i2] ); + } + } + } + // Add points to curves ... + + //size_t i, k; + //k = 0; + //for (i = 0; i < Curves.size(); i++) + //{ + // if (Curves[i]->Get_x().size() == 0) continue; + // curve = (LineItem ^) zg1->GraphPane->CurveList[k++]; + // // Get the PointPairList + // IPointListEdit ^ip = (IPointListEdit^) curve->Points; + // if ((size_t) ip->Count < Curves[i]->Get_x().size()) + // { + // if (Curves[i]->Get_y_axis() == 2) + // Y2 = true; + // else + // Y2 = false; + // for ( size_t i2 = ip->Count; i2 < Curves[i]->Get_x().size(); i2++ ) + // { + // if ((LogX && Curves[i]->Get_x()[i2] <=0) + // || (LogY && !Y2 && Curves[i]->Get_y()[i2] <=0) + // || (LogY2 && Y2 && Curves[i]->Get_y()[i2] <=0)) + // continue; + // else + // ip->Add(Curves[i]->Get_x()[i2], Curves[i]->Get_y()[i2] ); + // } + // } + //} + /* explicitly reset the max in case of log scale, zedgraphs doesn`t do this... */ + if ((fabs(chart->Get_axis_scale_x()[1] - NA) < 1e-3) && zg1->GraphPane->XAxis->Type == AxisType::Log) + { + double max = -1e99; + for (int i = 0; i < zg1->GraphPane->CurveList->Count; i++) + { + if (Curves[i]->Get_x()[Curves[i]->Get_x().size() - 1] > max) + max = Curves[i]->Get_x()[Curves[i]->Get_x().size() - 1]; + } + max += pow(10.0, log10(max / 3)); + zg1->GraphPane->XAxis->Scale->Max = max; + } + if ((fabs(chart->Get_axis_scale_y()[1] - NA) < 1e-3) && zg1->GraphPane->YAxis->Type == AxisType::Log) + { + double max = -1e99; + for (int i = 0; i < zg1->GraphPane->CurveList->Count; i++) + { + curve = (LineItem ^) zg1->GraphPane->CurveList[i]; + if (curve->IsY2Axis) continue; + if (Curves[i]->Get_y()[Curves[i]->Get_y().size() - 1] > max) + max = Curves[i]->Get_y()[Curves[i]->Get_y().size() - 1]; + } + max += pow(10.0, log10(max / 3)); + zg1->GraphPane->YAxis->Scale->Max = max; + } + if ((fabs(chart->Get_axis_scale_y2()[1] - NA) < 1e-3) && zg1->GraphPane->Y2Axis->Type == AxisType::Log) + { + double max = -1e99; + for (int i = 0; i < zg1->GraphPane->CurveList->Count; i++) + { + curve = (LineItem ^) zg1->GraphPane->CurveList[i]; + if (!curve->IsY2Axis) continue; + if (Curves[i]->Get_y()[Curves[i]->Get_y().size() - 1] > max) + max = Curves[i]->Get_y()[Curves[i]->Get_y().size() - 1]; + } + max += pow(10.0, log10(max / 3)); + zg1->GraphPane->Y2Axis->Scale->Max = max; + } + zg1->AxisChange(); + zg1->Refresh(); + } + // + // Following asserts may not be true for Log scales + // negative values are rejected, so chart may have fewer points than phreeqc + // + //for (size_t j = 0; j < chart->Get_CurvesCSV().size(); j++) + //{ + // if (zg1->GraphPane->CurveList[j]->Points->Count != chart->Get_CurvesCSV()[j]->Get_x().size()) + // { + // fprintf(stderr, "graph points = %d\n", zg1->GraphPane->CurveList[j]->Points->Count); + // fprintf(stderr, "phreeqc points = %d\n", chart->Get_CurvesCSV()[j]->Get_x().size()); + // } + // assert(zg1->GraphPane->CurveList[j]->Points->Count == chart->Get_CurvesCSV()[j]->Get_x().size()); + //} + //for (int j = chart->Get_CurvesCSV().size(); j < zg1->GraphPane->CurveList->Count; j++) + //{ + // int k = j - chart->Get_CurvesCSV().size(); + // if (zg1->GraphPane->CurveList[j]->Points->Count != chart->Get_Curves()[k]->Get_x().size()) + // { + // fprintf(stderr, "%d %d graph points = %d\n", j, k, zg1->GraphPane->CurveList[j]->Points->Count); + // fprintf(stderr, "phreeqc points = %d\n", chart->Get_Curves()[k]->Get_x().size()); + // } + // assert(zg1->GraphPane->CurveList[j]->Points->Count == chart->Get_Curves()[k]->Get_x().size()); + //} + chart->Set_point_added(false); + if (chart->Get_end_timer()) + { + timer1->Stop(); + chart->Set_done(true); + phreeqc_done = true; + + int batch = chart->Get_batch(); + if (batch > 0) + { + BatchSaveImage(); + } + + //unlock thread before setting chartobject_ptr to NULL + int n = System::Threading::Interlocked::Exchange(this->chartobject_ptr->usingResource, 0); + assert(n == 3); + + //this->phreeqc_ptr = NULL; + this->chartobject_ptr = NULL; + if (batch >= 0) + { + this->Close(); + } + return; + } + } + catch(...) + { + int n = System::Threading::Interlocked::Exchange(this->chartobject_ptr->usingResource, 0); + assert(n == 3); + + throw; + } + + //unlock thread + int n = System::Threading::Interlocked::Exchange(this->chartobject_ptr->usingResource, 0); + assert(n == 3); + //tickStart = Environment::TickCount; + return; + } + + void ToString(System::String^ src, std::string& dest) + { + using namespace System::Runtime::InteropServices; + const char* chars = (const char*)(Marshal::StringToHGlobalAnsi(src)).ToPointer(); + dest = chars; + Marshal::FreeHGlobal(IntPtr((void*)chars)); + } + ~Form1() { + if (this->zg1) delete zg1; + //if (this->timer1) delete timer1); + if (components) { + delete components; + } + } + public: ZedGraph::ZedGraphControl ^zg1; + private: System::Windows::Forms::Timer ^timer1; + private: System::ComponentModel::Container ^components; + ChartObject * chartobject_ptr; + + public: +#pragma region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + void InitializeComponent() + { + //System::Threading::Thread::Sleep(5000); + this->components = (gcnew System::ComponentModel::Container()); + this->zg1 = (gcnew ZedGraph::ZedGraphControl()); + this->timer1 = (gcnew System::Windows::Forms::Timer( this->components )); + this->SuspendLayout(); + // + // zg1 + // + this->zg1->Location = System::Drawing::Point(12, 12); + this->zg1->Name = L"zg1"; + this->zg1->ScrollGrace = 0; + this->zg1->ScrollMaxX = 0; + this->zg1->ScrollMaxY = 0; + this->zg1->ScrollMaxY2 = 0; + this->zg1->ScrollMinX = 0; + this->zg1->ScrollMinY = 0; + this->zg1->ScrollMinY2 = 0; + this->zg1->Size = System::Drawing::Size(this->chartobject_ptr->Get_PanelWidth() - 2 * 12, chartobject_ptr->Get_PanelHeight() - 2 * 12); + this->zg1->TabIndex = 0; + this->timer1->Tick += gcnew System::EventHandler( this, &Form1::timer1_Tick ); + // + // Form1 + // + this->AutoScaleDimensions = System::Drawing::SizeF(6, 13); + this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; + this->AutoValidate = System::Windows::Forms::AutoValidate::EnablePreventFocusChange; + this->ClientSize = System::Drawing::Size(this->chartobject_ptr->Get_PanelWidth(), chartobject_ptr->Get_PanelHeight()); + this->Controls->Add(this->zg1); + this->Name = L"Form1"; + this->StartPosition = System::Windows::Forms::FormStartPosition::WindowsDefaultLocation;//:CenterScreen; + //this->Text = L"PHREEQC chart"; + System::String ^desc = gcnew String(this->chartobject_ptr->Get_description().c_str()); + this->Text = L"Phreeqc USER_GRAPH " + this->chartobject_ptr->Get_n_user() + " " + desc; + this->TopMost = false; + System::ComponentModel::ComponentResourceManager^ resources = (gcnew System::ComponentModel::ComponentResourceManager(Form1::typeid)); + try + { + //this->Icon = gcnew System::Drawing::Icon("c:\\phreeqc\\phreex.ico"); + this->Icon = (cli::safe_cast(resources->GetObject(L"$this.Icon"))); + } + catch (...) + { + } + + this->FormClosing += gcnew System::Windows::Forms::FormClosingEventHandler(this, &Form1::MyFormClosingEventHandler); + this->Load += gcnew System::EventHandler(this, &Form1::Form1_Load); + this->Resize += gcnew System::EventHandler(this, &Form1::Form1_Resize); + this->ResumeLayout(false); + } +#pragma endregion + }; +} \ No newline at end of file diff --git a/phreeqcpp/Form1.resX b/phreeqcpp/Form1.resX new file mode 100644 index 00000000..8cb449b9 --- /dev/null +++ b/phreeqcpp/Form1.resX @@ -0,0 +1,36 @@ + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + + + AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAP8AMzOaAGgymgA0Mc0AmWVoAGdlmwBjZssAy5kvAP+ZNQDLmmMAzpmVAP/MLQDLzWUA/8xjAP3/ + mAAAAAAAiIiIiIiIiIiIgAAIhIiIhHAAAAB3KXcXcAeQAAeTk5fQDd0ADdoK3b27uwALugm7u7u7AAtz + k9vbuwAAu2u7Xbu7AAva29u5zMzAAMzMzMzu7u4ADu7szOAO7gAO7u7u4ADuAA7u7u7uAAAA7u7u7u7g + AA7u7u7u7u7u7u7u7u4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA + + + \ No newline at end of file diff --git a/phreeqcpp/GasComp.cxx b/phreeqcpp/GasComp.cxx new file mode 100644 index 00000000..21a13c71 --- /dev/null +++ b/phreeqcpp/GasComp.cxx @@ -0,0 +1,208 @@ +// GasComp.cxx: implementation of the cxxGasComp class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // std::cout std::cerr +#include // assert +#include // std::sort +#include + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "GasComp.h" +#include "phqalloc.h" +#include "Dictionary.h" + + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxGasComp::cxxGasComp(PHRQ_io *io) + // + // default constructor for cxxExchComp + // + : PHRQ_base(io) +{ + p_read = 0.0; + moles = 0.0; + initial_moles = 0.0; +} + +cxxGasComp::~cxxGasComp() +{ +} + +void +cxxGasComp::dump_raw(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + s_oss << indent0 << "# GAS_PHASE_MODIFY candidate identifiers #\n"; + s_oss << indent0 << "-moles " << this->moles << "\n"; + + s_oss << indent0 << "# GAS_PHASE_MODIFY candidate identifiers with new_def=true #\n"; + s_oss << indent0 << "-p_read " << this->p_read << "\n"; + + s_oss << indent0 << "# GasComp workspace variables #\n"; + s_oss << indent0 << "-initial_moles " << this->initial_moles << "\n"; +} + +bool +cxxGasComp::read_raw(CParser & parser, bool check) +{ + std::string str; + + int errors = parser.get_input_error(); + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + + bool moles_defined(false); + int opt; + for (;;) + { + opt = parser.get_option(vopts, next_char); + + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_KEYWORD; + // Allow return to Exchange for more processing + break; + + case 0: // phase_name + output_msg("-phase_name is obsolete. Define with -component\n"); + break; + + case 1: // name + output_msg("-name is obsolete. Define with -component\n"); + break; + + case 2: // p_read + if (!(parser.get_iss() >> this->p_read)) + { + this->p_read = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for initial partial pressure.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 3: // moles + if (!(parser.get_iss() >> this->moles)) + { + this->moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for moles.", + PHRQ_io::OT_CONTINUE); + } + moles_defined = true; + break; + + case 4: // initial_moles + if (!(parser.get_iss() >> this->initial_moles)) + { + this->initial_moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for initial_moles.", + PHRQ_io::OT_CONTINUE); + } + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (check) + { + // members that must be defined + if (moles_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Moles not defined for GasComp input.", + PHRQ_io::OT_CONTINUE); + } + } + return (parser.get_input_error() == errors); +} +void +cxxGasComp::add(const cxxGasComp & addee, LDBLE extensive) +{ + //LDBLE ext1, ext2; + if (extensive == 0.0) + return; + if (addee.phase_name.size() == 0) + return; + + /* + ext1 = this->moles; + ext2 = addee.moles * extensive; + if (ext1 + ext2 != 0) + { + f1 = ext1 / (ext1 + ext2); + f2 = ext2 / (ext1 + ext2); + } + else + { + f1 = 0.5; + f2 = 0.5; + } + */ + + assert(this->phase_name == addee.phase_name); + + this->p_read += addee.p_read * extensive; + this->moles += addee.moles * extensive; + this->initial_moles += addee.initial_moles * extensive; +} + +void +cxxGasComp::multiply(LDBLE extensive) +{ + this->p_read *= extensive; + this->moles *= extensive; + this->initial_moles *= extensive; +} +void +cxxGasComp::Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles) +{ + ints.push_back(dictionary.Find(this->phase_name)); + doubles.push_back(this->moles); + doubles.push_back(this->p_read); + doubles.push_back(this->initial_moles); +} + +void +cxxGasComp::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +{ + this->phase_name = dictionary.GetWords()[ints[ii++]]; + this->moles = doubles[dd++]; + this->p_read = doubles[dd++]; + this->initial_moles = doubles[dd++]; +} + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("phase_name"), // 0 + std::vector< std::string >::value_type("name"), // 1 + std::vector< std::string >::value_type("p_read"), // 2 + std::vector< std::string >::value_type("moles"), // 3 + std::vector< std::string >::value_type("initial_moles") // 4 +}; +const std::vector< std::string > cxxGasComp::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/GasComp.h b/phreeqcpp/GasComp.h new file mode 100644 index 00000000..d4661a6b --- /dev/null +++ b/phreeqcpp/GasComp.h @@ -0,0 +1,50 @@ +#if !defined(GASCOMP_H_INCLUDED) +#define GASCOMP_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NameDouble.h" + +class cxxGasComp: public PHRQ_base +{ + + public: + cxxGasComp(PHRQ_io *io=NULL); + + virtual ~cxxGasComp(); + + + void dump_raw(std::ostream & s_oss, unsigned int indent) const; + + bool read_raw(CParser & parser, bool check=true); + + std::string Get_phase_name(void) const {return this->phase_name;} + void Set_phase_name(std::string s) {this->phase_name = s;} + LDBLE Get_p_read() const {return this->p_read;} + void Set_p_read(LDBLE t) {this->p_read = t;} + LDBLE Get_moles() const {return this->moles;} + void Set_moles(LDBLE t) {this->moles = t;} + LDBLE Get_initial_moles() const {return this->initial_moles;} + void Set_initial_moles(LDBLE t) {this->initial_moles = t;} + + void add(const cxxGasComp & addee, LDBLE extensive); + void multiply(LDBLE extensive); + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + + protected: + std::string phase_name; + // GAS_PHASE_MODIFY candidates + LDBLE moles; + // GAS_PHASE_MODIFY candidates with new_def=true + LDBLE p_read; + // internal workspace + LDBLE initial_moles; + const static std::vector < std::string > vopts; +}; + +#endif // !defined(GASCOMP_H_INCLUDED) diff --git a/phreeqcpp/GasPhase.cxx b/phreeqcpp/GasPhase.cxx new file mode 100644 index 00000000..7be3206c --- /dev/null +++ b/phreeqcpp/GasPhase.cxx @@ -0,0 +1,729 @@ +// GasPhase.cxx: implementation of the cxxGasPhase class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort +#include + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "GasPhase.h" +#include "cxxMix.h" +#include "phqalloc.h" + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxGasPhase::cxxGasPhase(PHRQ_io * io) + // + // default constructor for cxxGasPhase + // +: cxxNumKeyword(io) +{ + new_def = false; + solution_equilibria = false; + n_solution = -999; + type = cxxGasPhase::GP_PRESSURE; + total_p = 1.0; + total_moles = 0.0; + volume = 1.0; + v_m = 0; + pr_in = false; + temperature = 298.15; +} +#ifdef SKIP +cxxGasPhase::cxxGasPhase(std::map < int, cxxGasPhase > &entity_map, + cxxMix & mx, int l_n_user, PHRQ_io * io) +: cxxNumKeyword(io) +{ + this->n_user = this->n_user_end = l_n_user; + total_p = 0; + volume = 0; + v_m = 0; + pr_in = false; + bool first = true; +// +// Mix +// + // accumulate in map + std::map comp_map; + std::map::iterator comp_it; + + const std::map < int, LDBLE > & mixcomps = mx.Get_mixComps(); + std::map < int, LDBLE >::const_iterator it; + for (it = mixcomps.begin(); it != mixcomps.end(); it++) + { + const cxxGasPhase *entity_ptr = &(entity_map.find(it->first)->second); + if (first) + { + this->new_def = entity_ptr->new_def; + this->solution_equilibria = entity_ptr->solution_equilibria; + this->n_solution = entity_ptr->n_solution; + this->type = entity_ptr->type; + this->total_p = entity_ptr->total_p * it->second; + this->total_moles = entity_ptr->total_moles * it->second; + this->volume = entity_ptr->volume * it->second; + this->v_m = entity_ptr->v_m * it->second; + this->pr_in = entity_ptr->pr_in; + this->temperature = entity_ptr->temperature; + first = false; + } + else + { + if (this->type != entity_ptr->type) + { + std::ostringstream oss; + oss << "Cannot mix two gas_phases with differing types."; + error_msg(oss.str().c_str(), CONTINUE); + return; + } + + this->total_p += entity_ptr->total_p * it->second; + this->volume += entity_ptr->volume * it->second; + this->v_m += entity_ptr->v_m * it->second; + } + cxxGasPhase *gas_phase_ptr = Utilities::Rxn_find(entity_map, it->first); + if (gas_phase_ptr) + { + std::vector add_comps = gas_phase_ptr->Get_gas_comps(); + for (size_t i = 0; i < add_comps.size(); i++) + { + comp_it = comp_map.find(add_comps[i].Get_phase_name()); + if (comp_it != comp_map.end()) + { + comp_it->second.add(add_comps[i], it->second); + } + else + { + cxxGasComp gc(add_comps[i]); + gc.multiply(it->second); + comp_map[add_comps[i].Get_phase_name()] = gc; + } + } + + } + } + + // put map into vector + this->gas_comps.clear(); + std::vector gc; + for (comp_it = comp_map.begin(); comp_it != comp_map.end(); comp_it++) + { + this->gas_comps.push_back(comp_it->second); + } +} +#endif +cxxGasPhase::cxxGasPhase(std::map < int, cxxGasPhase > &entity_map, + cxxMix & mx, int l_n_user, PHRQ_io * io) +: cxxNumKeyword(io) +{ + this->n_user = this->n_user_end = l_n_user; + total_p = 0; + volume = 0; + v_m = 0; + pr_in = false; + bool first = true; +// +// Mix +// + // accumulate in map + std::map comp_map; + std::map::iterator comp_it; + + const std::map < int, LDBLE > & mixcomps = mx.Get_mixComps(); + std::map < int, LDBLE >::const_iterator it; + this->total_p = 0; + double sum_fractions = 0.0; + for (it = mixcomps.begin(); it != mixcomps.end(); it++) + { + sum_fractions += it->second; + } + for (it = mixcomps.begin(); it != mixcomps.end(); it++) + { + if (entity_map.find(it->first) != entity_map.end()) + { + const cxxGasPhase *entity_ptr = &(entity_map.find(it->first)->second); + if (first) + { + this->new_def = entity_ptr->new_def; + this->solution_equilibria = entity_ptr->solution_equilibria; + this->n_solution = entity_ptr->n_solution; + this->type = entity_ptr->type; + this->total_moles = entity_ptr->total_moles * it->second; + this->volume = entity_ptr->volume * it->second; + if (sum_fractions > 0.0) + { + this->v_m = entity_ptr->v_m * it->second / sum_fractions; + this->total_p += entity_ptr->total_p * it->second / sum_fractions; + } + else + { + this->v_m = 0.0; + this->total_p = 0.0; + } + this->pr_in = entity_ptr->pr_in; + this->temperature = entity_ptr->temperature; + first = false; + } + else + { + if (this->type != entity_ptr->type) + { + std::ostringstream oss; + oss << "Cannot mix two gas_phases with differing types."; + error_msg(oss.str().c_str(), CONTINUE); + return; + } + + this->total_p += entity_ptr->total_p * it->second; + this->volume += entity_ptr->volume * it->second; + this->v_m += entity_ptr->v_m * it->second; + } + } + cxxGasPhase *gas_phase_ptr = Utilities::Rxn_find(entity_map, it->first); + if (gas_phase_ptr) + { + std::vector add_comps = gas_phase_ptr->Get_gas_comps(); + for (size_t i = 0; i < add_comps.size(); i++) + { + comp_it = comp_map.find(add_comps[i].Get_phase_name()); + if (comp_it != comp_map.end()) + { + comp_it->second.add(add_comps[i], it->second); + } + else + { + cxxGasComp gc(add_comps[i]); + gc.multiply(it->second); + comp_map[add_comps[i].Get_phase_name()] = gc; + } + } + + } + } + + // put map into vector + this->gas_comps.clear(); + std::vector gc; + for (comp_it = comp_map.begin(); comp_it != comp_map.end(); comp_it++) + { + this->gas_comps.push_back(comp_it->second); + } +} +#ifdef SKIP +cxxGasPhase::cxxGasPhase(const std::map < int, cxxGasPhase > &entities, + cxxMix & mix, int l_n_user, PHRQ_io * io): +cxxNumKeyword(io) +{ + this->n_user = this->n_user_end = l_n_user; + gasPhaseComps.type = cxxNameDouble::ND_NAME_COEF; + total_p = 0; + volume = 0; + v_m = 0; + pr_in = false; + bool first = true; +// +// Mix +// + //cxxNameDouble gasPhaseComps; + const std::map < int, LDBLE > & mixcomps = mix.Get_mixComps(); + std::map < int, LDBLE >::const_iterator it; + for (it = mixcomps.begin(); it != mixcomps.end(); it++) + { + if (entities.find(it->first) != entities.end()) + { + const cxxGasPhase *entity_ptr = + &(entities.find(it->first)->second); + this->gasPhaseComps.add_extensive(entity_ptr->gasPhaseComps, + it->second); + //GP_TYPE type; + //LDBLE total_p; + //LDBLE volume; + if (first) + { + this->type = entity_ptr->type; + this->total_p = entity_ptr->total_p * it->second; + this->volume = entity_ptr->volume * it->second; + this->v_m = entity_ptr->v_m * it->second; + this->pr_in = entity_ptr->pr_in; + first = false; + } + else + { + if (this->type != entity_ptr->type) + { + std::ostringstream oss; + oss << "Cannot mix two gas_phases with differing types."; + error_msg(oss.str().c_str(), CONTINUE); + //input_error++; + return; + } + + this->total_p += entity_ptr->total_p * it->second; + this->volume += entity_ptr->volume * it->second; + this->v_m += entity_ptr->v_m * it->second; + } + } + } +} +#endif +cxxGasPhase::~cxxGasPhase() +{ +} + +#ifdef SKIP +void +cxxGasPhase::dump_xml(std::ostream & s_oss, unsigned int indent) const const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // GasPhase element and attributes + s_oss << indent0; + s_oss << " + pitzer_gas_phase_gammas << "\"" << "\n"; + + // components + s_oss << indent1; + s_oss << "::const_iterator it = + gas_phaseComps.begin(); it != gas_phaseComps.end(); ++it) + { + it->dump_xml(s_oss, indent + 2); + } + + return; +} +#endif + +void +cxxGasPhase::dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // GasPhase element and attributes + s_oss << indent0; + int n_user_local = (n_out != NULL) ? *n_out : this->n_user; + s_oss << "GAS_PHASE_RAW " << n_user_local << " " << this->description << "\n"; + + s_oss << indent1 << "# GAS_PHASE_MODIFY candidate identifiers #\n"; + s_oss << indent1; + s_oss << "-type " << this->type << "\n"; + s_oss << indent1; + s_oss << "-total_p " << this->total_p << "\n"; + s_oss << indent1; + s_oss << "-volume " << this->volume << "\n"; + // gasPhaseComps + for (size_t i = 0 ; i < this->gas_comps.size(); i++) + { + s_oss << indent1; + s_oss << "-component " << this->gas_comps[i].Get_phase_name() << "\n"; + this->gas_comps[i].dump_raw(s_oss, indent + 2); + } + + s_oss << indent1 << "# GAS_PHASE_MODIFY candidate identifiers with new_def=true #\n"; + s_oss << indent1; + s_oss << "-new_def " << this->new_def << "\n"; + s_oss << indent1; + s_oss << "-solution_equilibria " << this->solution_equilibria << "\n"; + s_oss << indent1; + s_oss << "-n_solution " << this->n_solution << "\n"; + s_oss << indent1; + s_oss << "-temperature " << this->temperature << "\n"; + + s_oss << indent1 << "# GasPhase workspace variables #\n"; + s_oss << indent1; + s_oss << "-total_moles " << this->total_moles << "\n"; + s_oss << indent1; + s_oss << "-v_m " << this->v_m << "\n"; + s_oss << indent1; + s_oss << "-pr_in " << (this->pr_in ? 1 : 0) << "\n"; + s_oss << indent1; + s_oss << "-totals " << "\n"; + this->totals.dump_raw(s_oss, indent + 2); +} + +void +cxxGasPhase::read_raw(CParser & parser, bool check) +{ + + int i; + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + bool useLastLine(false); + + // Read gas_phase number and description + this->read_number_description(parser); + this->Set_new_def(false); + + opt_save = CParser::OPT_ERROR; + bool type_defined(false); + bool total_p_defined(false); + bool volume_defined(false); + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + CParser::ECHO_OPTION eo = parser.get_echo_file(); + parser.set_echo_file(CParser::EO_NONE); + opt = parser.getOptionFromLastLine(vopts, next_char, true); + parser.set_echo_file(eo); + } + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input in GAS_PHASE_COMP_RAW keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + useLastLine = false; + break; + + case 0: // type + if (!(parser.get_iss() >> i)) + { + this->type = cxxGasPhase::GP_PRESSURE; + parser.incr_input_error(); + parser.error_msg("Expected enum for type.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->type = (cxxGasPhase::GP_TYPE) i; + } + type_defined = true; + useLastLine = false; + break; + + case 1: // total_p + case 5: // pressure + if (!(parser.get_iss() >> this->total_p)) + { + this->total_p = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for total_p.", + PHRQ_io::OT_CONTINUE); + } + total_p_defined = true; + useLastLine = false; + break; + + case 2: // volume + if (!(parser.get_iss() >> this->volume)) + { + this->volume = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for volume.", + PHRQ_io::OT_CONTINUE); + } + volume_defined = true; + useLastLine = false; + break; + + case 3: // v_m + if (!(parser.get_iss() >> this->v_m)) + { + this->v_m = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for v_m.", + PHRQ_io::OT_CONTINUE); + } + useLastLine = false; + break; + case 4: // component + { + std::string str; + if (!(parser.get_iss() >> str)) + { + parser.incr_input_error(); + parser.error_msg("Expected string value for component name.", + PHRQ_io::OT_CONTINUE); + } + else + { + cxxGasComp temp_comp(io); + temp_comp.Set_phase_name(str); + cxxGasComp * comp_ptr = this->Find_comp(str.c_str()); + if (comp_ptr) + { + temp_comp = *comp_ptr; + } + temp_comp.read_raw(parser, false); + if (comp_ptr) + { + for (size_t j = 0; j < this->gas_comps.size(); j++) + { + if (Utilities::strcmp_nocase(this->gas_comps[j].Get_phase_name().c_str(), str.c_str()) == 0) + { + this->gas_comps[j] = temp_comp; + } + } + } + else + { + this->gas_comps.push_back(temp_comp); + } + useLastLine = true; + } + } + + opt_save = CParser::OPT_DEFAULT; + break; + case 6: // pr_in + if (!(parser.get_iss() >> i)) + { + parser.incr_input_error(); + parser.error_msg("Expected 0/1 for pr_in.", PHRQ_io::OT_CONTINUE); + } + else + { + this->pr_in = (i == 0) ? false : true; + } + useLastLine = false; + break; + case 7: // new_def + if (!(parser.get_iss() >> i)) + { + parser.incr_input_error(); + parser.error_msg("Expected 0/1 for new_def.", PHRQ_io::OT_CONTINUE); + } + else + { + this->new_def = (i == 0) ? false : true; + } + useLastLine = false; + break; + case 8: // solution_equilibria + if (!(parser.get_iss() >> i)) + { + parser.incr_input_error(); + parser.error_msg("Expected 0/1 for solution_equilibria.", PHRQ_io::OT_CONTINUE); + } + else + { + this->solution_equilibria = (i == 0) ? false : true; + } + useLastLine = false; + break; + case 9: // n_solution + if (!(parser.get_iss() >> this->n_solution)) + { + parser.incr_input_error(); + parser.error_msg("Expected integer for n_solution.", PHRQ_io::OT_CONTINUE); + } + useLastLine = false; + break; + case 10: // total_moles + if (!(parser.get_iss() >> this->total_moles)) + { + this->total_moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for total_moles.", + PHRQ_io::OT_CONTINUE); + } + useLastLine = false; + break; + case 11: // temperature + if (!(parser.get_iss() >> this->temperature)) + { + this->temperature = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for temperature.", + PHRQ_io::OT_CONTINUE); + } + useLastLine = false; + break; + case 12: // totals + if (this->totals.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected element name and molality for GasPhase totals.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 12; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (check) + { + // members that must be defined + if (type_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Type not defined for GAS_PHASE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (total_p_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Total_p not defined for GAS_PHASE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (volume_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Volume not defined for GAS_PHASE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + } +} + +void +cxxGasPhase::totalize(Phreeqc * phreeqc_ptr) +{ + this->totals.clear(); + // component structures + for (size_t i = 0; i < this->gas_comps.size(); i++) + { + struct phase *phase_ptr; + int l; + phase_ptr = phreeqc_ptr-> phase_bsearch(this->gas_comps[i].Get_phase_name().c_str(), &l, FALSE); + if (phase_ptr != NULL) + { + cxxNameDouble phase_formula(phase_ptr->next_elt); + this->totals.add_extensive(phase_formula, this->gas_comps[i].Get_moles()); + } + else + { + assert(false); + } + } + return; +} +LDBLE cxxGasPhase::Calc_total_moles(void)const +{ + LDBLE tot = 0.0; + for (size_t i = 0; i < this->gas_comps.size(); i++) + { + tot += gas_comps[i].Get_moles(); + } + return tot; +} +cxxGasComp * +cxxGasPhase::Find_comp(const char * comp_name) +{ + for (size_t i = 0; i < this->gas_comps.size(); i++) + { + if (Utilities::strcmp_nocase(this->gas_comps[i].Get_phase_name().c_str(), comp_name) == 0) + { + return &(this->gas_comps[i]); + } + } + return NULL; +} +void +cxxGasPhase::Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles) +{ + ints.push_back(this->n_user); + ints.push_back((this->type == cxxGasPhase::GP_PRESSURE) ? 0 : 1); + doubles.push_back(this->total_p); + doubles.push_back(this->volume); + ints.push_back((int) this->gas_comps.size()); + for (size_t i = 0; i < this->gas_comps.size(); i++) + { + this->gas_comps[i].Serialize(dictionary, ints, doubles); + } + ints.push_back(this->new_def ? 1 : 0); + ints.push_back(this->solution_equilibria ? 1 : 0); + ints.push_back(this->n_solution); + doubles.push_back(this->temperature); + doubles.push_back(this->total_moles); + doubles.push_back(this->v_m); + ints.push_back(this->pr_in ? 1 : 0); + this->totals.Serialize(dictionary, ints, doubles); + +} + +void +cxxGasPhase::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +{ + this->n_user = ints[ii++]; + this->n_user_end = this->n_user; + this->description = " "; + + this->type = (ints[ii++] == 0) ? cxxGasPhase::GP_PRESSURE : cxxGasPhase::GP_VOLUME; + this->total_p = doubles[dd++]; + this->volume = doubles[dd++]; + int count = ints[ii++]; + this->gas_comps.clear(); + for (int i = 0; i < count; i++) + { + cxxGasComp gc; + gc.Deserialize(dictionary, ints, doubles, ii, dd); + this->gas_comps.push_back(gc); + } + this->new_def = (ints[ii++] != 0) ? 1 : 0; + this->solution_equilibria = (ints[ii++] != 0) ? 1 : 0; + this->n_solution = ints[ii++]; + this->temperature = doubles[dd++]; + this->total_moles = doubles[dd++]; + this->v_m = doubles[dd++]; + this->pr_in = (ints[ii++] != 0); + this->totals.Deserialize(dictionary, ints, doubles, ii, dd); + +} + + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("type"), //0 + std::vector< std::string >::value_type("total_p"), //1 + std::vector< std::string >::value_type("volume"), //2 + std::vector< std::string >::value_type("v_m"), //3 + std::vector< std::string >::value_type("component"), //4 + std::vector< std::string >::value_type("pressure"), //5 + std::vector< std::string >::value_type("pr_in"), //6 + std::vector< std::string >::value_type("new_def"), //7 + std::vector< std::string >::value_type("solution_equilibria"), //8 + std::vector< std::string >::value_type("n_solution"), //9 + std::vector< std::string >::value_type("total_moles"), //10 + std::vector< std::string >::value_type("temperature"), //11 + std::vector< std::string >::value_type("totals") //12 +}; +const std::vector< std::string > cxxGasPhase::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/GasPhase.h b/phreeqcpp/GasPhase.h new file mode 100644 index 00000000..7dde49d1 --- /dev/null +++ b/phreeqcpp/GasPhase.h @@ -0,0 +1,97 @@ +#if !defined(GASPHASE_H_INCLUDED) +#define GASPHASE_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector +#include "phrqtype.h" +#include "NumKeyword.h" +#include "NameDouble.h" +#include "GasComp.h" +class cxxMix; + +class cxxGasPhase:public cxxNumKeyword +{ + + public: + cxxGasPhase(PHRQ_io * io=NULL); + cxxGasPhase(std::map < int, cxxGasPhase > &entity_map, + cxxMix & mx, int n_user, PHRQ_io * io=NULL); + ~cxxGasPhase(); + + enum GP_TYPE + { + GP_PRESSURE = 0, + GP_VOLUME = 1 + }; + + //void dump_xml(std::ostream& os, unsigned int indent = 0)const; + + void dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out=NULL) const; + + void read_raw(CParser & parser, bool check = true); + + void totalize(Phreeqc * phreeqc_ptr); + + const cxxNameDouble & Get_totals() const + { + return this->totals; + }; + std::vector & Get_gas_comps(void) {return gas_comps;}; + const std::vector & Get_gas_comps(void)const {return gas_comps;}; + void Set_gas_comps(const std::vector v) {gas_comps = v;}; + + GP_TYPE Get_type(void) const {return type;}; + void Set_type(GP_TYPE t) {type = t;}; + LDBLE Get_total_p(void) const {return total_p;}; + void Set_total_p(LDBLE t) {total_p = t;}; + LDBLE Get_volume(void) const {return volume;}; + void Set_volume(LDBLE v) {volume = v;}; + LDBLE Get_v_m(void) const {return v_m;}; + void Set_v_m(LDBLE v) {v_m = v;}; + bool Get_pr_in(void) const {return pr_in;}; + void Set_pr_in(bool tf) {pr_in = tf;}; + cxxNameDouble & Get_totals(void) {return totals;}; + + bool Get_new_def(void) const {return this->new_def;}; + void Set_new_def(bool tf) {this->new_def = tf;}; + bool Get_solution_equilibria(void) const {return this->solution_equilibria;}; + void Set_solution_equilibria(bool tf) {this->solution_equilibria = tf;}; + int Get_n_solution(void) const {return this->n_solution;}; + void Set_n_solution(int i) {this->n_solution = i;}; + LDBLE Get_total_moles(void)const {return total_moles;}; + void Set_total_moles(LDBLE t) {total_moles = t;}; + LDBLE Get_temperature(void)const {return temperature;}; + void Set_temperature(LDBLE t) {temperature = t;}; + LDBLE Calc_total_moles(void)const; + cxxGasComp *Find_comp(const char * comp_name); + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + void add(const cxxGasPhase & addee, LDBLE extensive); + +protected: + // candidate variables for GAS_PHASE_MODIFY + GP_TYPE type; + LDBLE total_p; + LDBLE volume; + std::vector gas_comps; + + // GAS_PHASE_MODIFY with new_def=true variables + bool new_def; + bool solution_equilibria; + int n_solution; + LDBLE temperature; + + // internal variables + LDBLE total_moles; + LDBLE v_m; + bool pr_in; + cxxNameDouble totals; + const static std::vector < std::string > vopts; +}; + +#endif // !defined(GASPHASE_H_INCLUDED) diff --git a/phreeqcpp/ISolution.cxx b/phreeqcpp/ISolution.cxx new file mode 100644 index 00000000..29afa583 --- /dev/null +++ b/phreeqcpp/ISolution.cxx @@ -0,0 +1,209 @@ +// ISolution.cxx: implementation of the cxxSolutionxx class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort +#include + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "ISolution.h" +#include "phqalloc.h" + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxISolution::cxxISolution(PHRQ_io *io) +: +units("mMol/kgw") +{ + default_pe = "pe"; + cxxChemRxn temp_pe_reactions; + pe_reactions[default_pe] = temp_pe_reactions; + this->calc_density = false; + +} +cxxISolution::~cxxISolution() +{ +} + +#ifdef SKIP_OR_MOVE_TO_STRUCTURES +void +cxxISolution::ConvertUnits(Phreeqc * phreeqc_ptr) + // + // Converts from input units to moles per kilogram water + // +{ + LDBLE sum_solutes = 0; + // foreach conc + std::map < std::string, cxxISolutionComp >::iterator iter = + this->comps.begin(); + for (; iter != this->comps.end(); ++iter) + { + struct master *master_ptr = phreeqc_ptr-> master_bsearch(iter->first.c_str()); + if (master_ptr != NULL && (master_ptr->minor_isotope == TRUE)) + continue; + //if (iter->second.Get_description() == "H(1)" || iter->second.Get_description() == "E") continue; + if (strcmp(iter->second.Get_description().c_str(), "H(1)") == 0 + || strcmp(iter->second.Get_description().c_str(), "E")) + continue; + if (iter->second.get_input_conc() <= 0.0) + continue; +/* +* Convert liters to kg solution +*/ + LDBLE moles = iter->second.get_input_conc(); + if (this->units.find("/l") != std::string::npos) + { + moles /= this->density; + } +/* +* Convert to moles +*/ + //set gfw for element + iter->second.set_gfw(phreeqc_ptr); + // convert to moles + if (iter->second.get_units().find("g/") != std::string::npos) + { + if (iter->second.get_gfw() != 0) + { + moles /= iter->second.get_gfw(); + } + else + { + std::ostringstream oss; + oss << "Could not find gfw, " << iter->second. + Get_description(); + error_msg(oss.str().c_str(), CONTINUE); + } + } +/* +* Convert milli or micro +*/ + char c = iter->second.get_units().c_str()[0]; + if (c == 'm') + { + moles *= 1e-3; + } + else if (c == 'u') + { + moles *= 1e-6; + } + iter->second.set_moles(moles); +/* +* Sum grams of solute, convert from moles necessary +*/ + sum_solutes += moles * (iter->second.get_gfw()); + } +/* + * Convert /kgs to /kgw + */ + LDBLE l_mass_water; + if ((this->units.find("kgs") != std::string::npos) || + (this->units.find("/l") != std::string::npos)) + { + l_mass_water = 1.0 - 1e-3 * sum_solutes; + for (; iter != this->comps.end(); ++iter) + { + iter->second.set_moles(iter->second.get_moles() / l_mass_water); + } + } +/* + * Scale by mass of water in solution + */ + l_mass_water = this->mass_water; + for (; iter != this->comps.end(); ++iter) + { + iter->second.set_moles(iter->second.get_moles() * l_mass_water); + } +} +#endif + +#ifdef SKIP +void +cxxISolution::dump_xml(std::ostream & os, unsigned int indent) const const +{ + unsigned int i; + + for (i = 0; i < indent; ++i) + os << Utilities::INDENT; + os << "\n"; + + cxxNumKeyword::dump_xml(os, indent); + + for (i = 0; i < indent + 1; ++i) + os << Utilities::INDENT; + os << "" << this->get_tc() << "" << "\n"; + + for (i = 0; i < indent + 1; ++i) + os << Utilities::INDENT; + os << "" << this->get_ph() << "" << "\n"; + + for (i = 0; i < indent + 1; ++i) + os << Utilities::INDENT; + os << "" << this->get_solution_pe() << "" << "\n"; + + assert(this->pe.size() > 0); + assert(this->default_pe >= 0); + assert(this->pe.size() > (unsigned int) this->default_pe); + + for (i = 0; i < indent + 1; ++i) + os << Utilities::INDENT; + os << "" << this->get_units() << "" << "\n"; + + for (i = 0; i < indent + 1; ++i) + os << Utilities::INDENT; + os << "" << this->get_density() << "" << "\n"; + + // foreach conc + if (!this->totals.empty()) + { + for (i = 0; i < indent + 1; ++i) + os << Utilities::INDENT; + os << "\n"; + + std::vector < cxxISolutionComp >::const_iterator iter = + this->totals.begin(); + for (; iter != this->totals.end(); ++iter) + { + (*iter).dump_xml(*this, os, indent + 2); + } + + for (i = 0; i < indent + 1; ++i) + os << Utilities::INDENT; + os << "\n"; + } + + // foreach isotope + if (!this->isotopes.empty()) + { + for (i = 0; i < indent + 1; ++i) + os << Utilities::INDENT; + os << "\n"; + + std::list < cxxIsotope >::const_iterator iter = + this->isotopes.begin(); + for (; iter != this->isotopes.end(); ++iter) + { + (*iter).dump_xml(os, indent + 2); + } + + for (i = 0; i < indent + 1; ++i) + os << Utilities::INDENT; + os << "\n"; + } + + for (i = 0; i < indent + 1; ++i) + os << Utilities::INDENT; + os << "" << this->get_mass_water() << "" << "\n"; + + for (i = 0; i < indent; ++i) + os << Utilities::INDENT; + os << "" << "\n"; +} +#endif diff --git a/phreeqcpp/ISolution.h b/phreeqcpp/ISolution.h new file mode 100644 index 00000000..1c7de94e --- /dev/null +++ b/phreeqcpp/ISolution.h @@ -0,0 +1,55 @@ +#if !defined(ISOLUTION_H_INCLUDED) +#define ISOLUTION_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector +#include // std::set +#include +#include +#include + +#include "ISolutionComp.h" +#include "PHRQ_base.h" +#include "NameDouble.h" +#include "global_structures.h" +class cxxISolution: public PHRQ_base +{ + + public: + cxxISolution(PHRQ_io *io=NULL); + cxxISolution(const cxxISolution *is); + virtual ~cxxISolution(); + std::string Get_units() const {return units;} + void Set_units(std::string l_units) {units = l_units;} + void Set_units(const char * l_units) + { + if (l_units != NULL) + this->units = std::string(l_units); + else + this->units.clear(); + } + const char * Get_default_pe() const {return default_pe;} + void Set_default_pe(const char * pe) {default_pe = pe;} + bool Get_calc_density(void) {return this->calc_density;} + void Set_calc_density(bool calc) {this->calc_density = calc;} + std::map < std::string, cxxISolutionComp > &Get_comps(void) {return this->comps;} + const std::map < std::string, cxxISolutionComp > &Get_comps(void)const {return this->comps;} + void Set_comps(std::map < std::string, cxxISolutionComp > &c) {this->comps = c;} + std::map < std::string, cxxChemRxn > &Get_pe_reactions(void) {return this->pe_reactions;} + void Set_pe_reactions(std::map < std::string, cxxChemRxn > &pe) {this->pe_reactions = pe;} + //void dump_xml(std::ostream& os, unsigned int indent = 0)const; + //void ConvertUnits(Phreeqc * phreeqc_ptr); + + protected: + friend class cxxISolutionComp; // for this->pe access + std::string units; + bool calc_density; + std::map < std::string, cxxISolutionComp > comps; + std::map pe_reactions; + const char * default_pe; +}; + +#endif // !defined(ISOLUTION_H_INCLUDED) diff --git a/phreeqcpp/ISolutionComp.cxx b/phreeqcpp/ISolutionComp.cxx new file mode 100644 index 00000000..7e0ca016 --- /dev/null +++ b/phreeqcpp/ISolutionComp.cxx @@ -0,0 +1,447 @@ +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include + +#include "Utils.h" +#include "ISolutionComp.h" +#include "Parser.h" +#include "Solution.h" +#include "phqalloc.h" + +cxxISolutionComp::cxxISolutionComp(PHRQ_io *io): +PHRQ_base(io), +moles(0.0), +input_conc(0.0), +phase_si(0.0), +gfw(0.0) +{ +} +cxxISolutionComp::~cxxISolutionComp(void) +{ +} + +#ifdef SKIP_OR_MOVE_TO_STRUCTURES +struct conc * +cxxISolutionComp::cxxISolutionComp2conc(Phreeqc * phreeqc_ptr, const std::map < std::string, + cxxISolutionComp > &totals) + // for ISolutions + // takes a std::vector cxxISolutionComp structures + // returns list of conc structures +{ + struct conc *c; + c = (struct conc *) + phreeqc_ptr-> PHRQ_malloc((size_t) ((totals.size() + 1) * sizeof(struct conc))); + if (c == NULL) + phreeqc_ptr-> malloc_error(); + int i = 0; + for (std::map < std::string, cxxISolutionComp >::const_iterator it = totals.begin(); + it != totals.end(); ++it) + { + c[i].description = phreeqc_ptr-> string_duplicate(it->second.description.c_str()); + c[i].moles = it->second.moles; + c[i].input_conc = it->second.input_conc; + if (it->second.units.size() == 0) + c[i].units = NULL; + else + c[i].units = phreeqc_ptr-> string_hsave(it->second.units.c_str()); + if (it->second.equation_name.size() == 0) + c[i].equation_name = NULL; + else + c[i].equation_name = phreeqc_ptr-> string_hsave(it->second.equation_name.c_str()); + c[i].phase_si = it->second.phase_si; + c[i].n_pe = it->second.n_pe; + c[i].as = phreeqc_ptr-> string_hsave(it->second.as.c_str()); + c[i].gfw = it->second.gfw; + //c[i].skip = 0; + c[i].phase = NULL; + i++; + } + c[i].description = NULL; + return (c); +} +#endif + +#ifdef SKIP_OR_MOVE_TO_STRUCTURES +void +cxxISolutionComp::set_gfw(Phreeqc * phreeqc_ptr) +{ +// return gfw + if (this->gfw > 0.0) + return; +// calculate gfw from as or from master species gfw + if (this->as.size() != 0) + { + /* use given chemical formula to calculate gfw */ + LDBLE l_gfw; + if (phreeqc_ptr-> compute_gfw(this->as.c_str(), &l_gfw) == ERROR) + { + std::ostringstream oss; + oss << "Could not compute gfw, " << this->as; + error_msg(oss.str().c_str(), CONTINUE); + return; + } + //if (this->description == "Alkalinity" && this->as == "CaCO3") + if (strcmp(this->description.c_str(), "Alkalinity") == 0 + && strcmp(this->as.c_str(), "CaCO3")) + { + l_gfw /= 2.; + } + this->gfw = l_gfw; + return; + } + /* use gfw of master species */ + std::string str(this->description); + struct master *master_ptr = phreeqc_ptr-> master_bsearch(str.c_str()); + if (master_ptr != NULL) + { + /* use gfw for element redox state */ + this->gfw = master_ptr->gfw; + return; + } + std::ostringstream oss; + oss << "Could not find gfw, " << this->description; + error_msg(oss.str().c_str(), CONTINUE); + return; +} +#endif +#ifdef SKIP +cxxISolutionComp::STATUS_TYPE cxxISolutionComp::read(CParser & parser, + cxxISolution & solution) +{ + // std::string& str = parser.line(); + std::string str = parser.line(); + + // defaults set in ctor + + // Remove space between "kg" and "solution" or "water" in units + Utilities::replace("Kg", "kg", str); + Utilities::replace("KG", "kg", str); + while (Utilities::replace("kg ", "kg", str)); + + std::istream::pos_type ptr = 0; + + // + // Read master species list for mass balance equation + // + std::string token; + std::string token1; + int + count_redox_states = 0; + CParser::TOKEN_TYPE j; + while (((j = parser.copy_token(token, ptr)) == CParser::TT_UPPER) || + (token[0] == '[') || + (Utilities::strcmp_nocase_arg1(token.c_str(), "ph") == 0) || + (Utilities::strcmp_nocase_arg1(token.c_str(), "pe") == 0)) + { + ++count_redox_states; + Utilities::replace("(+", "(", token); + if (count_redox_states > 1) + token1 += " "; + token1 += token; + } + if (count_redox_states == 0) + { + parser.incr_input_error(); + parser. + error_msg + ("No element or master species given for concentration input.", + PHRQ_io::OT_CONTINUE); + return cxxISolutionComp::ERROR; + } + description = token1; + + // Determine if reading alkalinity, allow equivalents for units + Utilities::str_tolower(token1); + bool + alk = false; + if (token1.find("alk") == 0) + { + alk = true; + } + + // Read concentration + if (!(std::istringstream(token) >> this->input_conc)) + { + std::ostringstream err; + err << "Concentration data error for " << token1 << + " in solution input."; + parser.error_msg(err, PHRQ_io::OT_CONTINUE); + return cxxISolutionComp::ERROR; + } + if ((j = parser.copy_token(token, ptr)) == CParser::TT_EMPTY) + return cxxISolutionComp::OK; + + // Read optional data + token1 = token; + + // Check for units info + if (parser.check_units(token1, alk, false, solution.get_units(), false) == + CParser::OK) + { + if (parser. + check_units(token1, alk, false, solution.get_units(), + true) == CParser::OK) + { + this->units = token1; + if ((j = parser.copy_token(token, ptr)) == CParser::TT_EMPTY) + return cxxISolutionComp::OK; + } + else + { + return cxxISolutionComp::ERROR; + } + } + + // Check for "as" followed by formula to be used for gfw + token1 = token; + Utilities::str_tolower(token1); + if (token1.compare("as") == 0) + { + parser.copy_token(token, ptr); + this->as = token; + if ((j = parser.copy_token(token, ptr)) == CParser::TT_EMPTY) + return cxxISolutionComp::OK; + } + // Check for "gfw" followed by gram formula weight + else if (token1.compare("gfw") == 0) + { + if (parser.copy_token(token, ptr) != CParser::TT_DIGIT) + { + parser.error_msg("Expecting gram formula weight.", + PHRQ_io::OT_CONTINUE); + return cxxISolutionComp::ERROR; + } + else + { + parser.get_iss() >> this->gfw; + if ((j = parser.copy_token(token, ptr)) == CParser::TT_EMPTY) + return cxxISolutionComp::OK; + } + } + + // Check for redox couple for pe + if (Utilities::strcmp_nocase_arg1(token.c_str(), "pe") == 0) + { + this->n_pe = cxxPe_Data::store(solution.pe, token); + if ((j = parser.copy_token(token, ptr)) == CParser::TT_EMPTY) + return cxxISolutionComp::OK; + } + else if (token.find("/") != std::string::npos) + { + if (parser.parse_couple(token) == CParser::OK) + { + this->n_pe = cxxPe_Data::store(solution.pe, token); + if ((j = parser.copy_token(token, ptr)) == CParser::TT_EMPTY) + return cxxISolutionComp::OK; + } + else + { + return cxxISolutionComp::ERROR; + } + } + + // Must have phase + this->equation_name = token; + if ((j = parser.copy_token(token, ptr)) == CParser::TT_EMPTY) + return cxxISolutionComp::OK; + + // Check for saturation index + if (!(std::istringstream(token) >> this->phase_si)) + { + parser.error_msg("Expected saturation index.", PHRQ_io::OT_CONTINUE); + return cxxISolutionComp::ERROR; + } + return cxxISolutionComp::OK; +} +#endif +#ifdef SKIP +void +cxxISolutionComp::dump_xml(std::ostream & s_oss, unsigned int indent) const const +{ + unsigned int i; + std::string indent0(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + + s_oss << indent0; + s_oss << "description << "\""; + + s_oss << " conc_moles=\"" << this->moles << "\""; + + s_oss << "\">" << "\n"; +} +#endif +/* ---------------------------------------------------------------------- */ +CParser::STATUS_TYPE cxxISolutionComp:: +read(const char *line_in, cxxSolution *solution_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Remove space between "kg" and "solution" or "water" in units + */ + std::string line = line_in; + Utilities::replace("Kg", "kg", line); + Utilities::replace("KG", "kg", line); + while (Utilities::replace("kg ", "kg", line) == TRUE); +/* + * Read master species list for mass balance equation + */ + std::string master_list; + std::string token; + std::string::iterator b = line.begin(); + std::string::iterator e = line.end(); + { + int j; + while (((j = CParser::copy_token(token, b, e)) == CParser::TT_UPPER) || + (token[0] == '[') || + (Utilities::strcmp_nocase(token.c_str(), "ph") == 0) || + (Utilities::strcmp_nocase(token.c_str(), "pe") == 0)) + { + Utilities::replace("(+", "(", token); + if (master_list.size() > 0) + master_list.append(" "); + master_list.append(token); + } + } + if (master_list.size() == 0) + { + error_msg + ("No element or master species given for concentration input.", + PHRQ_io::OT_CONTINUE); + return (CParser::PARSER_ERROR); + } + this->Set_description(master_list.c_str()); +/* + * Determine if reading alkalinity, allow equivalents for units + */ + bool alk; + Utilities::str_tolower(master_list); + if (strstr(master_list.c_str(), "alk") == master_list.c_str()) + { + alk = true; + } + else + { + alk = false; + } +/* + * Read concentration + */ + { + LDBLE dummy; + int j = sscanf(token.c_str(), SCANFORMAT, &dummy); + if (j == 0) + { + std::ostringstream errstr; + errstr << "Concentration data error for " << master_list << " in solution input."; + error_msg(errstr.str().c_str(), PHRQ_io::OT_CONTINUE); + return (CParser::PARSER_ERROR); + } + else + { + this->Set_input_conc(dummy); + } + if ((j = CParser::copy_token(token, b, e)) == CParser::TT_EMPTY) + return (CParser::PARSER_OK); + } +/* + * Read optional data + */ + std::string token1 = token; + +/* + * Check for units info + */ + CParser parser(this->io); + if (solution_ptr->Get_initial_data() == NULL) + { + error_msg("Initial_data instance not defined in cxxISolutionComp::read", 1); + } + if (parser.check_units(token1, alk, false, solution_ptr->Get_initial_data()->Get_units().c_str(), false) == CParser::PARSER_OK) + { + if (parser.check_units(token1, alk, false, solution_ptr->Get_initial_data()->Get_units().c_str(), true) == CParser::PARSER_OK) + { + this->units = token1; + if ((CParser::copy_token(token, b, e)) == CParser::TT_EMPTY) + return (CParser::PARSER_OK); + } + else + { + return (CParser::PARSER_ERROR); + } + } +/* + * Check for "as" followed by formula to be used for gfw + */ + token1 = token; + Utilities::str_tolower(token1); + if (strcmp(token1.c_str(), "as") == 0) + { + CParser::copy_token(token, b, e); + this->as = token; + if ((CParser::copy_token(token, b, e)) == CParser::TT_EMPTY) + return (CParser::PARSER_OK); +/* + * Check for "gfw" followed by gram formula weight + */ + } + else if (strcmp(token1.c_str(), "gfw") == 0 || strcmp(token1.c_str(), "gfm") == 0) + { + if (CParser::copy_token(token, b, e) != DIGIT) + { + error_msg("Expecting gram formula weight.", PHRQ_io::OT_CONTINUE); + return (CParser::PARSER_ERROR); + } + else + { + sscanf(token.c_str(), SCANFORMAT, &this->gfw); + if ((CParser::copy_token(token, b, e)) == CParser::TT_EMPTY) + return (CParser::PARSER_OK); + } + } +/* + * Check for redox couple for pe + */ + if (Utilities::strcmp_nocase(token.c_str(), "pe") == 0) + { + this->pe_reaction = token; + if ((CParser::copy_token(token, b, e)) == CParser::TT_EMPTY) + return (CParser::PARSER_OK); + } + else if (strstr(token.c_str(), "/") != NULL) + { + if (parser.parse_couple(token) == CParser::PARSER_OK) + { + this->pe_reaction = token; + if ((CParser::copy_token(token, b, e)) == CParser::TT_EMPTY) + return (CParser::PARSER_OK); + } + else + { + return (CParser::PARSER_ERROR); + } + } +/* + * Must have phase + */ + this->equation_name = token; + if (CParser::copy_token(token, b, e) == CParser::TT_EMPTY) + return (CParser::PARSER_OK); +/* + * Check for saturation index + */ + { + int j = sscanf(token.c_str(), SCANFORMAT, + &(this->phase_si)); + if (j != 1) + { + error_msg("Expected saturation index.", PHRQ_io::OT_CONTINUE); + return (CParser::PARSER_ERROR); + } + } + return (CParser::PARSER_OK); + +} \ No newline at end of file diff --git a/phreeqcpp/ISolutionComp.h b/phreeqcpp/ISolutionComp.h new file mode 100644 index 00000000..ef88e620 --- /dev/null +++ b/phreeqcpp/ISolutionComp.h @@ -0,0 +1,138 @@ +#if !defined(ISOLUTIONCOMP_H_INCLUDED) +#define ISOLUTIONCOMP_H_INCLUDED + +#include // ::strcmp +#include +#include // std::map +#include +#include +#include "phrqtype.h" // LDBLE +#include "Parser.h" // CParser +#include "PHRQ_base.h" // PHRQ_base + +// forward declarations +class cxxSolution; +class cxxISolution; // reqd for read and dump_xml +class PHRQ_io; + +class cxxISolutionComp: public PHRQ_base +{ + public: + cxxISolutionComp(PHRQ_io *io=NULL); + virtual ~cxxISolutionComp(void); + + public: + + CParser::STATUS_TYPE read(const char *line, cxxSolution *solution_ptr); + + void dump_xml(std::ostream & os, unsigned int indent = 0) const; + + const std::string &Get_description() const + { + return this->description; + } + void Set_description(const char *l_description) + { + if (l_description != NULL) + this->description = std::string(l_description); + else + this->description.clear(); + } + + LDBLE Get_moles() const + { + return this->moles; + } + void Set_moles(LDBLE l_moles) + { + this->moles = l_moles; + } + + LDBLE Get_input_conc() const + { + return this->input_conc; + } + void Set_input_conc(LDBLE l_input_conc) + { + this->input_conc = l_input_conc; + } + + std::string Get_units()const + { + return this->units; + } + void Set_units(const char *l_units) + { + if (l_units != NULL) + this->units = std::string(l_units); + else + this->units.clear(); + } + + const std::string &Get_equation_name() const + { + return this->equation_name; + } + void Set_equation_name(const char *l_equation_name) + { + if (l_equation_name != NULL) + this->equation_name = std::string(l_equation_name); + else + this->equation_name.clear(); + + } + + LDBLE Get_phase_si() const + { + return this->phase_si; + } + void Set_phase_si(int l_phase_si) + { + this->phase_si = l_phase_si; + } + std::string Get_pe_reaction() const + { + return this->pe_reaction; + } + void Set_pe_reaction(const std::string & pe_r) + { + this->pe_reaction = pe_r; + } + const std::string &Get_as() const + { + return this->as; + } + void Set_as(const char *l_as) + { + if (l_as != NULL) + this->as = std::string(l_as); + else + this->as.clear(); + } + LDBLE Get_gfw() const + { + return this->gfw; + }; + void Set_gfw(LDBLE l_gfw) + { + this->gfw = l_gfw; + } + + bool operator<(const cxxISolutionComp & conc) const + { + return ::strcmp(this->description.c_str(), conc.description.c_str()) < 0; + } + + protected: + std::string description; + LDBLE moles; + LDBLE input_conc; + std::string units; + std::string equation_name; + LDBLE phase_si; + std::string pe_reaction; + std::string as; + LDBLE gfw; +}; + +#endif // ISOLUTIONCOMP_H_INCLUDED diff --git a/phreeqcpp/KineticsComp.cxx b/phreeqcpp/KineticsComp.cxx new file mode 100644 index 00000000..3c9cd177 --- /dev/null +++ b/phreeqcpp/KineticsComp.cxx @@ -0,0 +1,355 @@ +// KineticsComp.cxx: implementation of the cxxKineticsComp class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "KineticsComp.h" +//#include "Dictionary.h" +#include "phqalloc.h" +#include "Dictionary.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxKineticsComp::cxxKineticsComp(PHRQ_io *io) +: +PHRQ_base(io) + // + // default constructor for cxxKineticsComp + // +{ + tol = 1e-8; + m = -1; + m0 = -1; + moles = 0.0; + initial_moles = 0; + namecoef.type = cxxNameDouble::ND_NAME_COEF; +} +cxxKineticsComp::~cxxKineticsComp() +{ +} + +#ifdef SKIP +void +cxxKineticsComp::dump_xml(std::ostream & s_oss, unsigned int indent) const const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Kinetics_Comp element and attributes + + s_oss << indent0 << "formula=\"" << this->formula << "\"" << "\n"; + s_oss << indent0 << "moles=\"" << this->moles << "\"" << "\n"; + s_oss << indent0 << "la=\"" << this->la << "\"" << "\n"; + s_oss << indent0 << "charge_balance=\"" << this-> + charge_balance << "\"" << "\n"; + if (this->phase_name != NULL) + { + s_oss << indent0 << "phase_name=\"" << this-> + phase_name << "\"" << "\n"; + } + if (this->rate_name != NULL) + { + s_oss << indent0 << "rate_name=\"" << this-> + rate_name << "\"" << "\n"; + } + s_oss << indent0 << "phase_proportion=\"" << this-> + phase_proportion << "\"" << "\n"; + + // totals + s_oss << indent0; + s_oss << "totals.dump_xml(s_oss, indent + 1); + + // formula_totals + s_oss << indent0; + s_oss << "formula_totals.dump_xml(s_oss, indent + 1); +} +#endif +void +cxxKineticsComp::dump_raw(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Kinetics_Comp element and attributes + s_oss << indent1 << "# KINETICS_MODIFY candidate identifiers #\n"; + s_oss << indent1 << "-tol " << this->tol << "\n"; + s_oss << indent1 << "-m " << this->m << "\n"; + s_oss << indent1 << "-m0 " << this->m0 << "\n"; + + // namecoef + s_oss << indent1; + s_oss << "-namecoef" << "\n"; + this->namecoef.dump_raw(s_oss, indent + 2); + + // d_params + s_oss << indent1; + s_oss << "-d_params" << "\n"; + { + int i = 0; + s_oss << indent2; + for (std::vector < LDBLE >::const_iterator it = d_params.begin(); + it != d_params.end(); it++) + { + if (i++ == 5) + { + s_oss << "\n"; + s_oss << indent2; + i = 0; + } + s_oss << *it << " "; + } + s_oss << "\n"; + } + + s_oss << indent1 << "# KineticsComp workspace variables #\n"; + s_oss << indent1 << "-moles " << this->moles << "\n"; + s_oss << indent1 << "-initial_moles " << this->initial_moles << "\n"; +} + +void +cxxKineticsComp::read_raw(CParser & parser, bool check) +{ + std::string str; + + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + std::vector < LDBLE > temp_d_params; + opt_save = CParser::OPT_ERROR; + bool tol_defined(false); + bool m_defined(false); + bool m0_defined(false); + bool d_params_defined(false); + + for (;;) + { + int opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_KEYWORD; + // Allow return to Kinetics for more processing + break; + + case 0: // rate_name not used + parser.warning_msg("Rate_name ignored. Define in -comp."); + break; + + case 1: // tol + if (!(parser.get_iss() >> this->tol)) + { + this->tol = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for tol.", + PHRQ_io::OT_CONTINUE); + } + tol_defined = true; + break; + + case 2: // m + if (!(parser.get_iss() >> this->m)) + { + this->m = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for m.", + PHRQ_io::OT_CONTINUE); + } + m_defined = true; + break; + + case 3: // m0 + if (!(parser.get_iss() >> this->m0)) + { + this->m0 = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for m0.", + PHRQ_io::OT_CONTINUE); + } + m0_defined = true; + break; + + + case 4: // moles + if (!(parser.get_iss() >> this->moles)) + { + this->moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for moles.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 5: // namecoef + if (this->namecoef.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected element name and molality for namecoef.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 5; + break; + + case 6: // d_params + while (parser.copy_token(token, next_char) == CParser::TT_DIGIT) + { + double dd; + sscanf(token.c_str(), "%lf", &dd); + temp_d_params.push_back((LDBLE) dd); + d_params_defined = true; + } + opt_save = 6; + break; + case 7: // initial_moles + if (!(parser.get_iss() >> this->initial_moles)) + { + this->moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for initial_moles.", + PHRQ_io::OT_CONTINUE); + } + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + + if (d_params_defined) + { + this->d_params = temp_d_params; + } + if (check) + { + // members that must be defined + if (tol_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Tol not defined for KineticsComp input.", + PHRQ_io::OT_CONTINUE); + } + if (m_defined == false) + { + parser.incr_input_error(); + parser.error_msg("M not defined for KineticsComp input.", + PHRQ_io::OT_CONTINUE); + } + if (m0_defined == false) + { + parser.incr_input_error(); + parser.error_msg("M0 not defined for KineticsComp input.", + PHRQ_io::OT_CONTINUE); + } + } +} + +void +cxxKineticsComp::add(const cxxKineticsComp & addee, LDBLE extensive) +{ + if (extensive == 0.0) + return; + if (addee.rate_name.size() == 0) + return; + // this and addee must have same name + // otherwise generate a new KineticsComp with multiply + if (this->rate_name.size() == 0 && addee.rate_name.size() == 0) + { + return; + } + assert(this->rate_name == addee.rate_name); + this->m += addee.m * extensive; + this->m0 += addee.m0 * extensive; + this->moles += addee.moles * extensive; +} + +void +cxxKineticsComp::multiply(LDBLE extensive) +{ + this->m *= extensive; + this->m0 *= extensive; + this->moles *= extensive; +} +void +cxxKineticsComp::Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles) +{ + ints.push_back(dictionary.Find(this->rate_name)); + this->namecoef.Serialize(dictionary, ints, doubles); + doubles.push_back(this->tol); + doubles.push_back(this->m); + doubles.push_back(this->m0); + ints.push_back((int) this->d_params.size()); + for (size_t i = 0; i < this->d_params.size(); i++) + { + doubles.push_back(d_params[i]); + } + doubles.push_back(this->moles); + doubles.push_back(this->initial_moles); + this->moles_of_reaction.Serialize(dictionary, ints, doubles); +} +void +cxxKineticsComp::Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd) +{ + this->rate_name = dictionary.GetWords()[ints[ii++]]; + this->namecoef.Deserialize(dictionary, ints, doubles, ii, dd); + this->tol = doubles[dd++]; + this->m = doubles[dd++]; + this->m0 = doubles[dd++]; + int n = ints[ii++]; + this->d_params.clear(); + for (int j = 0; j < n; j++) + { + this->d_params.push_back(doubles[dd++]); + } + this->moles = doubles[dd++]; + this->initial_moles = doubles[dd++]; + this->moles_of_reaction.Deserialize(dictionary, ints, doubles, ii, dd); +} +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("rate_name_not_used"), // 0 + std::vector< std::string >::value_type("tol"), // 1 + std::vector< std::string >::value_type("m"), // 2 + std::vector< std::string >::value_type("m0"), // 3 + std::vector< std::string >::value_type("moles"), // 4 + std::vector< std::string >::value_type("namecoef"), // 5 + std::vector< std::string >::value_type("d_params"), // 6 + std::vector< std::string >::value_type("initial_moles") // 7 +}; +const std::vector< std::string > cxxKineticsComp::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/KineticsComp.h b/phreeqcpp/KineticsComp.h new file mode 100644 index 00000000..eb3d954f --- /dev/null +++ b/phreeqcpp/KineticsComp.h @@ -0,0 +1,81 @@ +#if !defined(KINETICSCOMP_H_INCLUDED) +#define KINETICSCOMP_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NameDouble.h" + +class cxxKineticsComp: public PHRQ_base +{ + +public: + cxxKineticsComp(PHRQ_io *io=NULL); + cxxKineticsComp(struct kinetics_comp *, PHRQ_io *io=NULL); + virtual ~cxxKineticsComp(); + + void dump_xml(std::ostream & os, unsigned int indent = 0) const; + + void dump_raw(std::ostream & s_oss, unsigned int indent) const; + + void read_raw(CParser & parser, bool check = true); + + const std::string &Get_rate_name() const + { + return this->rate_name; + } + void Set_rate_name(const char * s) + { + if (s != NULL) + this->rate_name = std::string(s); + else + this->rate_name.clear(); + } + + cxxNameDouble &Get_namecoef(void) {return namecoef;} + const cxxNameDouble &Get_namecoef(void)const {return namecoef;} + void Set_namecoef(const cxxNameDouble nd) {namecoef = nd;} + LDBLE Get_tol(void) const {return tol;} + void Set_tol(LDBLE t) {tol = t;} + LDBLE Get_m(void) const {return m;} + void Set_m(LDBLE t) {m = t;} + LDBLE Get_m0(void) const {return m0;} + void Set_m0(LDBLE t) {m0 = t;} + LDBLE Get_moles(void) const {return moles;} + void Set_moles(LDBLE t) {moles = t;} + LDBLE Get_initial_moles(void) const {return initial_moles;} + void Set_initial_moles(LDBLE t) {initial_moles = t;} + std::vector < LDBLE > &Get_d_params(void) {return d_params;}; + const std::vector < LDBLE > &Get_d_params(void)const {return d_params;}; + std::vector < std::string > &Get_c_params(void) {return c_params;}; + cxxNameDouble &Get_moles_of_reaction(void) {return moles_of_reaction;} + const cxxNameDouble &Get_moles_of_reaction(void)const {return moles_of_reaction;} + void Set_moles_of_reaction(const cxxNameDouble nd) {moles_of_reaction = nd;} + + void add(const cxxKineticsComp & comp, LDBLE extensive); + void multiply(LDBLE extensive); + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + + protected: + std::string rate_name; + // KINETICS_MODIFY candidates + cxxNameDouble namecoef; //stoichiometry of reaction + LDBLE tol; + LDBLE m; + LDBLE m0; + std::vector< LDBLE > d_params; + std::vector< std::string > c_params; // Not used + // kinetics workspace variables + LDBLE moles; + LDBLE initial_moles; + cxxNameDouble moles_of_reaction; + const static std::vector < std::string > vopts; + public: + +}; + +#endif // !defined(KINETICSCOMP_H_INCLUDED) diff --git a/phreeqcpp/Makefile b/phreeqcpp/Makefile new file mode 100644 index 00000000..f1a978bb --- /dev/null +++ b/phreeqcpp/Makefile @@ -0,0 +1,639 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# src/Makefile. Generated from Makefile.in by configure. + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + + + +pkgdatadir = $(datadir)/phreeqc +pkgincludedir = $(includedir)/phreeqc +pkglibdir = $(libdir)/phreeqc +pkglibexecdir = $(libexecdir)/phreeqc +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +bin_PROGRAMS = phreeqc$(EXEEXT) +am__append_1 = \ + cl1mp.cpp + +subdir = src +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +PROGRAMS = $(bin_PROGRAMS) +am__phreeqc_SOURCES_DIST = class_main.cpp cxxKinetics.cxx \ + cxxKinetics.h cxxMix.cxx cxxMix.h dumper.cpp dumper.h \ + Exchange.cxx Exchange.h ExchComp.cxx ExchComp.h GasComp.cxx \ + GasComp.h GasPhase.cxx GasPhase.h ISolution.cxx ISolution.h \ + ISolutionComp.cxx ISolutionComp.h Keywords.cpp Keywords.h \ + KineticsComp.cxx KineticsComp.h NameDouble.cxx NameDouble.h \ + NumKeyword.cxx NumKeyword.h Parser.cxx Parser.h PBasic.cpp \ + PBasic.h Phreeqc.cpp Phreeqc.h PHRQ_base.cxx PHRQ_base.h \ + PHRQ_io.cpp PHRQ_io.h PPassemblage.cxx PPassemblage.h \ + PPassemblageComp.cxx PPassemblageComp.h Pressure.cxx \ + Pressure.h Reaction.cxx Reaction.h ReadClass.cxx runner.cpp \ + runner.h SelectedOutput.cpp SelectedOutput.h Solution.cxx \ + Solution.h SolutionIsotope.cxx SolutionIsotope.h \ + SSassemblage.cxx SSassemblage.h SS.cxx SS.h SScomp.cxx \ + SScomp.h StorageBin.cxx StorageBin.h StorageBinList.cpp \ + StorageBinList.h Surface.cxx Surface.h SurfaceCharge.cxx \ + SurfaceCharge.h SurfaceComp.cxx SurfaceComp.h System.cxx \ + System.h Temperature.cxx Temperature.h Use.cpp Use.h \ + UserPunch.cpp UserPunch.h Utils.cxx Utils.h advection.cpp \ + basicsubs.cpp cl1.cpp cvdense.cpp cvdense.h cvode.cpp cvode.h \ + dense.cpp dense.h dw.cpp gases.cpp global_structures.h \ + input.cpp integrate.cpp inverse.cpp isotopes.cpp kinetics.cpp \ + mainsubs.cpp model.cpp NA.h nvector.cpp nvector.h \ + nvector_serial.cpp nvector_serial.h parse.cpp phqalloc.cpp \ + phqalloc.h PHRQ_io_output.cpp phrqtype.h pitzer.cpp \ + pitzer_structures.cpp prep.cpp print.cpp read.cpp readtr.cpp \ + sit.cpp smalldense.cpp smalldense.h spread.cpp step.cpp \ + structures.cpp sundialsmath.cpp sundialsmath.h sundialstypes.h \ + tally.cpp tidy.cpp transport.cpp utilities.cpp cl1mp.cpp +am__objects_1 = cl1mp.$(OBJEXT) +am_phreeqc_OBJECTS = class_main.$(OBJEXT) cxxKinetics.$(OBJEXT) \ + cxxMix.$(OBJEXT) dumper.$(OBJEXT) Exchange.$(OBJEXT) \ + ExchComp.$(OBJEXT) GasComp.$(OBJEXT) GasPhase.$(OBJEXT) \ + ISolution.$(OBJEXT) ISolutionComp.$(OBJEXT) Keywords.$(OBJEXT) \ + KineticsComp.$(OBJEXT) NameDouble.$(OBJEXT) \ + NumKeyword.$(OBJEXT) Parser.$(OBJEXT) PBasic.$(OBJEXT) \ + Phreeqc.$(OBJEXT) PHRQ_base.$(OBJEXT) PHRQ_io.$(OBJEXT) \ + PPassemblage.$(OBJEXT) PPassemblageComp.$(OBJEXT) \ + Pressure.$(OBJEXT) Reaction.$(OBJEXT) ReadClass.$(OBJEXT) \ + runner.$(OBJEXT) SelectedOutput.$(OBJEXT) Solution.$(OBJEXT) \ + SolutionIsotope.$(OBJEXT) SSassemblage.$(OBJEXT) SS.$(OBJEXT) \ + SScomp.$(OBJEXT) StorageBin.$(OBJEXT) StorageBinList.$(OBJEXT) \ + Surface.$(OBJEXT) SurfaceCharge.$(OBJEXT) \ + SurfaceComp.$(OBJEXT) System.$(OBJEXT) Temperature.$(OBJEXT) \ + Use.$(OBJEXT) UserPunch.$(OBJEXT) Utils.$(OBJEXT) \ + advection.$(OBJEXT) basicsubs.$(OBJEXT) cl1.$(OBJEXT) \ + cvdense.$(OBJEXT) cvode.$(OBJEXT) dense.$(OBJEXT) dw.$(OBJEXT) \ + gases.$(OBJEXT) input.$(OBJEXT) integrate.$(OBJEXT) \ + inverse.$(OBJEXT) isotopes.$(OBJEXT) kinetics.$(OBJEXT) \ + mainsubs.$(OBJEXT) model.$(OBJEXT) nvector.$(OBJEXT) \ + nvector_serial.$(OBJEXT) parse.$(OBJEXT) phqalloc.$(OBJEXT) \ + PHRQ_io_output.$(OBJEXT) pitzer.$(OBJEXT) \ + pitzer_structures.$(OBJEXT) prep.$(OBJEXT) print.$(OBJEXT) \ + read.$(OBJEXT) readtr.$(OBJEXT) sit.$(OBJEXT) \ + smalldense.$(OBJEXT) spread.$(OBJEXT) step.$(OBJEXT) \ + structures.$(OBJEXT) sundialsmath.$(OBJEXT) tally.$(OBJEXT) \ + tidy.$(OBJEXT) transport.$(OBJEXT) utilities.$(OBJEXT) \ + $(am__objects_1) +phreeqc_OBJECTS = $(am_phreeqc_OBJECTS) +phreeqc_LDADD = $(LDADD) +am__DEPENDENCIES_1 = +phreeqc_DEPENDENCIES = $(am__DEPENDENCIES_1) +DEFAULT_INCLUDES = -I. +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(phreeqc_SOURCES) +DIST_SOURCES = $(am__phreeqc_SOURCES_DIST) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = ${SHELL} /raid/home/dlpark/programs/phreeqc3-trunk/config/missing --run aclocal-1.11 +AMTAR = ${SHELL} /raid/home/dlpark/programs/phreeqc3-trunk/config/missing --run tar +AUTOCONF = ${SHELL} /raid/home/dlpark/programs/phreeqc3-trunk/config/missing --run autoconf +AUTOHEADER = ${SHELL} /raid/home/dlpark/programs/phreeqc3-trunk/config/missing --run autoheader +AUTOMAKE = ${SHELL} /raid/home/dlpark/programs/phreeqc3-trunk/config/missing --run automake-1.11 +AWK = gawk +CPPFLAGS = +CXX = g++ +CXXCPP = g++ -E +CXXDEPMODE = depmode=gcc3 +CXXFLAGS = -g -O2 +CYGPATH_W = echo +DEFS = -DPACKAGE_NAME=\"phreeqc\" -DPACKAGE_TARNAME=\"phreeqc\" -DPACKAGE_VERSION=\"3.0.0-7109\" -DPACKAGE_STRING=\"phreeqc\ 3.0.0-7109\" -DPACKAGE_BUGREPORT=\"dlpark@usgs.gov\" -DPACKAGE=\"phreeqc\" -DVERSION=\"3.0.0-7109\" -DNDEBUG=1 -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_FLOAT_H=1 -DHAVE_LIMITS_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STDDEF_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STDLIB_H=1 -DHAVE_MALLOC=1 -DHAVE_STDLIB_H=1 -DHAVE_REALLOC=1 -DHAVE_MEMMOVE=1 -DHAVE_MEMSET=1 -DHAVE_STRCHR=1 -DHAVE_STRCSPN=1 -DHAVE_STRTOL=1 -DHAVE_FLOOR=1 -DHAVE_POW=1 -DHAVE_SQRT=1 -DHAVE_ISFINITE=/\*\*/ -DHAVE_FINITE=/\*\*/ -DHAVE_ISNAN=1 -DINVERSE_CL1MP=1 +DEPDIR = .deps +ECHO_C = +ECHO_N = -n +ECHO_T = +EGREP = /bin/grep -E +EXEEXT = +GREP = /bin/grep +INSTALL = /usr/bin/install -c +INSTALL_DATA = ${INSTALL} -m 644 +INSTALL_PROGRAM = ${INSTALL} +INSTALL_SCRIPT = ${INSTALL} +INSTALL_STRIP_PROGRAM = $(install_sh) -c -s +LDFLAGS = +LIBGMP = -lgmp +LIBOBJS = +LIBS = +LTLIBOBJS = +MAKEINFO = ${SHELL} /raid/home/dlpark/programs/phreeqc3-trunk/config/missing --run makeinfo +MKDIR_P = /bin/mkdir -p +OBJEXT = o +PACKAGE = phreeqc +PACKAGE_BUGREPORT = dlpark@usgs.gov +PACKAGE_NAME = phreeqc +PACKAGE_STRING = phreeqc 3.0.0-7109 +PACKAGE_TARNAME = phreeqc +PACKAGE_VERSION = 3.0.0-7109 +PATH_SEPARATOR = : +POW_LIB = +SET_MAKE = +SHELL = /bin/sh +STRIP = +VERSION = 3.0.0-7109 +abs_builddir = /raid/home/dlpark/programs/phreeqc3-trunk/src +abs_srcdir = /raid/home/dlpark/programs/phreeqc3-trunk/src +abs_top_builddir = /raid/home/dlpark/programs/phreeqc3-trunk +abs_top_srcdir = /raid/home/dlpark/programs/phreeqc3-trunk +ac_ct_CXX = g++ +am__include = include +am__leading_dot = . +am__quote = +am__tar = ${AMTAR} chof - "$$tardir" +am__untar = ${AMTAR} xf - +bindir = ${exec_prefix}/bin +build_alias = +builddir = . +datadir = ${datarootdir} +datarootdir = ${prefix}/share +docdir = ${datarootdir}/doc/${PACKAGE_TARNAME} +dvidir = ${docdir} +exec_prefix = ${prefix} +host_alias = +htmldir = ${docdir} +includedir = ${prefix}/include +infodir = ${datarootdir}/info +install_sh = ${SHELL} /raid/home/dlpark/programs/phreeqc3-trunk/config/install-sh +libdir = ${exec_prefix}/lib +libexecdir = ${exec_prefix}/libexec +localedir = ${datarootdir}/locale +localstatedir = ${prefix}/var +mandir = ${datarootdir}/man +mkdir_p = /bin/mkdir -p +oldincludedir = /usr/include +pdfdir = ${docdir} +prefix = /usr/local +program_transform_name = s,x,x, +psdir = ${docdir} +sbindir = ${exec_prefix}/sbin +sharedstatedir = ${prefix}/com +srcdir = . +sysconfdir = ${prefix}/etc +target_alias = +top_build_prefix = ../ +top_builddir = .. +top_srcdir = .. +AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/phreeqc + +# sources for phreeqc +phreeqc_SOURCES = class_main.cpp cxxKinetics.cxx cxxKinetics.h \ + cxxMix.cxx cxxMix.h dumper.cpp dumper.h Exchange.cxx \ + Exchange.h ExchComp.cxx ExchComp.h GasComp.cxx GasComp.h \ + GasPhase.cxx GasPhase.h ISolution.cxx ISolution.h \ + ISolutionComp.cxx ISolutionComp.h Keywords.cpp Keywords.h \ + KineticsComp.cxx KineticsComp.h NameDouble.cxx NameDouble.h \ + NumKeyword.cxx NumKeyword.h Parser.cxx Parser.h PBasic.cpp \ + PBasic.h Phreeqc.cpp Phreeqc.h PHRQ_base.cxx PHRQ_base.h \ + PHRQ_io.cpp PHRQ_io.h PPassemblage.cxx PPassemblage.h \ + PPassemblageComp.cxx PPassemblageComp.h Pressure.cxx \ + Pressure.h Reaction.cxx Reaction.h ReadClass.cxx runner.cpp \ + runner.h SelectedOutput.cpp SelectedOutput.h Solution.cxx \ + Solution.h SolutionIsotope.cxx SolutionIsotope.h \ + SSassemblage.cxx SSassemblage.h SS.cxx SS.h SScomp.cxx \ + SScomp.h StorageBin.cxx StorageBin.h StorageBinList.cpp \ + StorageBinList.h Surface.cxx Surface.h SurfaceCharge.cxx \ + SurfaceCharge.h SurfaceComp.cxx SurfaceComp.h System.cxx \ + System.h Temperature.cxx Temperature.h Use.cpp Use.h \ + UserPunch.cpp UserPunch.h Utils.cxx Utils.h advection.cpp \ + basicsubs.cpp cl1.cpp cvdense.cpp cvdense.h cvode.cpp cvode.h \ + dense.cpp dense.h dw.cpp gases.cpp global_structures.h \ + input.cpp integrate.cpp inverse.cpp isotopes.cpp kinetics.cpp \ + mainsubs.cpp model.cpp NA.h nvector.cpp nvector.h \ + nvector_serial.cpp nvector_serial.h parse.cpp phqalloc.cpp \ + phqalloc.h PHRQ_io_output.cpp phrqtype.h pitzer.cpp \ + pitzer_structures.cpp prep.cpp print.cpp read.cpp readtr.cpp \ + sit.cpp smalldense.cpp smalldense.h spread.cpp step.cpp \ + structures.cpp sundialsmath.cpp sundialsmath.h sundialstypes.h \ + tally.cpp tidy.cpp transport.cpp utilities.cpp $(am__append_1) +LDADD = $(LIBGMP) +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .cxx .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +phreeqc$(EXEEXT): $(phreeqc_OBJECTS) $(phreeqc_DEPENDENCIES) + @rm -f phreeqc$(EXEEXT) + $(CXXLINK) $(phreeqc_OBJECTS) $(phreeqc_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +include ./$(DEPDIR)/ExchComp.Po +include ./$(DEPDIR)/Exchange.Po +include ./$(DEPDIR)/GasComp.Po +include ./$(DEPDIR)/GasPhase.Po +include ./$(DEPDIR)/ISolution.Po +include ./$(DEPDIR)/ISolutionComp.Po +include ./$(DEPDIR)/Keywords.Po +include ./$(DEPDIR)/KineticsComp.Po +include ./$(DEPDIR)/NameDouble.Po +include ./$(DEPDIR)/NumKeyword.Po +include ./$(DEPDIR)/PBasic.Po +include ./$(DEPDIR)/PHRQ_base.Po +include ./$(DEPDIR)/PHRQ_io.Po +include ./$(DEPDIR)/PHRQ_io_output.Po +include ./$(DEPDIR)/PPassemblage.Po +include ./$(DEPDIR)/PPassemblageComp.Po +include ./$(DEPDIR)/Parser.Po +include ./$(DEPDIR)/Phreeqc.Po +include ./$(DEPDIR)/Pressure.Po +include ./$(DEPDIR)/Reaction.Po +include ./$(DEPDIR)/ReadClass.Po +include ./$(DEPDIR)/SS.Po +include ./$(DEPDIR)/SSassemblage.Po +include ./$(DEPDIR)/SScomp.Po +include ./$(DEPDIR)/SelectedOutput.Po +include ./$(DEPDIR)/Solution.Po +include ./$(DEPDIR)/SolutionIsotope.Po +include ./$(DEPDIR)/StorageBin.Po +include ./$(DEPDIR)/StorageBinList.Po +include ./$(DEPDIR)/Surface.Po +include ./$(DEPDIR)/SurfaceCharge.Po +include ./$(DEPDIR)/SurfaceComp.Po +include ./$(DEPDIR)/System.Po +include ./$(DEPDIR)/Temperature.Po +include ./$(DEPDIR)/Use.Po +include ./$(DEPDIR)/UserPunch.Po +include ./$(DEPDIR)/Utils.Po +include ./$(DEPDIR)/advection.Po +include ./$(DEPDIR)/basicsubs.Po +include ./$(DEPDIR)/cl1.Po +include ./$(DEPDIR)/cl1mp.Po +include ./$(DEPDIR)/class_main.Po +include ./$(DEPDIR)/cvdense.Po +include ./$(DEPDIR)/cvode.Po +include ./$(DEPDIR)/cxxKinetics.Po +include ./$(DEPDIR)/cxxMix.Po +include ./$(DEPDIR)/dense.Po +include ./$(DEPDIR)/dumper.Po +include ./$(DEPDIR)/dw.Po +include ./$(DEPDIR)/gases.Po +include ./$(DEPDIR)/input.Po +include ./$(DEPDIR)/integrate.Po +include ./$(DEPDIR)/inverse.Po +include ./$(DEPDIR)/isotopes.Po +include ./$(DEPDIR)/kinetics.Po +include ./$(DEPDIR)/mainsubs.Po +include ./$(DEPDIR)/model.Po +include ./$(DEPDIR)/nvector.Po +include ./$(DEPDIR)/nvector_serial.Po +include ./$(DEPDIR)/parse.Po +include ./$(DEPDIR)/phqalloc.Po +include ./$(DEPDIR)/pitzer.Po +include ./$(DEPDIR)/pitzer_structures.Po +include ./$(DEPDIR)/prep.Po +include ./$(DEPDIR)/print.Po +include ./$(DEPDIR)/read.Po +include ./$(DEPDIR)/readtr.Po +include ./$(DEPDIR)/runner.Po +include ./$(DEPDIR)/sit.Po +include ./$(DEPDIR)/smalldense.Po +include ./$(DEPDIR)/spread.Po +include ./$(DEPDIR)/step.Po +include ./$(DEPDIR)/structures.Po +include ./$(DEPDIR)/sundialsmath.Po +include ./$(DEPDIR)/tally.Po +include ./$(DEPDIR)/tidy.Po +include ./$(DEPDIR)/transport.Po +include ./$(DEPDIR)/utilities.Po + +.cpp.o: + $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< + $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +# source='$<' object='$@' libtool=no \ +# DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) \ +# $(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: + $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` + $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +# source='$<' object='$@' libtool=no \ +# DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) \ +# $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cxx.o: + $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< + $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +# source='$<' object='$@' libtool=no \ +# DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) \ +# $(CXXCOMPILE) -c -o $@ $< + +.cxx.obj: + $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` + $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +# source='$<' object='$@' libtool=no \ +# DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) \ +# $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic ctags distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-binPROGRAMS + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/phreeqcpp/Makefile.am b/phreeqcpp/Makefile.am new file mode 100644 index 00000000..b7651924 --- /dev/null +++ b/phreeqcpp/Makefile.am @@ -0,0 +1,159 @@ +EXTRA_DIST=\ + ChartHandler.cpp\ + ChartHandler.h\ + ChartObject.cpp\ + ChartObject.h\ + CurveObject.cpp\ + CurveObject.h\ + Form1.h\ + Form1.2005.resX\ + Form1.resX\ + ZedGraph.dll + +AM_CPPFLAGS=-I$(top_srcdir)/src -I$(top_srcdir)/src/common -I$(top_srcdir)/src/PhreeqcKeywords + +bin_PROGRAMS = phreeqc + +# sources for phreeqc +phreeqc_SOURCES=\ + advection.cpp\ + basicsubs.cpp\ + cl1.cpp\ + class_main.cpp\ + common/Parser.cxx\ + common/Parser.h\ + common/PHRQ_base.cxx\ + common/PHRQ_base.h\ + common/PHRQ_io.cpp\ + common/PHRQ_io.h\ + common/phrqtype.h\ + common/Utils.cxx\ + common/Utils.h\ + cvdense.cpp\ + cvdense.h\ + cvode.cpp\ + cvode.h\ + cxxKinetics.cxx\ + cxxKinetics.h\ + cxxMix.cxx\ + cxxMix.h\ + dense.cpp\ + dense.h\ + Dictionary.cpp\ + Dictionary.h\ + dumper.cpp\ + dumper.h\ + Exchange.cxx\ + Exchange.h\ + ExchComp.cxx\ + ExchComp.h\ + GasComp.cxx\ + GasComp.h\ + gases.cpp\ + GasPhase.cxx\ + GasPhase.h\ + global_structures.h\ + input.cpp\ + integrate.cpp\ + inverse.cpp\ + ISolution.cxx\ + ISolution.h\ + ISolutionComp.cxx\ + ISolutionComp.h\ + isotopes.cpp\ + kinetics.cpp\ + KineticsComp.cxx\ + KineticsComp.h\ + mainsubs.cpp\ + model.cpp\ + NA.h\ + NameDouble.cxx\ + NameDouble.h\ + NumKeyword.cxx\ + NumKeyword.h\ + nvector.cpp\ + nvector.h\ + nvector_serial.cpp\ + nvector_serial.h\ + parse.cpp\ + PBasic.cpp\ + PBasic.h\ + phqalloc.cpp\ + phqalloc.h\ + Phreeqc.cpp\ + Phreeqc.h\ + PhreeqcKeywords/Keywords.cpp\ + PhreeqcKeywords/Keywords.h\ + PHRQ_io_output.cpp\ + pitzer.cpp\ + pitzer_structures.cpp\ + PPassemblage.cxx\ + PPassemblage.h\ + PPassemblageComp.cxx\ + PPassemblageComp.h\ + prep.cpp\ + Pressure.cxx\ + Pressure.h\ + print.cpp\ + Reaction.cxx\ + Reaction.h\ + read.cpp\ + ReadClass.cxx\ + readtr.cpp\ + runner.cpp\ + runner.h\ + SelectedOutput.cpp\ + SelectedOutput.h\ + Serializer.cxx\ + Serializer.h\ + sit.cpp\ + smalldense.cpp\ + smalldense.h\ + Solution.cxx\ + Solution.h\ + SolutionIsotope.cxx\ + SolutionIsotope.h\ + spread.cpp\ + SS.cxx\ + SS.h\ + SSassemblage.cxx\ + SSassemblage.h\ + SScomp.cxx\ + SScomp.h\ + step.cpp\ + StorageBin.cxx\ + StorageBin.h\ + StorageBinList.cpp\ + StorageBinList.h\ + structures.cpp\ + sundialsmath.cpp\ + sundialsmath.h\ + sundialstypes.h\ + Surface.cxx\ + Surface.h\ + SurfaceCharge.cxx\ + SurfaceCharge.h\ + SurfaceComp.cxx\ + SurfaceComp.h\ + System.cxx\ + System.h\ + tally.cpp\ + Temperature.cxx\ + Temperature.h\ + tidy.cpp\ + transport.cpp\ + Use.cpp\ + Use.h\ + UserPunch.cpp\ + UserPunch.h\ + utilities.cpp + +if BUILD_GMP + +phreeqc_SOURCES+=\ + cl1mp.cpp + +endif + + +LDADD = $(LIBGMP) diff --git a/phreeqcpp/Makefile.old b/phreeqcpp/Makefile.old new file mode 100644 index 00000000..6d7279c2 --- /dev/null +++ b/phreeqcpp/Makefile.old @@ -0,0 +1,986 @@ +# Makefile for PHREEQCPP +# +# Generates object files and executables for 2 versions of PHREEQCPP +# Release +# Debug +# +# Serial verisons: Release Debug +# +# Makefile sets CFG variable, cd's to appropriate directory, runs Makefile recursively +# Recursive make sets appropriate compiler, objects, options, libraries, and compiles PHREEQC +# + +PROGRAM = phreeqc + +CFG1 :=`uname` +CFG :=$(shell echo $(CFG1) | sed "s/Linux.*/Linux/") +ifeq ($(CFG), Linux) + SPOOL=>& + SPOOL2= + CONCAT=; +else + SPOOL=> + SPOOL2=2>&1 + CONCAT=& +endif +all: class_release class_debug + +Debug: class_debug +debug: class_debug +Class_debug: class_debug + +Release: class_release +release: class_release +Class_release: class_release + +Debug_64: class_debug_64 +debug_64: class_debug_64 +Class_debug_64: class_debug_64 + +Release_64: class_release_64 +release_64: class_release_64 +Class_release_64: class_release_64 + + +CLASS_DEBUG_DIR = Class_debug +CLASS_DIR = Class_release +CLASS_DEBUG_64_DIR = Class_debug_64 +CLASS_64_DIR = Class_release_64 +MAKEFILE = Makefile.old + +# ----------------------------------------------------------------------------- +# fixes shared object lookup error(SIGFPE floating point exception) +HASH_STYLE=$(call ld-option, -Wl$(comma)--hash-style=sysv) + + +######################### +#### Serial Versions #### +######################### +.PHONY : Class_debug +class_debug: + mkdir -p $(CLASS_DEBUG_DIR) + cd $(CLASS_DEBUG_DIR); $(MAKE) -r -f ../$(MAKEFILE) CFG=CLASS_DEBUG $(PROGRAM) + +.PHONY : Class_release +class_release: + mkdir -p $(CLASS_DIR) + cd $(CLASS_DIR); $(MAKE) -r -f ../$(MAKEFILE) CFG=CLASS_RELEASE $(PROGRAM) +.PHONY : Class_debug_64 +class_debug_64: + mkdir -p $(CLASS_DEBUG_64_DIR) + cd $(CLASS_DEBUG_64_DIR); $(MAKE) -r -f ../$(MAKEFILE) CFG=CLASS_DEBUG_64 $(PROGRAM) + +.PHONY : Class_release_64 +class_release_64: + mkdir -p $(CLASS_64_DIR) + cd $(CLASS_64_DIR); $(MAKE) -r -f ../$(MAKEFILE) CFG=CLASS_RELEASE_64 $(PROGRAM) + +# Recursive make begins here +# +# ============================================================================= +# Significant suffixes [assuming Fortran 90 (.f90) source code]: +# Significant suffixes [assuming Fortran 90 (.F90) source code, needs to be preprocessed ]: +# ============================================================================= + +#SRC = ../phreeqc +.SUFFIXES : .o .c .cxx .cpp + +# compilers + +# ----------------------------------------------------------------------------- +.c.o : + ${CXX} ${CXXFLAGS} -c -o $@ $< + +.cxx.o : + ${CXX} ${CXXFLAGS} -c -o $@ $< + +.cpp.o : + ${CXX} ${CXXFLAGS} -c -o $@ $< + +# ----------------------------------------------------------------------------- +#hdf options +#HDF5_ROOT=$(HOME)/../../usr +#HDF5_INCLUDES=-I$(HDF5_ROOT)/src +#HDF5_LIBS=${HDF5_ROOT}/lib/libhdf5.a -lz -lpthread + +# ----------------------------------------------------------------------------- +# #define compile options + +# ----------------------------------------------------------------------------- +# #define gmp for inverse modeling +# comment the following lines to remove multiprecision option + +# ----------------------------------------------------------------------------- +#efence for debugging +EFENCE_LIB=-L$(HOME)/packages/efence + +# ----------------------------------------------------------------------------- +# 4 Versions +# ----------------------------------------------------------------------------- +ifeq ($(CFG), CLASS_DEBUG) + INVERSE_CL1MP= + ifdef INVERSE_CL1MP + DEFINE_INVERSE_CL1MP=-DINVERSE_CL1MP + CL1MP_OBJS=cl1mp.o +# CL1MP_LIB=-lgmp + CL1MP_LIB=/z/parkplace/usr/lib/libgmp.a + endif + DEFINES = -DUSE_PHRQ_ALLOC $(DEFINE_INVERSE_CL1MP) # -DPHREEQC2 + VPATH = ..:../PhreeqcKeywords:../common + INCLUDES = -I.. -I../PhreeqcKeywords -I../common + CXX = g++ + CXXFLAGS = -Wall -g $(DEFINES) $(INCLUDES) + OBJECT_FILES = $(CLASS_FILES) $(COMMON_COBJS) $(COMMON_CXXOBJS) $(CL1MP_OBJS) + LD_FLAGS = -lm ${CL1MP_LIB} ${HASH_STYLE} +endif + +ifeq ($(CFG), CLASS_RELEASE) + INVERSE_CL1MP= + ifdef INVERSE_CL1MP + DEFINE_INVERSE_CL1MP=-DINVERSE_CL1MP + CL1MP_OBJS=cl1mp.o +# CL1MP_LIB=-lgmp + CL1MP_LIB=/z/parkplace/usr/lib/libgmp.a + endif + DEFINES = -DNDEBUG $(DEFINE_INVERSE_CL1MP) # -DPHREEQC2 + VPATH = ..:../PhreeqcKeywords:../common + INCLUDES = -I.. -I../PhreeqcKeywords -I../common + CXX = g++ + CXXFLAGS = -Wall -pedantic -O3 $(DEFINES) $(INCLUDES) + OBJECT_FILES = $(CLASS_FILES) $(COMMON_COBJS) $(COMMON_CXXOBJS) $(CL1MP_OBJS) + LD_FLAGS = -lm ${CL1MP_LIB} ${HASH_STYLE} +endif + +ifeq ($(CFG), CLASS_DEBUG_64) +# INVERSE_CL1MP= + ifdef INVERSE_CL1MP + DEFINE_INVERSE_CL1MP=-DINVERSE_CL1MP + CL1MP_OBJS=cl1mp.o + CL1MP_LIB=libgmp.a + endif + DEFINES = -DUSE_PHRQ_ALLOC $(DEFINE_INVERSE_CL1MP) # -DPHREEQC2 + VPATH = ..:../PhreeqcKeywords:../common + INCLUDES = -I.. -I../PhreeqcKeywords -I../common + CXX = g++ + CXXFLAGS = -Wall -g $(DEFINES) $(INCLUDES) + OBJECT_FILES = $(CLASS_FILES) $(COMMON_COBJS) $(COMMON_CXXOBJS) $(CL1MP_OBJS) + LD_FLAGS = -lm ${CL1MP_LIB} ${HASH_STYLE} +endif + +ifeq ($(CFG), CLASS_RELEASE_64) + INVERSE_CL1MP= + ifdef INVERSE_CL1MP + DEFINE_INVERSE_CL1MP=-DINVERSE_CL1MP + CL1MP_OBJS=cl1mp.o + CL1MP_LIB=/usr/lib64/libgmp.a + endif + DEFINES = -DNDEBUG $(DEFINE_INVERSE_CL1MP) # -DUSE_PHRQ_ALLOC # -DR_SO + VPATH = ..:../PhreeqcKeywords:../common + INCLUDES = -I.. -I../PhreeqcKeywords -I../common + CXX = g++ + PROFILE = + CXXFLAGS = $(PROFILE) -Wall -pedantic -O3 $(DEFINES) $(INCLUDES) # -g +# CXXFLAGS = -fprofile-arcs -ftest-coverage $(DEFINES) $(INCLUDES) # -g + OBJECT_FILES = $(CLASS_FILES) $(COMMON_COBJS) $(COMMON_CXXOBJS) $(CL1MP_OBJS) + LD_FLAGS = $(PROFILE) -lm ${CL1MP_LIB} ${HASH_STYLE} # -L/usr/lib/gcc/x86_64-redhat-linux/4.4.4 -lgcov +endif + +# ----------------------------------------------------------------------------- +# + +COMMON_COBJS = \ + advection.o \ + basicsubs.o \ + cl1.o \ + cvdense.o \ + cvode.o \ + dense.o \ + gases.o \ + input.o \ + integrate.o \ + inverse.o \ + isotopes.o \ + kinetics.o \ + mainsubs.o \ + model.o \ + nvector.o \ + nvector_serial.o \ + parse.o \ + PHRQ_io_output.o \ + phqalloc.o \ + pitzer.o \ + pitzer_structures.o \ + prep.o \ + print.o \ + read.o \ + readtr.o \ + sit.o \ + smalldense.o \ + spread.o \ + step.o \ + structures.o \ + sundialsmath.o \ + tally.o \ + tidy.o \ + transport.o \ + utilities.o + +MAIN_FILE = main.o + +CLASS_FILES = \ + class_main.o \ + Phreeqc.o + + +COMMON_CXXOBJS = \ + dumper.o \ + Dictionary.o \ + Exchange.o \ + ExchComp.o \ + GasPhase.o \ + GasComp.o \ + ISolution.o \ + ISolutionComp.o \ + Keywords.o \ + KineticsComp.o \ + cxxKinetics.o \ + cxxMix.o \ + NameDouble.o \ + NumKeyword.o \ + Parser.o \ + PBasic.o \ + PHRQ_base.o \ + PHRQ_io.o \ + PPassemblageComp.o \ + PPassemblage.o \ + Pressure.o \ + Reaction.o \ + ReadClass.o \ + runner.o \ + SelectedOutput.o \ + Serializer.o \ + Solution.o \ + SolutionIsotope.o \ + SSassemblage.o \ + SScomp.o \ + SS.o \ + StorageBin.o \ + StorageBinList.o \ + Surface.o \ + SurfaceCharge.o \ + SurfaceComp.o \ + System.o \ + Temperature.o \ + Use.o \ + UserPunch.o \ + Utils.o + +# ----------------------------------------------------------------------------- +# Assign dependents to target on dependency line & link options on command +# line. Command line is initiated with a tab. ($@ is an internal macro for +# the current target.) + +${PROGRAM} : ${OBJECT_FILES} +# ${CXX} -p -o $@ ${OBJECT_FILES} ${LD_FLAGS} + ${CXX} -o $@ ${OBJECT_FILES} ${LD_FLAGS} + @echo; echo Done making for phreeqcpp +# ----------------------------------------------------------------------------- +# Module dependency list +# ----------------------------------------------------------------------------- +# +# CXX files +# + +cxxKinetics.o: ../cxxKinetics.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../cxxKinetics.h ../KineticsComp.h \ + ../phqalloc.h ../Dictionary.h +cxxMix.o: ../cxxMix.cxx ../common/Utils.h ../common/phrqtype.h \ + ../common/Parser.h ../common/PHRQ_base.h ../PhreeqcKeywords/Keywords.h \ + ../common/PHRQ_io.h ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../SelectedOutput.h ../NumKeyword.h ../UserPunch.h \ + ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h ../SurfaceComp.h \ + ../NameDouble.h ../SurfaceCharge.h ../global_structures.h ../NA.h \ + ../phqalloc.h +Exchange.o: ../Exchange.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../Exchange.h ../ExchComp.h ../phqalloc.h +ExchComp.o: ../ExchComp.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../ExchComp.h ../phqalloc.h \ + ../Dictionary.h +GasComp.o: ../GasComp.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../GasComp.h ../phqalloc.h \ + ../Dictionary.h +GasPhase.o: ../GasPhase.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../GasPhase.h ../GasComp.h ../phqalloc.h +ISolutionComp.o: ../ISolutionComp.cxx ../common/Utils.h \ + ../common/phrqtype.h ../ISolutionComp.h ../common/phrqtype.h \ + ../common/Parser.h ../common/PHRQ_base.h ../PhreeqcKeywords/Keywords.h \ + ../common/PHRQ_io.h ../common/PHRQ_base.h ../Solution.h ../NumKeyword.h \ + ../SolutionIsotope.h ../NameDouble.h ../common/PHRQ_io.h ../ISolution.h \ + ../global_structures.h ../Surface.h ../SurfaceComp.h ../SurfaceCharge.h \ + ../NA.h ../phqalloc.h +ISolution.o: ../ISolution.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../ISolution.h ../ISolutionComp.h \ + ../phqalloc.h +KineticsComp.o: ../KineticsComp.cxx ../common/Utils.h \ + ../common/phrqtype.h ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../KineticsComp.h ../phqalloc.h \ + ../Dictionary.h +NameDouble.o: ../NameDouble.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../Dictionary.h ../phqalloc.h \ + ../ISolutionComp.h +NumKeyword.o: ../NumKeyword.cxx ../NumKeyword.h ../common/PHRQ_base.h \ + ../common/Parser.h ../common/PHRQ_base.h ../PhreeqcKeywords/Keywords.h \ + ../common/PHRQ_io.h ../common/Utils.h ../common/phrqtype.h +PPassemblageComp.o: ../PPassemblageComp.cxx ../common/Utils.h \ + ../common/phrqtype.h ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../PPassemblageComp.h ../Dictionary.h \ + ../phqalloc.h +PPassemblage.o: ../PPassemblage.cxx ../common/Utils.h \ + ../common/phrqtype.h ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../PPassemblage.h ../PPassemblageComp.h \ + ../phqalloc.h +Pressure.o: ../Pressure.cxx ../common/Utils.h ../common/phrqtype.h \ + ../common/Parser.h ../common/PHRQ_base.h ../PhreeqcKeywords/Keywords.h \ + ../common/PHRQ_io.h ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../SelectedOutput.h ../NumKeyword.h ../UserPunch.h \ + ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h ../SurfaceComp.h \ + ../NameDouble.h ../SurfaceCharge.h ../global_structures.h ../NA.h \ + ../phqalloc.h +Reaction.o: ../Reaction.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../Reaction.h ../phqalloc.h +ReadClass.o: ../ReadClass.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../Solution.h ../SolutionIsotope.h \ + ../ISolution.h ../ISolutionComp.h ../Exchange.h ../ExchComp.h \ + ../PPassemblage.h ../PPassemblageComp.h ../cxxKinetics.h \ + ../KineticsComp.h ../SSassemblage.h ../SS.h ../SScomp.h ../GasPhase.h \ + ../GasComp.h ../Reaction.h ../Temperature.h ../phqalloc.h +Serializer.o: ../Serializer.cxx ../Serializer.h ../common/PHRQ_base.h \ + ../Dictionary.h ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../NumKeyword.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h \ + ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../common/Utils.h ../common/phrqtype.h \ + ../Solution.h ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h \ + ../Exchange.h ../ExchComp.h ../Temperature.h ../GasPhase.h ../GasComp.h \ + ../cxxKinetics.h ../KineticsComp.h ../PPassemblage.h \ + ../PPassemblageComp.h ../SSassemblage.h ../SS.h ../SScomp.h +Solution.o: ../Solution.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../Solution.h ../SolutionIsotope.h \ + ../ISolution.h ../ISolutionComp.h ../phqalloc.h ../Dictionary.h +SolutionIsotope.o: ../SolutionIsotope.cxx ../common/Utils.h \ + ../common/phrqtype.h ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../SolutionIsotope.h ../phqalloc.h \ + ../Dictionary.h +SSassemblage.o: ../SSassemblage.cxx ../common/Utils.h \ + ../common/phrqtype.h ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../SSassemblage.h ../SS.h ../SScomp.h \ + ../phqalloc.h ../Dictionary.h +SScomp.o: ../SScomp.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../SScomp.h ../phqalloc.h ../Dictionary.h +SS.o: ../SS.cxx ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../common/Utils.h ../common/phrqtype.h \ + ../SS.h ../SScomp.h ../Dictionary.h ../phqalloc.h +StorageBin.o: ../StorageBin.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../StorageBin.h ../System.h \ + ../SSassemblage.h ../SS.h ../SScomp.h ../Solution.h ../SolutionIsotope.h \ + ../ISolution.h ../ISolutionComp.h ../Exchange.h ../ExchComp.h \ + ../GasPhase.h ../GasComp.h ../cxxKinetics.h ../KineticsComp.h \ + ../PPassemblage.h ../PPassemblageComp.h ../Reaction.h ../Temperature.h \ + ../phqalloc.h +SurfaceCharge.o: ../SurfaceCharge.cxx ../common/Utils.h \ + ../common/phrqtype.h ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Dictionary.h +SurfaceComp.o: ../SurfaceComp.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Dictionary.h +Surface.o: ../Surface.cxx ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h +System.o: ../System.cxx ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../System.h ../SSassemblage.h ../SS.h \ + ../SScomp.h ../Solution.h ../SolutionIsotope.h ../ISolution.h \ + ../ISolutionComp.h ../Exchange.h ../ExchComp.h ../GasPhase.h \ + ../GasComp.h ../cxxKinetics.h ../KineticsComp.h ../PPassemblage.h \ + ../PPassemblageComp.h ../Reaction.h ../Temperature.h +Temperature.o: ../Temperature.cxx ../common/Utils.h ../common/phrqtype.h \ + ../common/Parser.h ../common/PHRQ_base.h ../PhreeqcKeywords/Keywords.h \ + ../common/PHRQ_io.h ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../SelectedOutput.h ../NumKeyword.h ../UserPunch.h \ + ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h ../SurfaceComp.h \ + ../NameDouble.h ../SurfaceCharge.h ../global_structures.h ../NA.h \ + ../Temperature.h ../phqalloc.h +advection.o: ../advection.cpp ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../cxxKinetics.h \ + ../KineticsComp.h ../Solution.h ../SolutionIsotope.h ../ISolution.h \ + ../ISolutionComp.h +basicsubs.o: ../basicsubs.cpp ../Phreeqc.h ../common/phrqtype.h \ + ../cvdense.h ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h \ + ../smalldense.h ../runner.h ../StorageBinList.h ../common/PHRQ_base.h \ + ../dumper.h ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h \ + ../SelectedOutput.h ../NumKeyword.h ../UserPunch.h ../Pressure.h \ + ../cxxMix.h ../Use.h ../Surface.h ../SurfaceComp.h ../NameDouble.h \ + ../common/Parser.h ../common/PHRQ_base.h ../common/PHRQ_io.h \ + ../SurfaceCharge.h ../global_structures.h ../NA.h ../phqalloc.h \ + ../common/Utils.h ../common/phrqtype.h ../PBasic.h ../Exchange.h \ + ../ExchComp.h ../GasPhase.h ../GasComp.h ../PPassemblage.h \ + ../PPassemblageComp.h ../SSassemblage.h ../SS.h ../SScomp.h \ + ../cxxKinetics.h ../KineticsComp.h ../Solution.h ../SolutionIsotope.h \ + ../ISolution.h ../ISolutionComp.h +ChartHandler.o: ../ChartHandler.cpp +ChartObject.o: ../ChartObject.cpp +cl1.o: ../cl1.cpp ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h +cl1mp.o: ../cl1mp.cpp +class_main.o: ../class_main.cpp ../Phreeqc.h ../common/phrqtype.h \ + ../cvdense.h ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h \ + ../smalldense.h ../runner.h ../StorageBinList.h ../common/PHRQ_base.h \ + ../dumper.h ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h \ + ../SelectedOutput.h ../NumKeyword.h ../UserPunch.h ../Pressure.h \ + ../cxxMix.h ../Use.h ../Surface.h ../SurfaceComp.h ../NameDouble.h \ + ../common/Parser.h ../common/PHRQ_base.h ../common/PHRQ_io.h \ + ../SurfaceCharge.h ../global_structures.h ../NA.h ../Solution.h \ + ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h ../Reaction.h \ + ../PPassemblage.h ../PPassemblageComp.h ../Exchange.h ../ExchComp.h \ + ../GasPhase.h ../GasComp.h ../SSassemblage.h ../SS.h ../SScomp.h \ + ../cxxKinetics.h ../KineticsComp.h +CurveObject.o: ../CurveObject.cpp +cvdense.o: ../cvdense.cpp ../cvdense.h ../cvode.h ../sundialstypes.h \ + ../common/phrqtype.h ../nvector.h ../dense.h ../smalldense.h \ + ../sundialsmath.h ../Phreeqc.h ../runner.h ../StorageBinList.h \ + ../common/PHRQ_base.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../NumKeyword.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h \ + ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h +cvode.o: ../cvode.cpp ../nvector_serial.h ../nvector.h ../sundialstypes.h \ + ../common/phrqtype.h ../cvode.h ../sundialsmath.h ../Phreeqc.h \ + ../cvdense.h ../dense.h ../smalldense.h ../runner.h ../StorageBinList.h \ + ../common/PHRQ_base.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../NumKeyword.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h \ + ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h +dense.o: ../dense.cpp ../sundialstypes.h ../common/phrqtype.h \ + ../sundialsmath.h ../dense.h ../smalldense.h +Dictionary.o: ../Dictionary.cpp ../Dictionary.h +dumper.o: ../dumper.cpp ../dumper.h ../StorageBinList.h \ + ../common/PHRQ_base.h ../common/Parser.h ../common/PHRQ_base.h \ + ../PhreeqcKeywords/Keywords.h ../common/PHRQ_io.h ../common/PHRQ_io.h +gases.o: ../gases.cpp ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../GasPhase.h ../GasComp.h +input.o: ../input.cpp ../common/Utils.h ../common/phrqtype.h ../Phreeqc.h \ + ../common/phrqtype.h ../cvdense.h ../cvode.h ../sundialstypes.h \ + ../nvector.h ../dense.h ../smalldense.h ../runner.h ../StorageBinList.h \ + ../common/PHRQ_base.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../NumKeyword.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h \ + ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h +integrate.o: ../integrate.cpp ../Phreeqc.h ../common/phrqtype.h \ + ../cvdense.h ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h \ + ../smalldense.h ../runner.h ../StorageBinList.h ../common/PHRQ_base.h \ + ../dumper.h ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h \ + ../SelectedOutput.h ../NumKeyword.h ../UserPunch.h ../Pressure.h \ + ../cxxMix.h ../Use.h ../Surface.h ../SurfaceComp.h ../NameDouble.h \ + ../common/Parser.h ../common/PHRQ_base.h ../common/PHRQ_io.h \ + ../SurfaceCharge.h ../global_structures.h ../NA.h ../phqalloc.h \ + ../common/Utils.h ../common/phrqtype.h ../Solution.h \ + ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +inverse.o: ../inverse.cpp ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../common/Utils.h \ + ../common/phrqtype.h ../Solution.h ../SolutionIsotope.h ../ISolution.h \ + ../ISolutionComp.h +isotopes.o: ../isotopes.cpp ../Phreeqc.h ../common/phrqtype.h \ + ../cvdense.h ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h \ + ../smalldense.h ../runner.h ../StorageBinList.h ../common/PHRQ_base.h \ + ../dumper.h ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h \ + ../SelectedOutput.h ../NumKeyword.h ../UserPunch.h ../Pressure.h \ + ../cxxMix.h ../Use.h ../Surface.h ../SurfaceComp.h ../NameDouble.h \ + ../common/Parser.h ../common/PHRQ_base.h ../common/PHRQ_io.h \ + ../SurfaceCharge.h ../global_structures.h ../NA.h ../phqalloc.h \ + ../Solution.h ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +kinetics.o: ../kinetics.cpp ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../StorageBin.h ../System.h \ + ../Reaction.h ../cxxKinetics.h ../KineticsComp.h ../Solution.h \ + ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h ../PPassemblage.h \ + ../PPassemblageComp.h ../Exchange.h ../ExchComp.h ../GasPhase.h \ + ../GasComp.h ../SSassemblage.h ../SS.h ../SScomp.h ../Temperature.h \ + ../nvector_serial.h +mainsubs.o: ../mainsubs.cpp ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../PBasic.h \ + ../Temperature.h ../Exchange.h ../ExchComp.h ../GasPhase.h ../GasComp.h \ + ../Reaction.h ../PPassemblage.h ../PPassemblageComp.h ../SSassemblage.h \ + ../SS.h ../SScomp.h ../cxxKinetics.h ../KineticsComp.h ../Solution.h \ + ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +model.o: ../model.cpp ../common/Utils.h ../common/phrqtype.h ../Phreeqc.h \ + ../common/phrqtype.h ../cvdense.h ../cvode.h ../sundialstypes.h \ + ../nvector.h ../dense.h ../smalldense.h ../runner.h ../StorageBinList.h \ + ../common/PHRQ_base.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../NumKeyword.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h \ + ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Exchange.h ../ExchComp.h \ + ../GasPhase.h ../GasComp.h ../PPassemblage.h ../PPassemblageComp.h \ + ../SSassemblage.h ../SS.h ../SScomp.h ../Solution.h ../SolutionIsotope.h \ + ../ISolution.h ../ISolutionComp.h +nvector.o: ../nvector.cpp ../nvector.h ../sundialstypes.h \ + ../common/phrqtype.h +nvector_serial.o: ../nvector_serial.cpp ../nvector_serial.h ../nvector.h \ + ../sundialstypes.h ../common/phrqtype.h ../sundialsmath.h +parse.o: ../parse.cpp ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h +PBasic.o: ../PBasic.cpp ../PBasic.h ../common/phrqtype.h \ + ../common/PHRQ_base.h ../global_structures.h ../Surface.h \ + ../NumKeyword.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../PhreeqcKeywords/Keywords.h ../common/PHRQ_io.h \ + ../SurfaceCharge.h ../NA.h ../Phreeqc.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../dumper.h ../common/PHRQ_io.h ../SelectedOutput.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../phqalloc.h \ + ../common/Utils.h ../common/phrqtype.h ../Solution.h \ + ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +phqalloc.o: ../phqalloc.cpp ../Phreeqc.h ../common/phrqtype.h \ + ../cvdense.h ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h \ + ../smalldense.h ../runner.h ../StorageBinList.h ../common/PHRQ_base.h \ + ../dumper.h ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h \ + ../SelectedOutput.h ../NumKeyword.h ../UserPunch.h ../Pressure.h \ + ../cxxMix.h ../Use.h ../Surface.h ../SurfaceComp.h ../NameDouble.h \ + ../common/Parser.h ../common/PHRQ_base.h ../common/PHRQ_io.h \ + ../SurfaceCharge.h ../global_structures.h ../NA.h +Phreeqc.o: ../Phreeqc.cpp ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../Solution.h ../SolutionIsotope.h \ + ../ISolution.h ../ISolutionComp.h ../Reaction.h ../PPassemblage.h \ + ../PPassemblageComp.h ../Exchange.h ../ExchComp.h ../GasPhase.h \ + ../GasComp.h ../SSassemblage.h ../SS.h ../SScomp.h ../cxxKinetics.h \ + ../KineticsComp.h ../phqalloc.h ../PBasic.h ../Temperature.h +PHRQ_io_output.o: ../PHRQ_io_output.cpp ../Phreeqc.h ../common/phrqtype.h \ + ../cvdense.h ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h \ + ../smalldense.h ../runner.h ../StorageBinList.h ../common/PHRQ_base.h \ + ../dumper.h ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h \ + ../SelectedOutput.h ../NumKeyword.h ../UserPunch.h ../Pressure.h \ + ../cxxMix.h ../Use.h ../Surface.h ../SurfaceComp.h ../NameDouble.h \ + ../common/Parser.h ../common/PHRQ_base.h ../common/PHRQ_io.h \ + ../SurfaceCharge.h ../global_structures.h ../NA.h ../phqalloc.h +pitzer.o: ../pitzer.cpp ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Exchange.h ../ExchComp.h \ + ../Solution.h ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +pitzer_structures.o: ../pitzer_structures.cpp ../Phreeqc.h \ + ../common/phrqtype.h ../cvdense.h ../cvode.h ../sundialstypes.h \ + ../nvector.h ../dense.h ../smalldense.h ../runner.h ../StorageBinList.h \ + ../common/PHRQ_base.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../NumKeyword.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h \ + ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h +prep.o: ../prep.cpp ../common/Utils.h ../common/phrqtype.h ../Phreeqc.h \ + ../common/phrqtype.h ../cvdense.h ../cvode.h ../sundialstypes.h \ + ../nvector.h ../dense.h ../smalldense.h ../runner.h ../StorageBinList.h \ + ../common/PHRQ_base.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../NumKeyword.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h \ + ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Exchange.h ../ExchComp.h \ + ../GasPhase.h ../GasComp.h ../PPassemblage.h ../PPassemblageComp.h \ + ../SSassemblage.h ../SS.h ../SScomp.h ../Solution.h ../SolutionIsotope.h \ + ../ISolution.h ../ISolutionComp.h ../cxxKinetics.h ../KineticsComp.h +print.o: ../print.cpp ../common/Utils.h ../common/phrqtype.h ../Phreeqc.h \ + ../common/phrqtype.h ../cvdense.h ../cvode.h ../sundialstypes.h \ + ../nvector.h ../dense.h ../smalldense.h ../runner.h ../StorageBinList.h \ + ../common/PHRQ_base.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../NumKeyword.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h \ + ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Temperature.h \ + ../Exchange.h ../ExchComp.h ../GasPhase.h ../GasComp.h ../Reaction.h \ + ../PPassemblage.h ../PPassemblageComp.h ../SSassemblage.h ../SS.h \ + ../SScomp.h ../cxxKinetics.h ../KineticsComp.h ../Solution.h \ + ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +read.o: ../read.cpp ../common/Utils.h ../common/phrqtype.h ../Phreeqc.h \ + ../common/phrqtype.h ../cvdense.h ../cvode.h ../sundialstypes.h \ + ../nvector.h ../dense.h ../smalldense.h ../runner.h ../StorageBinList.h \ + ../common/PHRQ_base.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../NumKeyword.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h \ + ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Temperature.h \ + ../Exchange.h ../ExchComp.h ../GasPhase.h ../GasComp.h ../Reaction.h \ + ../PPassemblage.h ../PPassemblageComp.h ../SSassemblage.h ../SS.h \ + ../SScomp.h ../cxxKinetics.h ../KineticsComp.h ../Solution.h \ + ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +readtr.o: ../readtr.cpp ../StorageBin.h ../System.h ../NameDouble.h \ + ../common/Parser.h ../common/PHRQ_base.h ../PhreeqcKeywords/Keywords.h \ + ../common/PHRQ_io.h ../common/phrqtype.h ../common/PHRQ_base.h \ + ../common/PHRQ_io.h ../SS.h ../SScomp.h ../Phreeqc.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../dumper.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../SurfaceCharge.h ../global_structures.h \ + ../NA.h ../phqalloc.h ../common/Utils.h ../common/phrqtype.h +runner.o: ../runner.cpp ../runner.h ../common/phrqtype.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../common/Parser.h \ + ../common/PHRQ_base.h ../PhreeqcKeywords/Keywords.h ../common/PHRQ_io.h \ + ../NA.h ../common/Utils.h ../common/phrqtype.h +SelectedOutput.o: ../SelectedOutput.cpp ../SelectedOutput.h \ + ../NumKeyword.h ../common/PHRQ_base.h +sit.o: ../sit.cpp ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Exchange.h ../ExchComp.h \ + ../Solution.h ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +smalldense.o: ../smalldense.cpp ../smalldense.h ../sundialstypes.h \ + ../common/phrqtype.h ../sundialsmath.h +spread.o: ../spread.cpp ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Solution.h \ + ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h ../common/Utils.h \ + ../common/phrqtype.h +step.o: ../step.cpp ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../common/Utils.h \ + ../common/phrqtype.h ../StorageBin.h ../System.h ../Solution.h \ + ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h ../PPassemblage.h \ + ../PPassemblageComp.h ../SSassemblage.h ../SS.h ../SScomp.h \ + ../Temperature.h ../Exchange.h ../ExchComp.h ../GasPhase.h ../GasComp.h \ + ../Reaction.h ../cxxKinetics.h ../KineticsComp.h +StorageBinList.o: ../StorageBinList.cpp ../StorageBinList.h \ + ../common/PHRQ_base.h ../common/Parser.h ../common/PHRQ_base.h \ + ../PhreeqcKeywords/Keywords.h ../common/PHRQ_io.h +structures.o: ../structures.cpp ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Temperature.h \ + ../Exchange.h ../ExchComp.h ../GasPhase.h ../GasComp.h ../Reaction.h \ + ../PPassemblage.h ../PPassemblageComp.h ../SSassemblage.h ../SS.h \ + ../SScomp.h ../cxxKinetics.h ../KineticsComp.h ../Solution.h \ + ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h ../StorageBin.h \ + ../System.h +sundialsmath.o: ../sundialsmath.cpp ../sundialsmath.h ../sundialstypes.h \ + ../common/phrqtype.h +tally.o: ../tally.cpp ../common/Utils.h ../common/phrqtype.h ../Phreeqc.h \ + ../common/phrqtype.h ../cvdense.h ../cvode.h ../sundialstypes.h \ + ../nvector.h ../dense.h ../smalldense.h ../runner.h ../StorageBinList.h \ + ../common/PHRQ_base.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../NumKeyword.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h \ + ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Temperature.h \ + ../Exchange.h ../ExchComp.h ../GasPhase.h ../GasComp.h ../Reaction.h \ + ../PPassemblage.h ../PPassemblageComp.h ../SSassemblage.h ../SS.h \ + ../SScomp.h ../cxxKinetics.h ../KineticsComp.h ../Solution.h \ + ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +tidy.o: ../tidy.cpp ../common/Utils.h ../common/phrqtype.h ../Phreeqc.h \ + ../common/phrqtype.h ../cvdense.h ../cvode.h ../sundialstypes.h \ + ../nvector.h ../dense.h ../smalldense.h ../runner.h ../StorageBinList.h \ + ../common/PHRQ_base.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../NumKeyword.h \ + ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h ../Surface.h \ + ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Exchange.h ../ExchComp.h \ + ../GasPhase.h ../GasComp.h ../PPassemblage.h ../PPassemblageComp.h \ + ../SSassemblage.h ../SS.h ../SScomp.h ../cxxKinetics.h ../KineticsComp.h \ + ../Solution.h ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +transport.o: ../transport.cpp ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Exchange.h ../ExchComp.h \ + ../GasPhase.h ../GasComp.h ../PPassemblage.h ../PPassemblageComp.h \ + ../SSassemblage.h ../SS.h ../SScomp.h ../cxxKinetics.h ../KineticsComp.h \ + ../Solution.h ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +Use.o: ../Use.cpp ../Use.h +UserPunch.o: ../UserPunch.cpp ../UserPunch.h ../NumKeyword.h \ + ../common/PHRQ_base.h ../Phreeqc.h ../common/phrqtype.h ../cvdense.h \ + ../cvode.h ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h \ + ../runner.h ../StorageBinList.h ../dumper.h ../common/PHRQ_io.h \ + ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h ../Pressure.h \ + ../cxxMix.h ../Use.h ../Surface.h ../SurfaceComp.h ../NameDouble.h \ + ../common/Parser.h ../common/PHRQ_base.h ../common/PHRQ_io.h \ + ../SurfaceCharge.h ../global_structures.h ../NA.h +utilities.o: ../utilities.cpp ../common/Utils.h ../common/phrqtype.h \ + ../Phreeqc.h ../common/phrqtype.h ../cvdense.h ../cvode.h \ + ../sundialstypes.h ../nvector.h ../dense.h ../smalldense.h ../runner.h \ + ../StorageBinList.h ../common/PHRQ_base.h ../dumper.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h ../SelectedOutput.h \ + ../NumKeyword.h ../UserPunch.h ../Pressure.h ../cxxMix.h ../Use.h \ + ../Surface.h ../SurfaceComp.h ../NameDouble.h ../common/Parser.h \ + ../common/PHRQ_base.h ../common/PHRQ_io.h ../SurfaceCharge.h \ + ../global_structures.h ../NA.h ../phqalloc.h ../Exchange.h ../ExchComp.h \ + ../Solution.h ../SolutionIsotope.h ../ISolution.h ../ISolutionComp.h +Keywords.o: ../PhreeqcKeywords/Keywords.cpp ../PhreeqcKeywords/Keywords.h +Parser.o: ../common/Parser.cxx ../common/Utils.h ../common/phrqtype.h \ + ../common/Parser.h ../common/PHRQ_base.h ../PhreeqcKeywords/Keywords.h \ + ../common/PHRQ_io.h +PHRQ_base.o: ../common/PHRQ_base.cxx ../common/PHRQ_base.h \ + ../common/PHRQ_io.h ../PhreeqcKeywords/Keywords.h +Utils.o: ../common/Utils.cxx ../common/Utils.h ../common/phrqtype.h \ + ../common/Parser.h ../common/PHRQ_base.h ../PhreeqcKeywords/Keywords.h \ + ../common/PHRQ_io.h + +# ----------------------------------------------------------------------------- +clean: + rm -rf Class_release Class_debug Class_release_64 Class_debug_64 + +dependencies: + mkdir -p $(CLASS_DEBUG_DIR) + cd $(CLASS_DEBUG_DIR); gcc -MM -I.. -I../PhreeqcKeywords -I../common ../*.cxx ../*.cpp ../PhreeqcKeywords/*.cpp ../common/*.cxx + +tester: +# cd ../mytest; make clean; make -k -j 1 $(SPOOL) make.out $(SPOOL2); make diff $(SPOOL) diff.out $(SPOOL2) + cd ../mytest $(CONCAT) make clean $(CONCAT) make -k -j 1 $(SPOOL) make.out $(SPOOL2) $(CONCAT) make diff $(SPOOL) diff.out $(SPOOL2) + cd ../examples $(CONCAT) make -f Makefile.old clean $(CONCAT) make -f Makefile.old -k -j 1 $(SPOOL) make.out $(SPOOL2) $(CONCAT) make -f Makefile.old diff $(SPOOL) diff.out $(SPOOL2) + svn status -q ../mytest + svn status -q ../examples + +#ld-option +# Usage: ldflags += $(call ld-option, -Wl$(comma)--hash-style=sysv) +comma=, +ld-option = $(shell if $(CC) $(1) \ + -nostdlib -o /dev/null -xc /dev/null \ + > /dev/null 2>&1 ; then echo "$(1)" ; else echo "$(2)"; fi) + +# ============================================================================= +# End of makefile. +# ============================================================================= diff --git a/phreeqcpp/NA.h b/phreeqcpp/NA.h new file mode 100644 index 00000000..7f1afa93 --- /dev/null +++ b/phreeqcpp/NA.h @@ -0,0 +1 @@ +#define NA -98.7654321 \ No newline at end of file diff --git a/phreeqcpp/NameDouble.cxx b/phreeqcpp/NameDouble.cxx new file mode 100644 index 00000000..5780cfc8 --- /dev/null +++ b/phreeqcpp/NameDouble.cxx @@ -0,0 +1,628 @@ +// NameDouble.cxx: implementation of the cxxNameDouble class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort +#include // std::sort +#include // std::cout std::cerr + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "NameDouble.h" +#include "Dictionary.h" +//#include "Dictionary.h" +#include "phqalloc.h" +#include "ISolutionComp.h" + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxNameDouble::cxxNameDouble() + // + // default constructor for cxxNameDouble + // +{ + this->type = ND_ELT_MOLES; +} + +cxxNameDouble::cxxNameDouble(struct elt_list *elt_list_ptr) + // + // constructor for cxxNameDouble from list of elt_list + // +{ + int i; + if (elt_list_ptr != NULL) + { + for (i = 0; elt_list_ptr[i].elt != NULL; i++) + { + (*this)[elt_list_ptr[i].elt->name] = elt_list_ptr[i].coef; + } + } + this->type = ND_ELT_MOLES; +} + +cxxNameDouble::cxxNameDouble(struct elt_list *elt_list_ptr, int count) + // + // constructor for cxxNameDouble from list of elt_list with known count + // +{ + int i; + if (elt_list_ptr != NULL) + { + for (i = 0; i < count; i++) + { + (*this)[elt_list_ptr[i].elt->name] = elt_list_ptr[i].coef; + } + } + this->type = ND_ELT_MOLES; +} + +cxxNameDouble::cxxNameDouble(const cxxNameDouble & old, LDBLE factor) + // + // constructor for cxxNameDouble from list of elt_list + // +{ + for (cxxNameDouble::const_iterator it = old.begin(); it != old.end(); + it++) + { + if (old.type == ND_ELT_MOLES) + { + if (it->second * factor > 0) + { + (*this)[(it->first)] = it->second * factor; + } + } + else + { + (*this)[(it->first)] = it->second * factor; + } + } + this->type = old.type; +} +cxxNameDouble::cxxNameDouble(std::map < std::string, cxxISolutionComp > &comps) + // + // constructor for cxxNameDouble from map of cxxISolutionComp + // +{ + std::map < std::string, cxxISolutionComp >::iterator it; + for (it = comps.begin(); it != comps.end(); it++) + { + (*this)[it->first] = it->second.Get_moles(); + } + this->type = ND_ELT_MOLES; +} +#ifdef SKIP +cxxNameDouble::cxxNameDouble(struct master_activity *ma, int count, + cxxNameDouble::ND_TYPE l_type) + // + // constructor for cxxNameDouble from list of elt_list + // +{ + int i; + for (i = 0; i < count; i++) + { + if (ma[i].description == NULL) + continue; + (*this)[ma[i].description] = ma[i].la; + } + this->type = l_type; +} +#endif +cxxNameDouble::cxxNameDouble(struct name_coef *nc, int count) + // + // constructor for cxxNameDouble from list of elt_list + // +{ + int i; + for (i = 0; i < count; i++) + { + if (nc[i].name == NULL) + continue; + + if ((*this).find(nc[i].name) == (*this).end()) + { + (*this)[nc[i].name] = nc[i].coef; + } + else + { + (*this)[nc[i].name] = (*this).find(nc[i].name)->second + nc[i].coef; + } + + } + this->type = ND_NAME_COEF; +} + +cxxNameDouble::~cxxNameDouble() +{ +} + +void +cxxNameDouble::dump_xml(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + std::string xmlElement, xmlAtt1, xmlAtt2; + + switch ((*this).type) + { + case cxxNameDouble::ND_SPECIES_LA: + xmlElement = "first << xmlAtt2 << it-> + second << "/>" << "\n"; + } +} + +void +cxxNameDouble::dump_raw(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + + for (const_iterator it = (*this).begin(); it != (*this).end(); it++) + { + s_oss << indent0; + if (it->first.size() < 29 - indent0.size()) + { + s_oss << Utilities::pad_right(it->first, 29 - indent0.size()) << it->second << "\n"; + } + else + { + s_oss << Utilities::pad_right(it->first, it->first.size() + indent0.size()) << " " << it->second << "\n"; + } + } +} + +CParser::STATUS_TYPE cxxNameDouble::read_raw(CParser & parser, + std::istream::pos_type & pos) +{ + std::string token; + LDBLE + d; + + CParser::TOKEN_TYPE j; + j = parser.copy_token(token, pos); + + if (j == CParser::TT_EMPTY) + return CParser::PARSER_OK; + + if (!(parser.get_iss() >> d)) + { + return CParser::PARSER_ERROR; + } + (*this)[token.c_str()] = d; + return CParser::PARSER_OK; +} + +void +cxxNameDouble::add_extensive(const cxxNameDouble & addee, LDBLE factor) +// +// Sums two name doubles, this + factor*nd2 +// +{ + if (factor == 0) + return; + for (cxxNameDouble::const_iterator it = addee.begin(); it != addee.end(); + it++) + { + cxxNameDouble::iterator current = (*this).find(it->first); + if (current != (*this).end()) + { + (*this)[it->first] = current->second + it->second * factor; + } + else + { + (*this)[it->first] = it->second * factor; + } + } +} +void +cxxNameDouble::add_intensive(const cxxNameDouble & addee, LDBLE f1, + LDBLE f2) +// +// Sums two name doubles, this*f1 + f2*nd2 +// +{ + assert(f1 >= 0 && f2 >= 0); + for (cxxNameDouble::const_iterator it = addee.begin(); it != addee.end(); + it++) + { + cxxNameDouble::iterator current = (*this).find(it->first); + if (current != (*this).end()) + { + (*this)[it->first] = f1 * current->second + f2 * it->second; + } + else + { + (*this)[it->first] = f2 * it->second; + } + } +} +void +cxxNameDouble::add_log_activities(const cxxNameDouble & addee, LDBLE f1, + LDBLE f2) +// +// Sums two name doubles, this*f1 + f2*nd2, assuming log values +// +{ + assert(f1 >= 0 && f2 >= 0); + for (cxxNameDouble::const_iterator it = addee.begin(); it != addee.end(); + it++) + { + cxxNameDouble::iterator current = (*this).find(it->first); + if (current != (*this).end()) + { + LDBLE a1 = pow((LDBLE) 10., current->second); + LDBLE a2 = pow((LDBLE) 10., it->second); + (*this)[it->first] = log10(f1 * a1 + f2 * a2); + } + else + { + (*this)[it->first] = it->second + log10(f2); + } + } +} +cxxNameDouble +cxxNameDouble::Simplify_redox(void) const +{ + cxxNameDouble const &nd = *this; + std::basic_string < char >::size_type indexCh; + cxxNameDouble new_totals; + new_totals.type = cxxNameDouble::ND_ELT_MOLES; + { + std::string current_ename; + std::string const *ename_ptr; + cxxNameDouble::const_iterator it; + + // make list of elements in new_totals + for (it = nd.begin(); it != nd.end(); ++it) + { + current_ename = it->first; + if (it->first.size() < 4) + { + ename_ptr = &(it->first); + } + else + { + indexCh = it->first.find("("); + if (indexCh != std::string::npos) + { + current_ename = it->first.substr(0, indexCh); + ename_ptr = &(current_ename); + } + else + { + ename_ptr = &(it->first); + } + } + if (current_ename == "H" || current_ename == "O" || current_ename == "Charge") + continue; + new_totals[*ename_ptr] = 0; + } + } + + // sum totals for elements + { + cxxNameDouble::const_iterator old_it = nd.begin(); + cxxNameDouble::iterator new_it = new_totals.begin(); + std::string old_ename; + std::string const *old_ename_ptr; + while (old_it != nd.end() && new_it != new_totals.end()) + { + if (old_it->first.size() < 4) + { + old_ename_ptr = &old_it->first; + } + else + { + indexCh = old_it->first.find("("); + if (indexCh != std::string::npos) + { + old_ename = old_it->first.substr(0, indexCh); + old_ename_ptr = &old_ename; + } + else + { + old_ename_ptr = &old_it->first; + } + } + int j = strcmp(new_it->first.c_str(), old_ename_ptr->c_str()); + if (j < 0) + { + new_it++; + } + else if (j == 0) + { + new_it->second += old_it->second; + old_it++; + } + else + { + old_it++; + } + } + } + return new_totals; +} +#ifdef SKIP +cxxNameDouble +cxxNameDouble::Simplify_redox(void) +{ + // remove individual redox states from totals + cxxNameDouble &nd = *this; + std::set list_of_elements; + cxxNameDouble::const_iterator it; + for (it = nd.begin(); it != nd.end(); ++it) + { + std::string current_ename(it->first); + std::basic_string < char >::size_type indexCh; + indexCh = current_ename.find("("); + if (indexCh != std::string::npos) + { + current_ename = current_ename.substr(0, indexCh); + } + if (current_ename == "H" || current_ename == "O" || current_ename == "Charge") + continue; + list_of_elements.insert(current_ename); + } + + cxxNameDouble new_totals; + new_totals.type = cxxNameDouble::ND_ELT_MOLES; + std::set::iterator nt_it = list_of_elements.begin(); + for( ; nt_it != list_of_elements.end(); nt_it++) + { + new_totals[(*nt_it).c_str()] = nd.Get_total_element((*nt_it).c_str()); + } + return new_totals; +} +#endif +void +cxxNameDouble::Multiply_activities_redox(std::string str, LDBLE f) +{ + // update original master_activities using just computed factors + cxxNameDouble::iterator it; + LDBLE lg_f = log10(f); + std::string redox_name = str; + redox_name.append("("); + + for (it = this->begin(); it != this->end(); it++) + { + if (str[0] > it->first[0]) continue; + if (it->first == str) + { + // Found exact match + it->second += lg_f; + } + else + { + // no exact match, current is element name, need to find all valences + if (strstr(it->first.c_str(), redox_name.c_str()) == it->first.c_str()) + { + it->second += lg_f; + } + } + if (str[0] < it->first[0]) break; + } +} +#ifdef SKIP +void +cxxNameDouble::Multiply_activities_redox(std::string str, LDBLE f) +{ + // update original master_activities using just computed factors + cxxNameDouble::iterator it; + LDBLE lg_f = log10(f); + std::string redox_name = str; + redox_name.append("("); + + for (it = this->begin(); it != this->end(); it++) + { + if (it->first == str) + { + // Found exact match + it->second += lg_f; + } + else + { + // no exact match, current is element name, need to find all valences + if (strstr(it->first.c_str(), redox_name.c_str()) == it->first.c_str()) + { + it->second += lg_f; + } + } + } +} +#endif +LDBLE +cxxNameDouble::Get_total_element(const char *string) const +{ + cxxNameDouble::const_iterator it; + LDBLE d = 0.0; + for (it = this->begin(); it != this->end(); ++it) + { + // C++ way to do it + std::string ename(string); + std::string current_ename(it->first); + std::basic_string < char >::size_type indexCh; + indexCh = current_ename.find("("); + if (indexCh != std::string::npos) + { + current_ename = current_ename.substr(0, indexCh); + } + if (current_ename == ename) + { + d += it->second; + } + } + return (d); +} +void +cxxNameDouble::add(const char *token, LDBLE total) +// +// add to total for a specified element +// +{ + char key[MAX_LENGTH]; + strcpy(key, token); + + cxxNameDouble::iterator current = (*this).find(key); + if (current != (*this).end()) + { + (*this)[key] = current->second + total; + } + else + { + (*this)[key] = total; + } +} +void +cxxNameDouble::multiply(LDBLE extensive) +{ +// +// Multiplies by extensive +// + + for (cxxNameDouble::iterator it = this->begin(); it != this->end(); it++) + { + it->second *= extensive; + } +} +void +cxxNameDouble::merge_redox(const cxxNameDouble & source) +// +// Merges source into this +// Accounts for possible conflicts between redox state and +// totals +// +{ + for (cxxNameDouble::const_iterator sit = source.begin(); sit != source.end(); sit++) + { + + std::string redox_name = sit->first; + std::string elt_name; + size_t pos = redox_name.find("("); + + bool redox; + if (pos != std::string::npos) + { + redox = true; + elt_name = redox_name.substr(0, pos - 1); + } + else + { + redox = false; + elt_name = redox_name; + } + if (redox) + { + // Remove elt_name, if present + if ((*this).find(elt_name) != (*this).end()) + { + (*this).erase((*this).find(elt_name)); + } + // Put in redox name + (*this)[redox_name] = sit->second; + } + else + { + std::string substring; + substring.append(elt_name); + substring.append("("); + + // Remove all redox + bool deleted = true; + while (deleted) + { + deleted = false; + cxxNameDouble::iterator current = (*this).begin(); + for ( ; current != (*this).end(); current++) + { + if (current->first.find(substring) == 0) + { + (*this).erase(current); + deleted = true; + break; + } + } + } + // Put in elt name + (*this)[elt_name] = sit->second; + } + } +} +struct DblCmp { + bool operator()(const std::pair &lhs, const std::pair &rhs) + { + return lhs.second > rhs.second; + } +}; +std::vector< std::pair > +cxxNameDouble::sort_second(void) +{ + std::vector< std::pair > myvec(this->begin(), this->end()); + std::sort(myvec.begin(), myvec.end(), DblCmp()); + + return myvec; +} +void +cxxNameDouble::Serialize(Dictionary &dictionary, std::vector < int >&ints, + std::vector < double >&doubles) +{ + ints.push_back((int) (*this).size()); + for (const_iterator it = (*this).begin(); it != (*this).end(); it++) + { + int n = dictionary.Find(it->first); + ints.push_back(n); + doubles.push_back(it->second); + } +} +void +cxxNameDouble::Deserialize(Dictionary &dictionary, std::vector &ints, std::vector &doubles, int &ii, int &dd) +{ + this->clear(); + int count = ints[ii++]; + for (int j = 0; j < count; j++) + { + int n = ints[ii++]; + assert(n >= 0); + std::string str = dictionary.GetWords()[n]; + if (str.size() != 0) + { + (*this)[str] = doubles[dd++]; + } + } +} diff --git a/phreeqcpp/NameDouble.h b/phreeqcpp/NameDouble.h new file mode 100644 index 00000000..7df7e53b --- /dev/null +++ b/phreeqcpp/NameDouble.h @@ -0,0 +1,71 @@ +#if !defined(NAMEDOUBLE_H_INCLUDED) +#define NAMEDOUBLE_H_INCLUDED + +#if defined(_WINDLL) +#define IPQ_DLL_EXPORT __declspec(dllexport) +#else +#define IPQ_DLL_EXPORT +#endif + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector +class Phreeqc; +#include "Parser.h" +#include "phrqtype.h" +class Dictionary; +class cxxISolutionComp; + +class IPQ_DLL_EXPORT cxxNameDouble:public + std::map < std::string, LDBLE > +{ + + public: + enum ND_TYPE + { + ND_ELT_MOLES = 1, + ND_SPECIES_LA = 2, + ND_SPECIES_GAMMA = 3, + ND_NAME_COEF = 4 + }; + + cxxNameDouble(); + cxxNameDouble(struct elt_list *); + cxxNameDouble(struct elt_list *, int count); + cxxNameDouble(std::map < std::string, cxxISolutionComp > &comps); + + cxxNameDouble(struct name_coef *nc, int count); + cxxNameDouble(const cxxNameDouble & old, LDBLE factor); + ~cxxNameDouble(); + + LDBLE Get_total_element(const char *string) const; + cxxNameDouble Simplify_redox(void) const; + void Multiply_activities_redox(std::string, LDBLE f); + + void dump_xml(std::ostream & s_oss, unsigned int indent) const; + void dump_raw(std::ostream & s_oss, unsigned int indent) const; + CParser::STATUS_TYPE read_raw(CParser & parser, std::istream::pos_type & pos); + void add_extensive(const cxxNameDouble & old, LDBLE factor); + void add_intensive(const cxxNameDouble & addee, LDBLE fthis, LDBLE f2); + void add_log_activities(const cxxNameDouble & addee, LDBLE fthis, LDBLE f2); + void add(const char *key, LDBLE total); + void multiply(LDBLE factor); + void merge_redox(const cxxNameDouble & source); + + std::vector< std::pair > sort_second(void); + + void + insert(const char *str, LDBLE d) + { + (*this)[str] = d; + } + void Serialize(Dictionary &dictionary, std::vector &ints, std::vector &doubles); + void Deserialize(Dictionary &dictionary, std::vector &ints, std::vector &doubles, int &ii, int &dd); + + enum ND_TYPE type; + +}; + +#endif // !defined(NAMEDOUBLE_H_INCLUDED) diff --git a/phreeqcpp/NumKeyword.cxx b/phreeqcpp/NumKeyword.cxx new file mode 100644 index 00000000..0dae9df6 --- /dev/null +++ b/phreeqcpp/NumKeyword.cxx @@ -0,0 +1,181 @@ +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +// NumKeyword.cxx: implementation of the cxxNumKeyword class. +// +////////////////////////////////////////////////////////////////////// + +#include // ::sscanf +#include "NumKeyword.h" +#include "Parser.h" +#include "Utils.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxNumKeyword::cxxNumKeyword(PHRQ_io *io) +{ + this->n_user = 1; + this->n_user_end = 1; + this->Set_io(io); +} + +cxxNumKeyword::~cxxNumKeyword() +{ +} + +void +cxxNumKeyword::dump_xml(std::ostream & os, unsigned int indent) const +{ + unsigned int i; + + for (i = 0; i < indent + 1; ++i) + os << " "; + os << "" << this->n_user << "" << "\n"; + + for (i = 0; i < indent + 1; ++i) + os << " "; + os << "" << this->n_user_end << "" << "\n"; + + for (i = 0; i < indent + 1; ++i) + os << " "; + os << "" << this->description << "" << "\n"; +} + +void +cxxNumKeyword::read_number_description(CParser & parser) +{ + std::string keyword; + std::istream::pos_type ptr; + + // skip keyword + parser.copy_token(keyword, ptr); + + // skip whitespace + while (::isspace(parser.get_iss().peek())) + parser.get_iss().ignore(); + + // read number + if (::isdigit(parser.get_iss().peek()) || parser.get_iss().peek() == '-') + { + parser.get_iss() >> this->n_user; + char ch = (char)parser.get_iss().peek(); + if (ch == '-') + { + parser.get_iss() >> ch; // eat '-' + parser.get_iss() >> this->n_user_end; + if (this->n_user_end < this->n_user) + { + this->n_user_end = this->n_user; + } + } + else + { + this->n_user_end = this->n_user; + } + } + else + { + this->n_user = this->n_user_end = 1; + } + + // skip whitespace + while (::isspace(parser.get_iss().peek())) + parser.get_iss().ignore(); + + // copy description + std::getline(parser.get_iss(), this->description); +} + + +void +cxxNumKeyword::read_number_description(std::istream & is) +{ + // KEYWORD [[1[-20]] [This is the description]] + + // eat keyword + std::string token; + is >> token; + + // skip whitespace + while (::isspace(is.peek())) + is.ignore(); + + if (::isdigit(is.peek())) + { + is >> this->n_user; + char ch = (char)is.peek(); + if (ch == '-') + { + is >> ch; // eat '-' + is >> this->n_user_end; + } + else + { + this->n_user_end = this->n_user; + } + } + else + { + this->n_user = this->n_user_end = 1; + } + + while (::isspace(is.peek())) + is.ignore(); + + std::getline(is, this->description); +} +void +cxxNumKeyword::read_number_description(const std::string & line_in) +{ + std::string keyword, token; + //std::istream::pos_type ptr; + + std::string line = line_in; + std::string::iterator b = line.begin(); + std::string::iterator e = line.end(); + // skip keyword + CParser::copy_token(keyword, b, e); + + // read number + if (CParser::copy_token(token, b, e) == CParser::TT_DIGIT) + { + if (token[0] == '-') + { + token = token.substr(1); + Utilities::replace("-", " ", token); + token = "-" + token; + } + else + { + Utilities::replace("-", " ", token); + } + int j = sscanf(token.c_str(), "%d%d", &this->n_user, &this->n_user_end); + if (j == 0) + { + this->n_user = this->n_user_end = 1; + } + else if (j == 1) + { + this->n_user_end = this->n_user; + } + if (this->n_user_end < this->n_user) + { + this->n_user_end = this->n_user; + } + } + else + { + this->n_user = this->n_user_end = 1; + } + + // skip whitespace + std::string::iterator ic; + this->description.clear(); + for (ic = b; ic != e; ic++) + { + this->description += *ic; + } + trim_left(this->description); +} diff --git a/phreeqcpp/NumKeyword.h b/phreeqcpp/NumKeyword.h new file mode 100644 index 00000000..f795c4e9 --- /dev/null +++ b/phreeqcpp/NumKeyword.h @@ -0,0 +1,67 @@ +#if !defined(NUMKEYWORD_H_INCLUDED) +#define NUMKEYWORD_H_INCLUDED + +#include // std::ostream +#include // std::string +#include "PHRQ_base.h" +class CParser; + +class cxxNumKeyword: public PHRQ_base +{ + public: + cxxNumKeyword(PHRQ_io *io=NULL); + virtual ~ cxxNumKeyword(); + const std::string &Get_description() const + { + return this->description; + } + void Set_description(std::string str) + { + this->description = str; + } + void Set_description(const char *str) + { + if (str != NULL) + this->description = str; + } + + int Get_n_user() const + { + return this->n_user; + } + void Set_n_user(int user) + { + this->n_user = user; + } + + int Get_n_user_end() const + { + return this->n_user_end; + } + void Set_n_user_end(int user_end) + { + this->n_user_end = user_end; + } + void Set_n_user_both(int user_end) + { + this->n_user = this->n_user_end = user_end; + } + + bool operator<(const cxxNumKeyword & key) const + { + return (this->n_user < key.n_user); + } + + virtual void dump_xml(std::ostream & os, unsigned int indent = 0) const; + + void read_number_description(CParser & parser); + void read_number_description(const std::string & line_in); + protected: + int n_user; + int n_user_end; + std::string description; + + protected: + void read_number_description(std::istream & is); +}; +#endif // !defined(NUMKEYWORD_H_INCLUDED) diff --git a/phreeqcpp/PBasic.cpp b/phreeqcpp/PBasic.cpp new file mode 100644 index 00000000..2cfcadc5 --- /dev/null +++ b/phreeqcpp/PBasic.cpp @@ -0,0 +1,7343 @@ +#if defined(WIN32) && !defined(__GNUC__) +#include +#if defined(PHREEQCI_GUI) +#include "../../resource.h" +#endif +#else +#include +#define _ASSERTE assert +#endif +#include +#include "PBasic.h" +#include "Phreeqc.h" +#include "phqalloc.h" +#include "NameDouble.h" +#include "Utils.h" +#include "Solution.h" + +/* Run-time library for PhreeqcPtr->use with "p2c", the Pascal to C translator */ + +/* "p2c" Copyright (C) 1989, 1990, 1991 Free Software Foundation. + * By Dave Gillespie, daveg@csvax.cs.caltech.edu. Version 1.20. + * This file may be copied, modified, etc. in any way. It is not restricted + * by the licence agreement accompanying p2c itself. + */ + +#define STOP 1 +#define CONTINUE 0 +#define Isspace(c) isspace(c) /* or "((c) == ' ')" if preferred */ +#define toklength 20 +typedef long chset[9]; + +/* Output from p2c, the Pascal-to-C translator */ +/* From input file "basic.p" */ + +PBasic::PBasic(Phreeqc * ptr, PHRQ_io *phrq_io) + : PHRQ_base(phrq_io) +{ + if (ptr == NULL) + { + error_msg("No Phreeqc instance in PBasic constructor\n", 1); + } + PhreeqcPtr = ptr; + inbuf = NULL; + linebase = NULL; + varbase = NULL; + loopbase = NULL; + curline = 0; + stmtline = NULL; + dataline = NULL; + stmttok = NULL; + datatok = NULL; + buf = NULL; + exitflag = false; + EXCP_LINE = 0; + P_escapecode = 0; + P_ioresult = 0; + parse_all = false; + phreeqci_gui = false; + parse_whole_program = true; +#if defined(PHREEQCI_GUI) + hInfiniteLoop = 0; + nIDErrPrompt = 0; +#else + nIDErrPrompt = (PBasic::IDErr)0; +#endif + nErrLineNumber = 0; + // Basic commands initialized at bottom of file +} +PBasic::~PBasic(void) +{ + +} + +int PBasic:: +basic_compile(char *commands, void **lnbase, void **vbase, void **lpbase) +{ /*main */ + int l; + char *ptr; + + P_escapecode = 0; + P_ioresult = 0; + inbuf = (char *) PhreeqcPtr->PHRQ_calloc(PhreeqcPtr->max_line, sizeof(char)); + if (inbuf == NULL) + PhreeqcPtr->malloc_error(); + linebase = NULL; + varbase = NULL; + loopbase = NULL; + exitflag = false; + ptr = commands; + do + { + try + { + ptr = commands; + do + { + if (sget_logical_line(&ptr, &l, inbuf) == EOF) + { + strcpy(inbuf, "bye"); + } + parseinput(&buf); + if (curline == 0) + { + stmtline = NULL; + stmttok = buf; + if (stmttok != NULL) + exec(); + disposetokens(&buf); + } + } + while (!(exitflag || P_eof())); + } + catch (const PBasicStop&) + { + if (P_escapecode != -20) + { + if (phreeqci_gui) + { + _ASSERTE(false); + } + else + { + char * error_string = PhreeqcPtr->sformatf("%d/%d", (int) P_escapecode, + (int) P_ioresult); + PhreeqcPtr->warning_msg(error_string); + } + } + else + { + if (phreeqci_gui) + { + _ASSERTE(false); + } + else + { + /* putchar('\n');*/ + output_msg("\n"); + } + } + } + catch (const PhreeqcStop&) + { + // clean up memory + disposetokens(&buf); + PhreeqcPtr->PHRQ_free(inbuf); + *lnbase = (void *) linebase; + *vbase = (void *) varbase; + *lpbase = (void *) loopbase; + throw; // rethrow + } + } + while (!(exitflag || P_eof())); + /* exit(EXIT_SUCCESS); */ + PhreeqcPtr->PHRQ_free(inbuf); + *lnbase = (void *) linebase; + *vbase = (void *) varbase; + *lpbase = (void *) loopbase; + return (P_escapecode); +} + +int PBasic:: +basic_renumber(char *commands, void **lnbase, void **vbase, void **lpbase) +{ /*main */ + int l, i; + char *ptr; + + P_escapecode = 0; + P_ioresult = 0; + inbuf = (char *) PhreeqcPtr->PHRQ_calloc(PhreeqcPtr->max_line, sizeof(char)); + if (inbuf == NULL) + PhreeqcPtr->malloc_error(); + linebase = NULL; + varbase = NULL; + loopbase = NULL; + exitflag = false; + ptr = commands; + do + { + try + { + i = 0; + ptr = commands; + do + { + if (sget_logical_line(&ptr, &l, inbuf) == EOF) + { + i++; + if (i == 1) + { + strcpy(inbuf, "renum"); + } + else if (i == 2) + { + strcpy(inbuf, "list"); + } + else if (i == 3) + { + strcpy(inbuf, "new"); + } + else if (i == 4) + { + strcpy(inbuf, "bye"); + } + } + parseinput(&buf); + if (curline == 0) + { + stmtline = NULL; + stmttok = buf; + if (stmttok != NULL) + exec(); + disposetokens(&buf); + } + } + while (!(exitflag || P_eof())); + } + catch (const PBasicStop&) + { + if (P_escapecode != -20) + { + char * error_string = PhreeqcPtr->sformatf( "%d/%d", (int) P_escapecode, + (int) P_ioresult); + PhreeqcPtr->warning_msg(error_string); + } + else + { + /* putchar('\n');*/ + output_msg("\n"); + } + } + } + while (!(exitflag || P_eof())); + /* exit(EXIT_SUCCESS); */ + PhreeqcPtr->PHRQ_free(inbuf); + *lnbase = (void *) linebase; + *vbase = (void *) varbase; + *lpbase = (void *) loopbase; + + return (P_escapecode); +} + +int PBasic:: +basic_run(char *commands, void *lnbase, void *vbase, void *lpbase) +{ /*main */ + int l; + char *ptr; + P_escapecode = 0; + P_ioresult = 0; + inbuf = (char *) PhreeqcPtr->PHRQ_calloc(PhreeqcPtr->max_line, sizeof(char)); + if (inbuf == NULL) + PhreeqcPtr->malloc_error(); + linebase = NULL; + varbase = NULL; + loopbase = NULL; + exitflag = false; + ptr = commands; + linebase = (linerec *) lnbase; + varbase = (varrec *) vbase; + loopbase = (looprec *) lpbase; + do + { + try + { + do + { + if (sget_logical_line(&ptr, &l, inbuf) == EOF) + { + strcpy(inbuf, "bye"); + } + parseinput(&buf); + if (curline == 0) + { + stmtline = NULL; + stmttok = buf; + if (stmttok != NULL) + exec(); + disposetokens(&buf); + } + } + while (!(exitflag || P_eof())); + } + catch (const PBasicStop&) + { + if (P_escapecode != -20) + { + if (phreeqci_gui) + { + _ASSERTE(FALSE); + } + else + { + char * error_string = PhreeqcPtr->sformatf( "%d/%d", (int) P_escapecode, + (int) P_ioresult); + PhreeqcPtr->warning_msg(error_string); + } + } + else + { + /* putchar('\n');*/ + output_msg("\n"); + } + } + } + while (!(exitflag || P_eof())); + + /* exit(EXIT_SUCCESS); */ + PhreeqcPtr->PHRQ_free(inbuf); + + // Cleanup after run + clearvars(); + clearloops(); + restoredata(); + + return (P_escapecode); +} + +int PBasic:: +basic_main(char *commands) +{ /*main */ + int l; + char *ptr; + + P_escapecode = 0; + P_ioresult = 0; + inbuf = (char *) PhreeqcPtr->PHRQ_calloc(PhreeqcPtr->max_line, sizeof(char)); + if (inbuf == NULL) + PhreeqcPtr->malloc_error(); + linebase = NULL; + varbase = NULL; + loopbase = NULL; +#ifdef SKIP + printf("Chipmunk BASIC 1.0\n\n"); +#endif + exitflag = false; + ptr = commands; + do + { + try + { + do + { + if (sget_logical_line(&ptr, &l, inbuf) == EOF) + { + strcpy(inbuf, "bye"); + } + parseinput(&buf); + if (curline == 0) + { + stmtline = NULL; + stmttok = buf; + if (stmttok != NULL) + exec(); + disposetokens(&buf); + } + } + while (!(exitflag || P_eof())); + } + catch (const PBasicStop&) + { + if (P_escapecode != -20) + { + char * error_string = PhreeqcPtr->sformatf( "%d/%d", (int) P_escapecode, + (int) P_ioresult); + PhreeqcPtr->warning_msg(error_string); + } + else + { + /* putchar('\n');*/ + output_msg("\n"); + } + } + } + while (!(exitflag || P_eof())); + return 1; +/* exit(EXIT_SUCCESS); */ +} + +/* ---------------------------------------------------------------------- */ +int PBasic:: +sget_logical_line(char **ptr, int *l, char *return_line) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads file fp until end of line, ";", or eof + * stores characters in line_save + * reallocs line_save and line if more space is needed + * + * returns: + * EOF on empty line on end of file or + * OK otherwise + * *l returns length of line + */ + int i; + char c; + i = 0; + if (**ptr == '\0') + return (EOF); + for (;;) + { + c = **ptr; + if (c == '\0') + break; + (*ptr)++; + if (c == ';' || c == '\n') + break; + return_line[i++] = c; + } + return_line[i] = '\0'; + *l = i; + return (1); +} +void PBasic:: +restoredata(void) +{ + dataline = NULL; + datatok = NULL; +} + +void PBasic:: +clearloops(void) +{ + looprec *l; + + while (loopbase != NULL) + { + l = loopbase->next; + PhreeqcPtr->PHRQ_free(loopbase); + loopbase = l; + } +} + +void PBasic:: +clearvar(varrec * v) +{ + if (v->numdims != 0) + { + if (v->stringvar == 0) + { + PhreeqcPtr->PHRQ_free(v->UU.U0.arr); + v->UU.U0.arr = NULL; + } + else + { + free_dim_stringvar(v); + } + } + else if (v->stringvar && v->UU.U1.sv != NULL) + { + PhreeqcPtr->PHRQ_free(v->UU.U1.sv); + } + v->numdims = 0; + if (v->stringvar) + { + v->UU.U1.sv = NULL; + v->UU.U1.sval = &v->UU.U1.sv; + } + else + { + v->UU.U0.rv = 0.0; + v->UU.U0.val = &v->UU.U0.rv; + } +} + +void PBasic:: +clearvars(void) +{ + varrec *v; + + v = varbase; + while (v != NULL) + { + clearvar(v); + v = v->next; + } +} + +char * PBasic:: +numtostr(char * Result, LDBLE n) +{ + char *l_s; + long i; + + l_s = (char *) PhreeqcPtr->PHRQ_calloc(PhreeqcPtr->max_line, sizeof(char)); + if (l_s == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + l_s[PhreeqcPtr->max_line - 1] = '\0'; +/* if ((n != 0 && fabs(n) < 1e-2) || fabs(n) >= 1e12) { */ + if (ceil(n) == floor(n)) + { + //if (PhreeqcPtr->current_selected_output != NULL && + // !PhreeqcPtr->current_selected_output->Get_high_precision()) + //{ + // sprintf(l_s, "%12.0f", (double) n); + //} + //else + //{ + // sprintf(l_s, "%20.0f", (double) n); + //} + bool temp_high_precision = (PhreeqcPtr->current_selected_output != NULL) ? + PhreeqcPtr->current_selected_output->Get_high_precision() : + PhreeqcPtr->high_precision; + if (!temp_high_precision) + { + sprintf(l_s, "%12.0f", (double) n); + } + else + { + sprintf(l_s, "%20.0f", (double) n); + } + } + else + { + bool temp_high_precision = (PhreeqcPtr->current_selected_output != NULL) ? + PhreeqcPtr->current_selected_output->Get_high_precision() : + PhreeqcPtr->high_precision; + if (!temp_high_precision) + { + sprintf(l_s, "%12.4e", (double) n); + } + else + { + sprintf(l_s, "%20.12e", (double) n); + } + } + i = (int) strlen(l_s) + 1; + l_s[i - 1] = '\0'; +/* p2c: basic.p, line 237: + * Note: Modification of string length may translate incorrectly [146] */ + strcpy(Result, l_s); + PhreeqcPtr->free_check_null(l_s); + return (Result); +/* } else { + if (PhreeqcPtr->punch.high_precision == FALSE) sprintf(l_s, "%30.10f", n); + else sprintf(l_s, "%30.12f", n); + i = strlen(l_s) + 1; + do { + i--; + } while (l_s[i - 1] == '0'); + if (l_s[i - 1] == '.') + i--; + l_s[i] = '\0'; + * p2c: basic.p, line 248: + * Note: Modification of string length may translate incorrectly [146] * + return strcpy(Result, strltrim(l_s)); + } */ +} + +void PBasic:: +parse(char * l_inbuf, tokenrec ** l_buf) +{ + long i, j, begin, len, m, lp, q; + char token[toklength + 1] = {0}; + tokenrec *t, *tptr; + varrec *v; + char ch; + char *ptr; + + tptr = NULL; + *l_buf = NULL; + i = 1; + lp = q = 0; + do + { + ch = ' '; + while (i <= (int) strlen(l_inbuf) && (ch == ' ' || ch == '\t')) + { + ch = l_inbuf[i - 1]; + i++; + } + if (ch != ' ') + { + t = (tokenrec *) PhreeqcPtr->PHRQ_calloc(1, sizeof(tokenrec)); + if (t == NULL) + PhreeqcPtr->malloc_error(); + if (tptr == NULL) + *l_buf = t; + else + tptr->next = t; + if (phreeqci_gui) + { + t->n_sz = 0; + t->sz_num = 0; + } + tptr = t; + t->next = NULL; + switch (ch) + { + + case '"': + case '\'': + q += 1; + t->kind = tokstr; + j = 0; + len = (int) strlen(l_inbuf); + begin = i; + while (i <= len && l_inbuf[i - 1] != ch) + { + ++j; + ++i; + } + if (l_inbuf[i - 1] == ch) q -= 1; + m = 256; + if (j + 1 > m) + m = j + 1; + t->UU.sp = (char *) PhreeqcPtr->PHRQ_calloc(m, sizeof(char)); + if (t->UU.sp == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + strncpy(t->UU.sp, l_inbuf + begin - 1, j); + t->UU.sp[j] = '\0'; +/* p2c: basic.p, line 415: + * Note: Modification of string length may translate incorrectly [146] */ + i++; + break; + + case '+': + t->kind = tokplus; + break; + + case '-': + t->kind = tokminus; + break; + + case '*': + t->kind = toktimes; + break; + + case '/': + t->kind = tokdiv; + break; + + case '^': + t->kind = tokup; + break; + + case '(': + case '[': + t->kind = toklp; + lp += 1; + break; + + case ')': + case ']': + t->kind = tokrp; + lp -= 1; + break; + + case ',': + t->kind = tokcomma; + break; + + case ';': + t->kind = toksemi; + break; + + case ':': + t->kind = tokcolon; + break; + + case '?': + t->kind = tokprint; + break; + + case '=': + t->kind = tokeq; + break; + + case '<': + if (i <= (int) strlen(l_inbuf) && l_inbuf[i - 1] == '=') + { + t->kind = tokle; + i++; + } + else if (i <= (int) strlen(l_inbuf) && l_inbuf[i - 1] == '>') + { + t->kind = tokne; + i++; + } + else + t->kind = toklt; + break; + + case '>': + if (i <= (int) strlen(l_inbuf) && l_inbuf[i - 1] == '=') + { + t->kind = tokge; + i++; + } + else + t->kind = tokgt; + break; + + default: + if (isalpha((int) ch)) + { + i--; + j = 0; + token[toklength] = '\0'; + while (i <= (int) strlen(l_inbuf) && + (l_inbuf[i - 1] == '$' || l_inbuf[i - 1] == '_' || + isalnum((int) l_inbuf[i - 1]))) + { + if (j < toklength) + { + j++; + token[j - 1] = l_inbuf[i - 1]; + } + i++; + } + token[j] = '\0'; +/* p2c: basic.p, line 309: + * Note: Modification of string length may translate incorrectly [146] */ + +/* + * Search hash list + */ + PhreeqcPtr->str_tolower(token); + std::map::const_iterator item; + item = command_tokens.find(token); + if (item != command_tokens.end()) + { + t->kind = item->second; + if (t->kind == tokrem) + { + m = (int) strlen(l_inbuf) + 1; + if (m < 256) + m = 256; + t->UU.sp = (char *) PhreeqcPtr->PHRQ_calloc(m, sizeof(char)); + if (t->UU.sp == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + sprintf(t->UU.sp, "%.*s", + (int) (strlen(l_inbuf) - i + 1), + l_inbuf + i - 1); + i = (int) strlen(l_inbuf) + 1; + } + } + else + { + t->kind = tokvar; + v = varbase; + while (v != NULL && strcmp(v->name, token)) + v = v->next; + if (v == NULL) + { + v = (varrec *) PhreeqcPtr->PHRQ_calloc(1, sizeof(varrec)); + if (v == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + v->UU.U0.arr = NULL; + v->next = varbase; + varbase = v; + strcpy(v->name, token); + v->numdims = 0; + if (token[strlen(token) - 1] == '$') + { + v->stringvar = true; + v->UU.U1.sv = NULL; + v->UU.U1.sval = &v->UU.U1.sv; + } + else + { + v->stringvar = false; + v->UU.U0.rv = 0.0; + v->UU.U0.val = &v->UU.U0.rv; + } + } + t->UU.vp = v; + } + } + else if (isdigit((int) ch) || ch == '.') + { + t->kind = toknum; + i--; + t->UU.num = strtod(&l_inbuf[i - 1], &ptr); + if (&l_inbuf[i - 1] == ptr) + { + /* + Note: the following causes an infinite loop: + X = ..9 + */ + t->kind = toksnerr; + t->UU.snch = ch; + i++; + break; + } +#if defined(PHREEQCI_GUI) + if (phreeqci_gui) + { + _ASSERTE(t->n_sz == 0); + _ASSERTE(t->sz_num == NULL); + t->n_sz = max(23, ptr - &inbuf[i - 1]); + t->sz_num = + (char *) PhreeqcPtr->PHRQ_calloc((t->n_sz + 1), sizeof(char)); + if (t->sz_num == NULL) + PhreeqcPtr->malloc_error(); + if (ptr > &inbuf[i - 1]) + { + strncpy(t->sz_num, &inbuf[i - 1], + (ptr - &inbuf[i - 1])); + t->sz_num[ptr - &inbuf[i - 1]] = '\0'; + } + else + { + t->sz_num[0] = '\0'; + } + } +#endif + i += (int) (ptr - &l_inbuf[i - 1]); + } + else + { + t->kind = toksnerr; + t->UU.snch = ch; + } + break; + } + } + } + while (i <= (int) strlen(l_inbuf)); + if (q) { + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + _ASSERTE(P_escapecode == 0); + nIDErrPrompt = IDS_ERR_MISSING_Q; + P_escapecode = -20; + return; + } + else + { + char * error_string = PhreeqcPtr->sformatf( " missing \" or \' in BASIC line\n %ld %s", curline, l_inbuf); + error_msg(error_string, STOP); + } + } + if (lp > 0) { + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + _ASSERTE(P_escapecode == 0); + nIDErrPrompt = IDS_ERR_MISSING_RP; + P_escapecode = -20; + return; + } + else + { + char * error_string = PhreeqcPtr->sformatf( " missing ) or ] in BASIC line\n %ld %s", curline, l_inbuf); + error_msg(error_string, STOP); + } + } + else if (lp < 0) { + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + _ASSERTE(P_escapecode == 0); + nIDErrPrompt = IDS_ERR_MISSING_RP; + P_escapecode = -20; + return; + } + else + { + char * error_string = PhreeqcPtr->sformatf( " missing ( or [ in BASIC line\n %ld %s", curline, l_inbuf); + error_msg(error_string, STOP); + } + } +} + +#undef toklength + +void PBasic:: +listtokens(FILE * f, tokenrec * l_buf) +{ + bool ltr; + char STR1[256] = {0}; + char *string; + ltr = false; + while (l_buf != NULL) + { + if ((l_buf->kind >= (long) toknot && l_buf->kind <= (long) tokrenum) || + l_buf->kind == (long) toknum || l_buf->kind == (long) tokvar || + l_buf->kind >= (long) toktc) + { + if (ltr) + /*putc(' ', f); */ + output_msg(" "); + ltr = (bool) (l_buf->kind != toknot); + } + else + ltr = false; + switch (l_buf->kind) + { + + case tokvar: + /*fputs(l_buf->UU.vp->name, f); */ + PhreeqcPtr->output_msg(PhreeqcPtr->sformatf("%s", l_buf->UU.vp->name)); + break; + + case toknum: + /*fputs(numtostr(STR1, l_buf->UU.num), f); */ + string = numtostr(STR1, l_buf->UU.num); + PhreeqcPtr->string_trim(string); + output_msg(PhreeqcPtr->sformatf("%s", string)); + break; + + case tokstr: + if (strchr(l_buf->UU.sp, '\"')) + { + output_msg(PhreeqcPtr->sformatf("\'%s\'", l_buf->UU.sp)); + } + else + { + output_msg(PhreeqcPtr->sformatf("\"%s\"", l_buf->UU.sp)); + } + break; + + case toksnerr: + output_msg(PhreeqcPtr->sformatf("{%c}", l_buf->UU.snch)); + break; + + case tokplus: + /*putc('+', f); */ + output_msg("+"); + break; + + case tokminus: + /*putc('-', f); */ + output_msg("-"); + break; + + case toktimes: + /*putc('*', f); */ + output_msg("*"); + break; + + case tokdiv: + /*putc('/', f); */ + output_msg("/"); + break; + + case tokup: + /*putc('^', f); */ + output_msg("^"); + break; + + case toklp: + /*putc('(', f); */ + output_msg("("); + break; + + case tokrp: + /*putc(')', f); */ + output_msg(")"); + break; + + case tokcomma: + /*putc(',', f); */ + output_msg(","); + break; + + case toksemi: + /*putc(';', f); */ + output_msg(";"); + break; + + case tokcolon: + output_msg(" : "); + break; + + case tokeq: + output_msg(" = "); + break; + + case toklt: + output_msg(" < "); + break; + + case tokgt: + output_msg(" > "); + break; + + case tokle: + output_msg(" <= "); + break; + + case tokge: + output_msg(" >= "); + break; + + case tokne: + output_msg(" <> "); + break; + + case tokand: + output_msg(" AND "); + break; + + case tokor: + output_msg(" OR "); + break; + + case tokxor: + output_msg(" XOR "); + break; + + case tokmod: + output_msg(" MOD "); + break; + + case toknot: + output_msg("NOT "); + break; + + case toksqr: + output_msg("SQR"); + break; + + case toksqrt: + output_msg("SQRT"); + break; + + case tokceil: + output_msg("CEIL"); + break; + + case tokfloor: + output_msg("FLOOR"); + break; + + case toksin: + output_msg("SIN"); + break; + + case tokcos: + output_msg("COS"); + break; + + case toktan: + output_msg("TAN"); + break; + + case tokarctan: + output_msg("ARCTAN"); + break; + + case toklog: + output_msg("LOG"); + break; + + case tokexp: + output_msg("EXP"); + break; + + case tokabs: + output_msg("ABS"); + break; + + case toksgn: + output_msg("SGN"); + break; + + case tokstr_: + output_msg("STR$"); + break; + + case tokval: + output_msg("VAL"); + break; + + case tokchr_: + output_msg("CHR$"); + break; + + case tokeol_: + output_msg("EOL$"); + break; + + case tokasc: + output_msg("ASC"); + break; + + case toklen: + output_msg("LEN"); + break; + + case tokmid_: + output_msg("MID$"); + break; + + case tokpeek: + output_msg("PEEK"); + break; + + case tokrem: + output_msg(PhreeqcPtr->sformatf("REM%s", l_buf->UU.sp)); + break; + + case toklet: + output_msg("LET"); + break; + + case tokprint: + output_msg("PRINT"); + break; + + case tokinput: + output_msg("INPUT"); + break; + + case tokgoto: + output_msg("GOTO"); + break; + + case tokif: + output_msg("IF"); + break; + + case tokend: + output_msg("END"); + break; + + case tokstop: + output_msg("STOP"); + break; + + case tokfor: + output_msg("FOR"); + break; + + case toknext: + output_msg("NEXT"); + break; + + case tokwhile: + output_msg("WHILE"); + break; + + case tokwend: + output_msg("WEND"); + break; + + case tokgosub: + output_msg("GOSUB"); + break; + + case tokreturn: + output_msg("RETURN"); + break; + + case tokread: + output_msg("READ"); + break; + + case tokdata: + output_msg("DATA"); + break; + + case tokrestore: + output_msg("RESTORE"); + break; + + case tokgotoxy: + output_msg("GOTOXY"); + break; + + case tokon: + output_msg("ON"); + break; + + case tokdim: + output_msg("DIM"); + break; + + case tokerase: + output_msg("ERASE"); + break; + + case tokpoke: + output_msg("POKE"); + break; + + case toklist: + output_msg("LIST"); + break; + + case tokrun: + output_msg("RUN"); + break; + + case toknew: + output_msg("NEW"); + break; + + case tokload: + output_msg("LOAD"); + break; + + case tokmerge: + output_msg("MERGE"); + break; + + case toksave: + output_msg("SAVE"); + break; + + case tokbye: + output_msg("BYE"); + break; + + case tokdel: + output_msg("DEL"); + break; + + case tokrenum: + output_msg("RENUM"); + break; + + case tokthen: + output_msg(" THEN "); + break; + + case tokelse: + output_msg(" ELSE "); + break; + + case tokto: + output_msg(" TO "); + break; + + case tokstep: + output_msg(" STEP "); + break; + + case toktc: + output_msg("TC"); + break; + + case tokm0: + output_msg("M0"); + break; + + case tokm: + output_msg("M"); + break; + + case tokparm: + output_msg("PARM"); + break; + + case tokact: + output_msg("ACT"); + break; + + case tokchange_por: + output_msg("CHANGE_POR"); + break; + + case tokget_por: + output_msg("GET_POR"); + break; + + case tokchange_surf: + output_msg("CHANGE_SURF"); + break; + + case tokporevolume: + output_msg("POREVOLUME"); + break; + + case tokmol: + output_msg("MOL"); + break; + + case tokla: + output_msg("LA"); + break; + + case toklm: + output_msg("LM"); + break; + + case toksr: + output_msg("SR"); + break; + + case toksi: + output_msg("SI"); + break; + + case toktot: + output_msg("TOT"); + break; + + case toktotmole: + case toktotmol: + case toktotmoles: + output_msg("TOTMOLE"); + break; + + case toktk: + output_msg("TK"); + break; + + case toktime: + output_msg("TIME"); + break; + + case toklog10: + output_msg("LOG10"); + break; + + case toksim_time: + output_msg("SIM_TIME"); + break; + + case tokequi: + output_msg("EQUI"); + break; + + case tokequi_delta: + output_msg("EQUI_DELTA"); + break; + + case tokgas: + output_msg("GAS"); + break; + + case tokpunch: + output_msg("PUNCH"); + break; + + case tokkin: + output_msg("KIN"); + break; + + case tokkin_delta: + output_msg("KIN_DELTA"); + break; + + case tokkin_time: + output_msg("KIN_TIME"); + break; + + case toks_s: + output_msg("S_S"); + break; + + case tokmu: + output_msg("MU"); + break; + + case tokosmotic: + output_msg("OSMOTIC"); + break; + + case tokalk: + output_msg("ALK"); + break; + + case toklk_species: + output_msg("LK_SPECIES"); + break; + + case toklk_named: + output_msg("LK_NAMED"); + break; + + case toklk_phase: + output_msg("LK_PHASE"); + break; + + case toksum_species: + output_msg("SUM_SPECIES"); + break; + + case toksum_gas: + output_msg("SUM_GAS"); + break; + + case toksum_s_s: + output_msg("SUM_s_s"); + break; + + case tokcalc_value: + output_msg("CALC_VALUE"); + break; + + case tokdescription: + output_msg("DESCRIPTION"); + break; + + case toktitle: + output_msg("TITLE"); + break; + + case toksys: + output_msg("SYS"); + break; + + case tokinstr: + output_msg("INSTR"); + break; + + case tokltrim: + output_msg("LTRIM"); + break; + + case tokrtrim: + output_msg("RTRIM"); + break; + + case toktrim: + output_msg("TRIM"); + break; + + case tokpad: + output_msg("PAD"); + break; + + case tokrxn: + output_msg("RXN"); + break; + + case tokdist: + output_msg("DIST"); + break; + + case tokmisc1: + output_msg("MISC1"); + break; + + case tokmisc2: + output_msg("MISC2"); + break; + + case tokedl: + output_msg("EDL"); + break; + + case toksurf: + output_msg("SURF"); + break; + + case tokedl_species: + output_msg("EDL_SPECIES"); + break; + + case tokstep_no: + output_msg("STEP_NO"); + break; + + case toksim_no: + output_msg("SIM_NO"); + break; + + case toktotal_time: + output_msg("TOTAL_TIME"); + break; + + case tokput: + output_msg("PUT"); + break; + + case tokget: + output_msg("GET"); + break; + + case tokcharge_balance: + output_msg("CHARGE_BALANCE"); + break; + + case tokpercent_error: + output_msg("PERCENT_ERROR"); + break; + +#if defined PHREEQ98 || defined MULTICHART + case tokgraph_x: + output_msg("GRAPH_X"); + break; + + case tokgraph_y: + output_msg("GRAPH_Y"); + break; + + case tokgraph_sy: + output_msg("GRAPH_SY"); + break; +#endif + +#if defined MULTICHART + case tokplot_xy: + output_msg("PLOT_XY"); + break; +#endif + + case tokcell_no: + output_msg("CELL_NO"); + break; + + case tokexists: + output_msg("EXISTS"); + break; + + case toksc: + output_msg("SC"); + break; + + case tokgamma: + output_msg("GAMMA"); + break; + + case toklg: + output_msg("LG"); + break; + +/* VP: Density Start */ + case tokrho: + output_msg("RHO"); + break; + case tokrho_0: + output_msg("RHO_0"); + break; +/* VP: Density End */ + case tokcell_volume: + output_msg("CELL_VOLUME"); + break; + case tokcell_pore_volume: + output_msg("CELL_PORE_VOLUME"); + break; + case tokcell_porosity: + output_msg("CELL_POROSITY"); + break; + case tokcell_saturation: + output_msg("CELL_SATURATION"); + break; + case tokiso: + output_msg("ISO"); + break; + case tokiso_unit: + output_msg("ISO_UNIT"); + break; + case tokkinetics_formula: + case tokkinetics_formula_: + output_msg("KINETICS_FORMULA$"); + break; + case tokphase_formula: + case tokphase_formula_: + output_msg("PHASE_FORMULA$"); + break; + case tokspecies_formula: + case tokspecies_formula_: + output_msg("SPECIES_FORMULA$"); + break; + case toklist_s_s: + output_msg("LIST_S_S"); + break; + case tokpr_p: + output_msg("PR_P"); + break; + case tokpr_phi: + output_msg("PR_PHI"); + break; + case tokgas_p: + output_msg("GAS_P"); + break; + case tokgas_vm: + output_msg("GAS_VM"); + break; + case tokpressure: + output_msg("PRESSURE"); + break; + case tokeps_r: + output_msg("EPS_R"); // dielectric constant + break; + case tokvm: + output_msg("VM"); // mole volume of aqueous solute + break; + case tokphase_vm: + output_msg("PHASE_VM"); // mole volume of a phase + break; + case tokaphi: + output_msg("APHI"); // mole volume of a phase + break; + case tokdh_a: + output_msg("DH_A"); // Debye-Hueckel A + break; + case tokdh_b: + output_msg("DH_B"); // Debye-Hueckel B + break; + case tokdh_av: + output_msg("DH_Av"); // Debye-Hueckel Av + break; + case tokqbrn: + output_msg("QBrn"); // Q_Born, d(eps_r)/d(P)/(eps_r^2) + break; + case tokkappa: + output_msg("KAPPA"); // compressibility of pure water, d(rho)/d(P) / rho + break; + case tokgfw: + output_msg("GFW"); // gram formula weight of a formula + break; + case toksoln_vol: + output_msg("SOLN_VOL"); // volume of solution + break; + case tokstr_f_: + output_msg("STR_F$"); + break; + case tokstr_e_: + output_msg("STR_E$"); + break; + case tokeq_frac: + case tokequiv_frac: + output_msg("EQ_FRAC"); + break; + case tokcallback: + output_msg("CALLBACK"); + break; + case tokdiff_c: + output_msg("DIFF_C"); + break; + case toksetdiff_c: + output_msg("SETDIFF_C"); + break; + case toksa_declercq: + output_msg("SA_DECLERCQ"); + break; + case tokviscos: + output_msg("VISCOS"); + break; + case tokviscos_0: + output_msg("VISCOS_0"); + break; + case tokcurrent_a: + output_msg("CURRENT_A"); + break; + case tokpot_v: + output_msg("POT_V"); + break; + case tokt_sc: + output_msg("T_SC"); + break; + } + l_buf = l_buf->next; + } +} + +void PBasic:: +disposetokens(tokenrec ** tok) +{ + tokenrec *tok1; + + while (*tok != NULL) + { + tok1 = (*tok)->next; + if (phreeqci_gui) + { + if ((*tok)->kind == (long) toknum) + { + PhreeqcPtr->PHRQ_free((*tok)->sz_num); + } +#ifdef _DEBUG + else + { + _ASSERTE((*tok)->sz_num == NULL); + } +#endif /* _DEBUG */ + } + if ((*tok)->kind == (long) tokrem || (*tok)->kind == (long) tokstr) + { + (*tok)->UU.sp = (char *) PhreeqcPtr->free_check_null((*tok)->UU.sp); + } + *tok = (tokenrec *) PhreeqcPtr->free_check_null(*tok); + *tok = tok1; + } +} + +void PBasic:: +parseinput(tokenrec ** l_buf) +{ + linerec *l, *l0, *l1; + + while (PhreeqcPtr->replace("\t", " ", inbuf)); + while (PhreeqcPtr->replace("\r", " ", inbuf)); + PhreeqcPtr->string_trim(inbuf); + curline = 0; + while (*inbuf != '\0' && isdigit((int) inbuf[0])) + { + curline = curline * 10 + inbuf[0] - 48; + memmove(inbuf, inbuf + 1, strlen(inbuf)); + } + parse(inbuf, l_buf); + if (curline == 0) + return; + l = linebase; + l0 = NULL; + while (l != NULL && l->num < curline) + { + l0 = l; + l = l->next; + } + if (l != NULL && l->num == curline) + { + l1 = l; + l = l->next; + if (l0 == NULL) + linebase = l; + else + l0->next = l; + disposetokens(&l1->txt); + PhreeqcPtr->PHRQ_free(l1); + } + if (*l_buf != NULL) + { + l1 = (linerec *) PhreeqcPtr->PHRQ_calloc(1, sizeof(linerec)); + if (l1 == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + l1->next = l; + if (l0 == NULL) + linebase = l1; + else + l0->next = l1; + l1->num = curline; + l1->txt = *l_buf; + strncpy(l1->inbuf, inbuf, MAX_LINE); + l1->inbuf[MAX_LINE-1] = '\0'; + } + clearloops(); + restoredata(); +} + +void PBasic:: +errormsg(const char * l_s) +{ + if (phreeqci_gui) + { + /* set nIDErrPrompt before calling errormsg see snerr */ + _ASSERTE(nIDErrPrompt != 0); + } + else + { + error_msg(l_s, CONTINUE); + } + _Escape(42); +} + +void PBasic:: + snerr(const char * l_s) +{ + char str[MAX_LENGTH] = {0}; + strcpy(str, "Syntax_error "); + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_SYNTAX; + } + errormsg(strcat(str, l_s)); +} + +void PBasic:: + tmerr(const char * l_s) +{ + char str[MAX_LENGTH] = {0}; + strcpy(str, "Type mismatch error"); + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_MISMATCH; + } + errormsg(strcat(str, l_s)); +} + +void PBasic:: + badsubscr(void) +{ + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_BAD_SUBSCRIPT; + } + errormsg("Bad subscript"); +} + +LDBLE PBasic:: +realfactor(struct LOC_exec *LINK) +{ + valrec n; + n = factor(LINK); + if (n.stringval) + tmerr(": found characters, not a number"); + return (n.UU.val); +} + +char * PBasic:: +strfactor(struct LOC_exec * LINK) +{ + valrec n; + n = factor(LINK); + if (!n.stringval) + //tmerr(": chemical name is not enclosed in \" \"" ); + tmerr(": Expected quoted string or character variable." ); + return (n.UU.sval); +} + +char * PBasic:: +stringfactor(char * Result, struct LOC_exec * LINK) +{ + valrec n; + + n = factor(LINK); + if (!n.stringval) + //tmerr(": chemical name is not enclosed in \" \"" ); + tmerr(": Expected quoted string or character variable." ); + strcpy(Result, n.UU.sval); + PhreeqcPtr->PHRQ_free(n.UU.sval); + return Result; +} + +const char * PBasic:: +stringfactor(std::string & Result, struct LOC_exec * LINK) +{ + valrec n; + + n = factor(LINK); + if (!n.stringval) + //tmerr(": chemical name is not enclosed in \" \"" ); + tmerr(": Expected quoted string or character variable." ); + Result = n.UU.sval; + PhreeqcPtr->PHRQ_free(n.UU.sval); + return Result.c_str(); +} + +long PBasic:: +intfactor(struct LOC_exec *LINK) +{ + return ((long) floor(realfactor(LINK) + 0.5)); +} + +LDBLE PBasic:: +realexpr(struct LOC_exec *LINK) +{ + valrec n; + + n = expr(LINK); + if (n.stringval) + tmerr(": found characters, not a number"); + return (n.UU.val); +} + +char * PBasic:: +strexpr(struct LOC_exec * LINK) +{ + valrec n; + + n = expr(LINK); + if (!n.stringval) + //tmerr(": chemical name is not enclosed in \" \"" ); + tmerr(": Expected quoted string or character variable." ); + return (n.UU.sval); +} + +char * PBasic:: +stringexpr(char * Result, struct LOC_exec * LINK) +{ + valrec n; + + n = expr(LINK); + if (!n.stringval) + //tmerr(": chemical name is not enclosed in \" \"" ); + tmerr(": Expected quoted string or character variable." ); + strcpy(Result, n.UU.sval); + PhreeqcPtr->PHRQ_free(n.UU.sval); + return Result; +} + +long PBasic:: +intexpr(struct LOC_exec *LINK) +{ + return ((long) floor(realexpr(LINK) + 0.5)); +} + +void PBasic:: +require(int k, struct LOC_exec *LINK) +{ + char str[MAX_LENGTH] = {0}; + if (LINK->t == NULL || LINK->t->kind != k) + { + std::map::const_iterator item; + for (item = command_tokens.begin(); item != command_tokens.end(); item++) + { + if (item->second == k) + break; + } + + if (item == command_tokens.end()) + snerr(": missing unknown command"); + else { + strcpy(str, ": missing "); + snerr(strcat(str, item->first.c_str())); + } +#if !defined(R_SO) + exit(4); +#endif + } + LINK->t = LINK->t->next; +} + + +void PBasic:: +skipparen(struct LOC_exec *LINK) +{ + do + { + if (LINK->t == NULL) + { + snerr(": parenthesis missing"); +#if !defined(R_SO) + exit(4); +#endif + } + if (LINK->t->kind == tokrp || LINK->t->kind == tokcomma) + goto _L1; + if (LINK->t->kind == toklp) + { + LINK->t = LINK->t->next; + skipparen(LINK); + } + LINK->t = LINK->t->next; + } + while (true); + _L1:; +} + +varrec * PBasic:: +findvar(struct LOC_exec *LINK) +{ + varrec *v; + long i, j, k; + tokenrec *tok; + long FORLIM; + + if (LINK->t == NULL || LINK->t->kind != tokvar) + { + snerr(": can`t find variable"); +#if !defined(R_SO) + exit(4); +#endif + } + v = LINK->t->UU.vp; + LINK->t = LINK->t->next; + if (LINK->t == NULL || LINK->t->kind != toklp) + { + if (v->numdims != 0) + badsubscr(); + return v; + } + if (v->numdims == 0) + { + tok = LINK->t; + i = 0; + j = 1; + do + { + if (i >= maxdims) + badsubscr(); + LINK->t = LINK->t->next; + skipparen(LINK); + j *= 11; + i++; + v->dims[i - 1] = 11; + } + while (LINK->t->kind != tokrp); + v->numdims = (char) i; + if (v->stringvar) + { + v->UU.U1.sarr = (char **) PhreeqcPtr->PHRQ_malloc(j * sizeof(char *)); + if (v->UU.U1.sarr == NULL) + PhreeqcPtr->malloc_error(); + for (k = 0; k < j; k++) + v->UU.U1.sarr[k] = NULL; + } + else + { + v->UU.U0.arr = (LDBLE *) PhreeqcPtr->PHRQ_malloc(j * sizeof(LDBLE)); + if (v->UU.U0.arr == NULL) + PhreeqcPtr->malloc_error(); + for (k = 0; k < j; k++) + v->UU.U0.arr[k] = 0.0; + } + LINK->t = tok; + } + k = 0; + LINK->t = LINK->t->next; + FORLIM = v->numdims; + for (i = 1; i <= FORLIM; i++) + { + j = intexpr(LINK); + if ((unsigned long) j >= (unsigned long) v->dims[i - 1]) + badsubscr(); + k = k * v->dims[i - 1] + j; + if (i < v->numdims) + require(tokcomma, LINK); + } + require(tokrp, LINK); + if (v->stringvar) + v->UU.U1.sval = &v->UU.U1.sarr[k]; + else + v->UU.U0.val = &v->UU.U0.arr[k]; + return v; +} + +valrec PBasic:: +factor(struct LOC_exec * LINK) +{ + char string[MAX_LENGTH] = {0}; + cxxSolution *soln_ptr; + varrec *v; + tokenrec *facttok; + valrec n; + + long i, j, m; + tokenrec *tok, *tok1; + char *l_s; + LDBLE l_dummy; + int i_rate; + union + { + long i; + char *c; + } trick; + struct save_values s_v, *s_v_ptr; + int k; + LDBLE TEMP; + std::string STR1, STR2; + const char *elt_name, *surface_name, *mytemplate, *name; + varrec *count_varrec = NULL, *names_varrec = NULL, *types_varrec = + NULL, *moles_varrec = NULL; + char **names_arg, **types_arg; + LDBLE *moles_arg; + int arg_num; + LDBLE count_species; + const char *string1, *string2; + + if (LINK->t == NULL) + snerr(": missing variable or command"); + facttok = LINK->t; + LINK->t = LINK->t->next; + n.stringval = false; + s_v.count_subscripts = 0; + /*s_v.subscripts = (int *) PhreeqcPtr->PHRQ_malloc (sizeof (int)); */ + s_v.subscripts = NULL; + switch (facttok->kind) + { + + case toknum: + n.UU.val = facttok->UU.num; + break; + + case tokstr: + n.stringval = true; + m = (int) strlen(facttok->UU.sp) + 1; + if (m < 256) + m = 256; + n.UU.sval = (char *) PhreeqcPtr->PHRQ_calloc(m, sizeof(char)); + if (n.UU.sval == NULL) + PhreeqcPtr->malloc_error(); + strcpy(n.UU.sval, facttok->UU.sp); + break; + + case tokvar: + LINK->t = facttok; + v = findvar(LINK); + n.stringval = v->stringvar; + if (n.stringval) + { + if (*v->UU.U1.sval != NULL) + { + m = (int) strlen(*v->UU.U1.sval) + 1; + if (m < 256) + m = 256; + } + else + { + m = 256; + } + n.UU.sval = (char *) PhreeqcPtr->PHRQ_calloc(m, sizeof(char)); + if (n.UU.sval == NULL) + PhreeqcPtr->malloc_error(); + if (*v->UU.U1.sval != NULL) + { + strcpy(n.UU.sval, *v->UU.U1.sval); + } + + } + else + n.UU.val = *v->UU.U0.val; + break; + + case toklp: + n = expr(LINK); + require(tokrp, LINK); + break; + + case tokminus: + n.UU.val = -realfactor(LINK); + break; + + case tokplus: + n.UU.val = realfactor(LINK); + break; + + case toknot: + n.UU.val = ~intfactor(LINK); + break; + + case toksqr: + TEMP = realfactor(LINK); + n.UU.val = TEMP * TEMP; + break; + + case toksqrt: + n.UU.val = sqrt(realfactor(LINK)); + break; + + case tokceil: + n.UU.val = ceil(realfactor(LINK)); + break; + + case tokfloor: + n.UU.val = floor(realfactor(LINK)); + break; + + case toktc: + n.UU.val = PhreeqcPtr->tc_x; + break; + + case toktk: + n.UU.val = PhreeqcPtr->tc_x + 273.15; + break; + + case toktime: + n.UU.val = PhreeqcPtr->rate_time; + break; + + case toksim_time: + if (!PhreeqcPtr->use.Get_kinetics_in()) + { + if (PhreeqcPtr->state == PHAST) + { + n.UU.val = PhreeqcPtr->rate_sim_time; + } + else if (PhreeqcPtr->state == TRANSPORT) + { + n.UU.val = PhreeqcPtr->transport_step * PhreeqcPtr->timest; + } + else if (PhreeqcPtr->state == ADVECTION) + { + if (PhreeqcPtr->advection_kin_time_defined == TRUE) + { + n.UU.val = PhreeqcPtr->advection_step * PhreeqcPtr->advection_kin_time; + } + else + { + n.UU.val = PhreeqcPtr->advection_step; + } + } + else + { + n.UU.val = 0; + } + } + else + { + n.UU.val = PhreeqcPtr->rate_sim_time; + } + break; + + case toktotal_time: + if (!PhreeqcPtr->use.Get_kinetics_in()) + { + if (PhreeqcPtr->state == PHAST) + { + n.UU.val = PhreeqcPtr->rate_sim_time_end; + } + else if (PhreeqcPtr->state == TRANSPORT) + { + n.UU.val = PhreeqcPtr->initial_total_time + PhreeqcPtr->transport_step * PhreeqcPtr->timest; + } + else if (PhreeqcPtr->state == ADVECTION) + { + n.UU.val = + PhreeqcPtr->initial_total_time + PhreeqcPtr->advection_step * PhreeqcPtr->advection_kin_time; + } + else + { + n.UU.val = 0; + } + } + else + { + n.UU.val = PhreeqcPtr->initial_total_time + PhreeqcPtr->rate_sim_time; + } + break; + + case tokm0: + n.UU.val = PhreeqcPtr->rate_m0; + break; + + case tokm: + n.UU.val = PhreeqcPtr->rate_m; + break; + + case tokparm: + i_rate = intfactor(LINK); + if (parse_all) + { + n.UU.val = 1; + } + else + { + if (i_rate > PhreeqcPtr->count_rate_p || i_rate == 0) + { + errormsg("Parameter subscript out of range."); + } + n.UU.val = PhreeqcPtr->rate_p[i_rate - 1]; + } + + break; + + case tokact: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->activity(str); + } + break; + + case tokgamma: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->activity_coefficient(str); + } + break; + + case toklg: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->log_activity_coefficient(str); + } + break; + + case tokget_por: + i = intfactor(LINK); + if (parse_all) + { + n.UU.val = 1; + } + else + { + if (PhreeqcPtr->phast != TRUE) + { + if (i <= 0 || i > PhreeqcPtr->count_cells * (1 + PhreeqcPtr->stag_data->count_stag) + 1 + || i == PhreeqcPtr->count_cells + 1) + { + /* warning_msg("Note... no porosity for boundary solutions."); */ + n.UU.val = 0; + break; + } + else + n.UU.val = PhreeqcPtr->cell_data[i].por; + break; + } + else + { + n.UU.val = PhreeqcPtr->cell_porosity; + break; + } + } + break; + case tokedl: + require(toklp, LINK); + elt_name = stringfactor(STR1, LINK); + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + LINK->t = LINK->t->next; + surface_name = stringfactor(STR2, LINK); + } + else + { + surface_name = NULL; + } + require(tokrp, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->diff_layer_total(elt_name, surface_name); + break; + + case toksurf: + require(toklp, LINK); + elt_name = stringfactor(STR1, LINK); + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + LINK->t = LINK->t->next; + surface_name = stringfactor(STR2, LINK); + } + else + { + surface_name = NULL; + } + require(tokrp, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->surf_total(elt_name, surface_name); + break; + + case tokequi: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->equi_phase(str); + } + break; + + case tokequi_delta: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->equi_phase_delta(str); + } + break; + + case tokkin: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->kinetics_moles(str); + } + break; + + case tokkin_delta: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->kinetics_moles_delta(str); + } + break; + + case tokkin_time: + { + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->rate_kin_time; + } + break; + + case tokgas: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->find_gas_comp(str); + } + break; + + case toks_s: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->find_ss_comp(str); + } + break; + + case tokmisc1: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->find_misc1(str); + } + break; + + case tokmisc2: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->find_misc2(str); + } + break; + + case tokmu: + n.UU.val = PhreeqcPtr->mu_x; + break; + + case tokosmotic: + if (PhreeqcPtr->pitzer_model == TRUE || PhreeqcPtr->sit_model == TRUE) + { + n.UU.val = PhreeqcPtr->COSMOT; + } + else + { + n.UU.val = 0.0; + } + break; + + case tokalk: + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->total_alkalinity / PhreeqcPtr->mass_water_aq_x; + break; + + case toklk_species: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->calc_logk_s(str); + } + break; + + case toklk_named: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->calc_logk_n(str); + } + break; + + case toklk_phase: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->calc_logk_p(str); + } + break; + + case toksum_species: + require(toklp, LINK); + mytemplate = stringfactor(STR1, LINK); + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + LINK->t = LINK->t->next; + elt_name = stringfactor(STR2, LINK); + } + else + { + elt_name = NULL; + } + require(tokrp, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->sum_match_species(mytemplate, elt_name); + break; + + case toksum_gas: + require(toklp, LINK); + mytemplate = stringfactor(STR1, LINK); + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + LINK->t = LINK->t->next; + elt_name = stringfactor(STR2, LINK); + } + else + { + elt_name = NULL; + } + require(tokrp, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->sum_match_gases(mytemplate, elt_name); + break; + + case toksum_s_s: + require(toklp, LINK); + mytemplate = stringfactor(STR1, LINK); + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + LINK->t = LINK->t->next; + elt_name = stringfactor(STR2, LINK); + } + else + { + elt_name = NULL; + } + require(tokrp, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->sum_match_ss(mytemplate, elt_name); + break; + + case tokcalc_value: + require(toklp, LINK); + name = stringfactor(STR1, LINK); + require(tokrp, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->get_calculate_value(name); + break; + + case tokdescription: + n.stringval = true; + if (PhreeqcPtr->state == REACTION) + { + if (PhreeqcPtr->use.Get_mix_in()) + { + sprintf(string, "Mix %d", PhreeqcPtr->use.Get_n_mix_user()); + n.UU.sval = PhreeqcPtr->string_duplicate(string); + } + else + { + soln_ptr = Utilities::Rxn_find(PhreeqcPtr->Rxn_solution_map, + PhreeqcPtr->use.Get_n_solution_user()); + if (soln_ptr != NULL) + { + n.UU.sval = PhreeqcPtr->string_duplicate(soln_ptr->Get_description().c_str()); + } + else + { + n.UU.sval = PhreeqcPtr->string_duplicate("Unknown"); + } + } + } + else if (PhreeqcPtr->state == ADVECTION || PhreeqcPtr->state == TRANSPORT || PhreeqcPtr->state == PHAST) + { + sprintf(string, "Cell %d", PhreeqcPtr->cell_no); + n.UU.sval = PhreeqcPtr->string_duplicate(string); + } + else + { + if (PhreeqcPtr->use.Get_solution_ptr() != NULL) + { + n.UU.sval = PhreeqcPtr->string_duplicate(PhreeqcPtr->use.Get_solution_ptr()->Get_description().c_str()); + } + else + { + n.UU.sval = PhreeqcPtr->string_duplicate("Unknown"); + } + } + while (PhreeqcPtr->replace("\t", " ", n.UU.sval)); + break; + + case toktitle: + n.stringval = true; + if (strlen(PhreeqcPtr->last_title_x.c_str()) == 0) + { + PhreeqcPtr->last_title_x = " "; + } + n.UU.sval = PhreeqcPtr->string_duplicate(PhreeqcPtr->last_title_x.c_str()); + while (PhreeqcPtr->replace("\t", " ", n.UU.sval)); + break; + + case tokinstr: + require(toklp, LINK); + string1 = stringfactor(STR1, LINK); + require(tokcomma, LINK); + string2 = stringfactor(STR2, LINK); + require(tokrp, LINK); + { + const char * cptr = strstr(string1, string2); + if (cptr == NULL) + { + n.UU.val = 0; + } + else + { + n.UU.val = ((LDBLE) (cptr - string1)) + 1; + } + } + break; + + case tokltrim: + n.stringval = true; + require(toklp, LINK); + string1 = stringfactor(STR1, LINK); + require(tokrp, LINK); + trim_left(STR1); + n.UU.sval = PhreeqcPtr->string_duplicate(STR1.c_str()); + break; + + case tokrtrim: + n.stringval = true; + require(toklp, LINK); + string1 = stringfactor(STR1, LINK); + require(tokrp, LINK); + trim_right(STR1); + n.UU.sval = PhreeqcPtr->string_duplicate(STR1.c_str()); + break; + + case toktrim: + n.stringval = true; + require(toklp, LINK); + string1 = stringfactor(STR1, LINK); + require(tokrp, LINK); + STR1 = trim(STR1); + n.UU.sval = PhreeqcPtr->string_duplicate(STR1.c_str()); + break; + + case tokiso: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->iso_value(str); + } + break; + + case tokiso_unit: + n.stringval = true; + require(toklp, LINK); + string1 = stringfactor(STR1, LINK); + require(tokrp, LINK); + trim(STR1); + n.UU.sval = (parse_all) ? PhreeqcPtr->string_duplicate("unknown") : PhreeqcPtr->iso_unit(STR1.c_str()); + break; + + case tokpad: + n.stringval = true; + require(toklp, LINK); + string1 = stringfactor(STR1, LINK); + require(tokcomma, LINK); + i = intexpr(LINK); + require(tokrp, LINK); + n.UU.sval = PhreeqcPtr->string_pad(string1, i); + break; + + case toksys: + require(toklp, LINK); + elt_name = stringfactor(STR1, LINK); + /* + * Parse arguments + */ + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + /* return number of species */ + LINK->t = LINK->t->next; + count_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || !count_varrec || count_varrec->stringvar != 0) + { + snerr(": can`t find variable"); +#if !defined(R_SO) + exit(4); +#endif + } + + /* return number of names of species */ + LINK->t = LINK->t->next; + require(tokcomma, LINK); + names_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || !names_varrec || names_varrec->stringvar != 1) + { + snerr(": can`t find name of species"); +#if !defined(R_SO) + exit(4); +#endif + } + + /* return number of types of species */ + LINK->t = LINK->t->next; + require(tokcomma, LINK); + types_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || types_varrec->stringvar != 1) + snerr(": can`t find type of species"); + + /* return number of moles of species */ + LINK->t = LINK->t->next; + require(tokcomma, LINK); + moles_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || moles_varrec->stringvar != 0) + snerr(": can`t find moles of species"); + LINK->t = LINK->t->next; + arg_num = 4; + } + else + { + arg_num = 1; + } + require(tokrp, LINK); + + if (arg_num > 1) + { + free_dim_stringvar(names_varrec); + free_dim_stringvar(types_varrec); + PhreeqcPtr->free_check_null(moles_varrec->UU.U0.arr); + moles_varrec->UU.U0.arr = NULL; + } + /* + * Call subroutine + */ + /* + n.UU.val = system_total(elt_name, count_varrec->UU.U0.val, &(names_varrec->UU.U1.sarr), &(types_varrec->UU.U1.sarr), &(moles_varrec->UU.U0.arr)); + */ + if (parse_all) + { + PhreeqcPtr->sys_tot = 0; + PhreeqcPtr->count_sys = 1000; + int count_sys = PhreeqcPtr->count_sys; + names_arg = (char **) PhreeqcPtr->PHRQ_calloc((size_t) (count_sys + 1), sizeof(char *)); + if (names_arg == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + types_arg = (char **)PhreeqcPtr->PHRQ_calloc((size_t) (count_sys + 1), sizeof(char *)); + if (types_arg == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + moles_arg = (LDBLE *) PhreeqcPtr->PHRQ_calloc((size_t) (count_sys + 1), sizeof(LDBLE)); + if (moles_arg == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + names_arg[0] = NULL; + types_arg[0] = NULL; + moles_arg[0] = 0; + count_species = (LDBLE) count_sys; + n.UU.val = 0; + } + else + { + n.UU.val = PhreeqcPtr->system_total(elt_name, &count_species, &(names_arg), + &(types_arg), &(moles_arg)); + } + + /* + * fill in varrec structure + */ + if (arg_num > 1) + { + *count_varrec->UU.U0.val = count_species; + names_varrec->UU.U1.sarr = names_arg; + types_varrec->UU.U1.sarr = types_arg; + moles_varrec->UU.U0.arr = moles_arg; + + for (i = 0; i < maxdims; i++) + { + names_varrec->dims[i] = 0; + types_varrec->dims[i] = 0; + moles_varrec->dims[i] = 0; + } + names_varrec->dims[0] = (long) (*count_varrec->UU.U0.val) + 1; + types_varrec->dims[0] = (long) (*count_varrec->UU.U0.val) + 1; + moles_varrec->dims[0] = (long) (*count_varrec->UU.U0.val) + 1; + names_varrec->numdims = 1; + types_varrec->numdims = 1; + moles_varrec->numdims = 1; + } + else + { + for (i = 0; i < count_species + 1; i++) + { + PhreeqcPtr->free_check_null(names_arg[i]); + PhreeqcPtr->free_check_null(types_arg[i]); + } + PhreeqcPtr->free_check_null(names_arg); + PhreeqcPtr->free_check_null(types_arg); + PhreeqcPtr->free_check_null(moles_arg); + } + break; + + case tokedl_species: + { + double area, thickness; + require(toklp, LINK); + const char *surf_name = stringfactor(STR1, LINK); + require(tokcomma, LINK); + // variable for number of species + count_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || !count_varrec || count_varrec->stringvar != 0) + { + snerr(": Missing or wrong type count variable."); +#if !defined(R_SO) + exit(4); +#endif + } + // variable for species names + LINK->t = LINK->t->next; + require(tokcomma, LINK); + names_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || !names_varrec || names_varrec->stringvar != 1) + { + snerr(": Missing or wrong type name variable."); +#if !defined(R_SO) + exit(4); +#endif + } + // variable for species concentrations + LINK->t = LINK->t->next; + require(tokcomma, LINK); + moles_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || moles_varrec->stringvar != 0) + snerr(": Missing or wrong type moles variable."); + // variable for area + LINK->t = LINK->t->next; + require(tokcomma, LINK); + varrec *area_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || area_varrec->stringvar != 0) + snerr(": Missing or wrong type area varaiable."); + // varaiable for thickness + LINK->t = LINK->t->next; + require(tokcomma, LINK); + varrec *thickness_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || thickness_varrec->stringvar != 0) + snerr(": Missing or wrong type thickness variable."); + LINK->t = LINK->t->next; + require(tokrp, LINK); + + free_dim_stringvar(names_varrec); + PhreeqcPtr->free_check_null(moles_varrec->UU.U0.arr); + moles_varrec->UU.U0.arr = NULL; + + // Call subroutine + if (parse_all) + { + PhreeqcPtr->sys_tot = 0; + PhreeqcPtr->count_sys = 1000; + int count_sys = PhreeqcPtr->count_sys; + names_arg = (char **) PhreeqcPtr->PHRQ_calloc((size_t) (count_sys + 1), sizeof(char *)); + if (names_arg == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + moles_arg = (LDBLE *) PhreeqcPtr->PHRQ_calloc((size_t) (count_sys + 1), sizeof(LDBLE)); + if (moles_arg == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + names_arg[0] = NULL; + moles_arg[0] = 0; + count_species = (LDBLE) count_sys; + n.UU.val = 0; + } + else + { + //n.UU.val = PhreeqcPtr->system_total(elt_name, &count_species, &(names_arg), + // &(types_arg), &(moles_arg)); + n.UU.val = PhreeqcPtr->edl_species(surf_name, &count_species, &(names_arg), &(moles_arg), &area, &thickness); + } + /* + * fill in varrec structures + */ + *count_varrec->UU.U0.val = count_species; + names_varrec->UU.U1.sarr = names_arg; + moles_varrec->UU.U0.arr = moles_arg; + *area_varrec->UU.U0.val = area; + *thickness_varrec->UU.U0.val = thickness; + + for (i = 0; i < maxdims; i++) + { + names_varrec->dims[i] = 0; + moles_varrec->dims[i] = 0; + } + names_varrec->dims[0] = (long) (*count_varrec->UU.U0.val) + 1; + moles_varrec->dims[0] = (long) (*count_varrec->UU.U0.val) + 1; + names_varrec->numdims = 1; + moles_varrec->numdims = 1; + } + break; + + case toklist_s_s: + { + /* list_s_s("calcite", count, name$, moles) */ + /* return total moles */ + require(toklp, LINK); + std::string s_s_name(stringfactor(STR1, LINK)); + cxxNameDouble composition; + /* + * Parse arguments + */ + arg_num = -1; + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + LINK->t = LINK->t->next; + count_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || count_varrec->stringvar != 0) + snerr(": Cannot find count variable"); + + /* return number of names of components */ + LINK->t = LINK->t->next; + require(tokcomma, LINK); + names_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || names_varrec->stringvar != 1) + snerr(": Cannot find component string variable"); + + /* return number of moles of components */ + LINK->t = LINK->t->next; + require(tokcomma, LINK); + moles_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || moles_varrec->stringvar != 0) + snerr(": Cannot find moles of component variable"); + LINK->t = LINK->t->next; + arg_num = 4; + } + else + { + snerr(": Expected 4 arguments for list_s_s"); +#if !defined(R_SO) + exit(4); +#endif + } + require(tokrp, LINK); + + if (arg_num > 1) + { + free_dim_stringvar(names_varrec); + if (moles_varrec) + { + PhreeqcPtr->free_check_null(moles_varrec->UU.U0.arr); + moles_varrec->UU.U0.arr = NULL; + } + } + /* + * Call subroutine + */ + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->list_ss(s_s_name, composition); + + /* + * fill in varrec structure + */ + + if (arg_num > 1) + { + size_t count = composition.size(); + *count_varrec->UU.U0.val = (LDBLE) count; + /* + * malloc space + */ + names_varrec->UU.U1.sarr = (char **) PhreeqcPtr->PHRQ_malloc((count + 1) * sizeof(char *)); + if (names_varrec->UU.U1.sarr == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + moles_varrec->UU.U0.arr = (LDBLE *) PhreeqcPtr->PHRQ_malloc((count + 1) * sizeof(LDBLE)); + if (moles_varrec->UU.U0.arr == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + + // first position not used + names_varrec->UU.U1.sarr[0] = NULL; + moles_varrec->UU.U0.arr[0] = 0; + + // set dims for Basic array + for (i = 0; i < maxdims; i++) + { + names_varrec->dims[i] = 0; + moles_varrec->dims[i] = 0; + } + // set dims for first dimension and number of dims + names_varrec->dims[0] = (long) (count + 1); + moles_varrec->dims[0] = (long) (count + 1); + names_varrec->numdims = 1; + moles_varrec->numdims = 1; + + // fill in arrays + i = 1; + std::vector< std::pair > sort_comp = composition.sort_second(); + size_t j; + for (j = 0; j != sort_comp.size(); j++) + { + names_varrec->UU.U1.sarr[i] = PhreeqcPtr->string_duplicate(sort_comp[j].first.c_str()); + moles_varrec->UU.U0.arr[i] = sort_comp[j].second; + i++; + } + + } + break; + } + + case tokkinetics_formula: + case tokkinetics_formula_: + { + require(toklp, LINK); + std::string kinetics_name(stringfactor(STR1, LINK)); + varrec *elts_varrec = NULL, *coef_varrec = NULL; + cxxNameDouble stoichiometry; + /* + * Parse arguments + */ + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + /* kinetics_formula("calcite", count, elt, coef) */ + /* return formula */ + /*int c; */ + /* struct varrec *count_varrec, *names_varrec, *types_varrec, *moles_varrec; */ + /* struct varrec *count_varrec, *elt_varrec, *coef_varrec; */ + /* return number of species */ + LINK->t = LINK->t->next; + count_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || count_varrec->stringvar != 0) + snerr(": Cannot find count variable"); + + /* return number of names of elements */ + LINK->t = LINK->t->next; + require(tokcomma, LINK); + elts_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || elts_varrec->stringvar != 1) + snerr(": Cannot find element string variable"); + + /* return coefficients of species */ + LINK->t = LINK->t->next; + require(tokcomma, LINK); + coef_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || coef_varrec->stringvar != 0) + snerr(": Cannot find coefficient variable"); + LINK->t = LINK->t->next; + arg_num = 4; + } + else + { + arg_num = 1; + } + require(tokrp, LINK); + + if (arg_num > 1) + { + free_dim_stringvar(elts_varrec); + PhreeqcPtr->free_check_null(coef_varrec->UU.U0.arr); + coef_varrec->UU.U0.arr = NULL; + } + /* + * Call subroutine + */ + std::string form = PhreeqcPtr->kinetics_formula(kinetics_name, stoichiometry); + + // put formula as return value + n.stringval = true; + n.UU.sval = PhreeqcPtr->string_duplicate(form.c_str()); + + /* + * fill in varrec structure + */ + + if (arg_num > 1) + { + size_t count = stoichiometry.size(); + *count_varrec->UU.U0.val = (LDBLE) count; + /* + * malloc space + */ + elts_varrec->UU.U1.sarr = (char **) PhreeqcPtr->PHRQ_malloc((count + 1) * sizeof(char *)); + if (elts_varrec->UU.U1.sarr == NULL) + PhreeqcPtr->malloc_error(); + coef_varrec->UU.U0.arr = (LDBLE *) PhreeqcPtr->PHRQ_malloc((count + 1) * sizeof(LDBLE)); + if (coef_varrec->UU.U0.arr == NULL) + PhreeqcPtr->malloc_error(); + + // first position not used + elts_varrec->UU.U1.sarr[0] = NULL; + coef_varrec->UU.U0.arr[0] = 0; + + // set dims for Basic array + for (i = 0; i < maxdims; i++) + { + elts_varrec->dims[i] = 0; + coef_varrec->dims[i] = 0; + } + // set dims for first dimension and number of dims + elts_varrec->dims[0] = (long) (count + 1); + coef_varrec->dims[0] = (long) (count + 1); + elts_varrec->numdims = 1; + coef_varrec->numdims = 1; + + // fill in arrays + i = 1; + for (cxxNameDouble::iterator it = stoichiometry.begin(); it != stoichiometry.end(); it++) + { + elts_varrec->UU.U1.sarr[i] = PhreeqcPtr->string_duplicate((it->first).c_str()); + coef_varrec->UU.U0.arr[i] = it->second; + i++; + } + + } + break; + } + case tokphase_formula: + case tokphase_formula_: + { + require(toklp, LINK); + std::string phase_name(stringfactor(STR1, LINK)); + varrec *elts_varrec = NULL, *coef_varrec = NULL; + cxxNameDouble stoichiometry; + /* + * Parse arguments + */ + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + /* phase_formula("calcite", count, elt, coef) */ + /* return formula */ + /*int c; */ + /* struct varrec *count_varrec, *names_varrec, *types_varrec, *moles_varrec; */ + /* struct varrec *count_varrec, *elt_varrec, *coef_varrec; */ + /* return number of species */ + LINK->t = LINK->t->next; + count_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || count_varrec->stringvar != 0) + snerr(": Cannot find count variable"); + + /* return number of names of species */ + LINK->t = LINK->t->next; + require(tokcomma, LINK); + elts_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || elts_varrec->stringvar != 1) + snerr(": Cannot find element string variable"); + + /* return coefficients of species */ + LINK->t = LINK->t->next; + require(tokcomma, LINK); + coef_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || coef_varrec->stringvar != 0) + snerr(": Cannot find coefficient variable"); + LINK->t = LINK->t->next; + arg_num = 4; + } + else + { + arg_num = 1; + } + require(tokrp, LINK); + + if (arg_num > 1) + { + free_dim_stringvar(elts_varrec); + PhreeqcPtr->free_check_null(coef_varrec->UU.U0.arr); + coef_varrec->UU.U0.arr = NULL; + } + /* + * Call subroutine + */ + std::string form = PhreeqcPtr->phase_formula(phase_name, stoichiometry); + + // put formula as return value + n.stringval = true; + n.UU.sval = PhreeqcPtr->string_duplicate(form.c_str()); + + /* + * fill in varrec structure + */ + + if (arg_num > 1) + { + size_t count = stoichiometry.size(); + *count_varrec->UU.U0.val = (LDBLE) count; + /* + * malloc space + */ + elts_varrec->UU.U1.sarr = (char **) PhreeqcPtr->PHRQ_malloc((count + 1) * sizeof(char *)); + if (elts_varrec->UU.U1.sarr == NULL) + PhreeqcPtr->malloc_error(); + coef_varrec->UU.U0.arr = (LDBLE *) PhreeqcPtr->PHRQ_malloc((count + 1) * sizeof(LDBLE)); + if (coef_varrec->UU.U0.arr == NULL) + PhreeqcPtr->malloc_error(); + + // first position not used + elts_varrec->UU.U1.sarr[0] = NULL; + coef_varrec->UU.U0.arr[0] = 0; + + // set dims for Basic array + for (i = 0; i < maxdims; i++) + { + elts_varrec->dims[i] = 0; + coef_varrec->dims[i] = 0; + } + // set dims for first dimension and number of dims + elts_varrec->dims[0] = (long) (count + 1); + coef_varrec->dims[0] = (long) (count + 1); + elts_varrec->numdims = 1; + coef_varrec->numdims = 1; + + // fill in arrays + i = 1; + for (cxxNameDouble::iterator it = stoichiometry.begin(); it != stoichiometry.end(); it++) + { + elts_varrec->UU.U1.sarr[i] = PhreeqcPtr->string_duplicate((it->first).c_str()); + coef_varrec->UU.U0.arr[i] = it->second; + i++; + } + + } + break; + } + case tokspecies_formula: + case tokspecies_formula_: + { + require(toklp, LINK); + std::string species_name(stringfactor(STR1, LINK)); + varrec *elts_varrec = NULL, *coef_varrec = NULL; + cxxNameDouble stoichiometry; + /* + * Parse arguments + */ + require(tokcomma, LINK); + + count_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || count_varrec->stringvar != 0) + snerr(": Cannot find count variable"); + + /* return number of names of species */ + LINK->t = LINK->t->next; + require(tokcomma, LINK); + elts_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || elts_varrec->stringvar != 1) + snerr(": Cannot find element string variable"); + + /* return coefficients of species */ + LINK->t = LINK->t->next; + require(tokcomma, LINK); + coef_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || coef_varrec->stringvar != 0) + snerr(": Cannot find coefficient variable"); + LINK->t = LINK->t->next; + + require(tokrp, LINK); + + free_dim_stringvar(elts_varrec); + PhreeqcPtr->free_check_null(coef_varrec->UU.U0.arr); + coef_varrec->UU.U0.arr = NULL; + /* + * Call subroutine + */ + std::string type = PhreeqcPtr->species_formula(species_name, stoichiometry); + + // put type as return value + n.stringval = true; + n.UU.sval = PhreeqcPtr->string_duplicate(type.c_str()); + + /* + * fill in varrec structure + */ + + size_t count = stoichiometry.size(); + *count_varrec->UU.U0.val = (LDBLE) count; + /* + * malloc space + */ + elts_varrec->UU.U1.sarr = (char **) PhreeqcPtr->PHRQ_malloc((count + 1) * sizeof(char *)); + if (elts_varrec->UU.U1.sarr == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + coef_varrec->UU.U0.arr = (LDBLE *) PhreeqcPtr->PHRQ_malloc((count + 1) * sizeof(LDBLE)); + if (coef_varrec->UU.U0.arr == NULL) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + + // first position not used + elts_varrec->UU.U1.sarr[0] = NULL; + coef_varrec->UU.U0.arr[0] = 0; + + // set dims for Basic array + for (i = 0; i < maxdims; i++) + { + elts_varrec->dims[i] = 0; + coef_varrec->dims[i] = 0; + } + // set dims for first dimension and number of dims + elts_varrec->dims[0] = (long) (count + 1); + coef_varrec->dims[0] = (long) (count + 1); + elts_varrec->numdims = 1; + coef_varrec->numdims = 1; + + // fill in arrays + i = 1; + for (cxxNameDouble::iterator it = stoichiometry.begin(); it != stoichiometry.end(); it++) + { + elts_varrec->UU.U1.sarr[i] = PhreeqcPtr->string_duplicate((it->first).c_str()); + coef_varrec->UU.U0.arr[i] = it->second; + i++; + } + + break; + } + case tokrxn: + if (PhreeqcPtr->state == REACTION || + PhreeqcPtr->state == ADVECTION || + PhreeqcPtr->state == TRANSPORT) + { + n.UU.val = PhreeqcPtr->step_x; + } + else + { + n.UU.val = 0.0; + } + break; + + case tokdist: + if (PhreeqcPtr->state == PHAST) + { + n.UU.val = 0; + } + else if (PhreeqcPtr->state == TRANSPORT) + { + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->cell_data[PhreeqcPtr->cell].mid_cell_x; + } + else if (PhreeqcPtr->state == ADVECTION) + { + n.UU.val = (parse_all) ? 1 : (LDBLE) PhreeqcPtr->use.Get_n_solution_user(); + } + else + { + n.UU.val = 0; + } + break; + + case tokmol: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->molality(str); + } + break; + + case tokla: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->log_activity(str); + } + break; + + case toklm: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->log_molality(str); + } + break; + + case toksr: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->saturation_ratio(str); + } + break; + + case tokstep_no: + if (PhreeqcPtr->state == PHAST) + { + n.UU.val = 0; + } + else if (PhreeqcPtr->state == TRANSPORT) + { + n.UU.val = PhreeqcPtr->transport_step; + } + else if (PhreeqcPtr->state == ADVECTION) + { + n.UU.val = PhreeqcPtr->advection_step; + } + else if (PhreeqcPtr->state == REACTION) + { + n.UU.val = PhreeqcPtr->reaction_step; + } + else + { + n.UU.val = 0; + } + break; + + case tokcell_no: + if (parse_all) + { + n.UU.val = 1; + break; + } + n.UU.val = PhreeqcPtr->solution_number(); +#ifdef SKIP + if (PhreeqcPtr->state == TRANSPORT) + { + n.UU.val = PhreeqcPtr->cell_no; + } + else if (PhreeqcPtr->state == PHAST) + { + n.UU.val = PhreeqcPtr->cell_no; + } + else if (PhreeqcPtr->state == ADVECTION) + { + n.UU.val = PhreeqcPtr->cell_no; + } + else if (PhreeqcPtr->state < REACTION) + { + n.UU.val = PhreeqcPtr->use.Get_solution_ptr()->Get_n_user(); + } + else + { + if (PhreeqcPtr->use.Get_mix_in()) + { + n.UU.val = PhreeqcPtr->use.Get_n_mix_user(); + } + else + { + n.UU.val = PhreeqcPtr->use.Get_n_solution_user(); + } + } +#endif + break; + + case toksim_no: + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->simulation; + break; + + case tokget: + require(toklp, LINK); + + s_v.count_subscripts = 0; + /* get first subscript */ + if (LINK->t != NULL && LINK->t->kind != tokrp) + { + i = intexpr(LINK); + if (s_v.subscripts == NULL) + { + s_v.subscripts = (int *) PhreeqcPtr->PHRQ_malloc(sizeof(int)); + if (s_v.subscripts == NULL) + PhreeqcPtr->malloc_error(); + } + s_v.subscripts = + (int *) PhreeqcPtr->PHRQ_realloc(s_v.subscripts, + (size_t) (s_v.count_subscripts + + 1) * sizeof(int)); + if (s_v.subscripts == NULL) + PhreeqcPtr->malloc_error(); + s_v.subscripts[s_v.count_subscripts] = i; + s_v.count_subscripts++; + } + + /* get other subscripts */ + for (;;) + { + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + LINK->t = LINK->t->next; + j = intexpr(LINK); + if (s_v.subscripts == NULL) + { + s_v.subscripts = (int *) PhreeqcPtr->PHRQ_malloc(sizeof(int)); + if (s_v.subscripts == NULL) + PhreeqcPtr->malloc_error(); + } + s_v.subscripts = + (int *) PhreeqcPtr->PHRQ_realloc(s_v.subscripts, + (size_t) (s_v.count_subscripts + + 1) * sizeof(int)); + if (s_v.subscripts == NULL) + PhreeqcPtr->malloc_error(); + s_v.subscripts[s_v.count_subscripts] = j; + s_v.count_subscripts++; + } + else + { + /* get right parentheses */ + require(tokrp, LINK); + break; + } + } + s_v_ptr = (parse_all) ? NULL : PhreeqcPtr->save_values_bsearch(&s_v, &k); + if (s_v_ptr == NULL) + { + n.UU.val = (parse_all) ? 1 : 0; + } + else + { + n.UU.val = s_v_ptr->value; + } + break; + + case tokexists: + require(toklp, LINK); + + s_v.count_subscripts = 0; + /* get first subscript */ + if (LINK->t != NULL && LINK->t->kind != tokrp) + { + i = intexpr(LINK); + if (s_v.subscripts == NULL) + { + s_v.subscripts = (int *) PhreeqcPtr->PHRQ_malloc(sizeof(int)); + if (s_v.subscripts == NULL) + PhreeqcPtr->malloc_error(); + } + s_v.subscripts = + (int *) PhreeqcPtr->PHRQ_realloc(s_v.subscripts, + (size_t) (s_v.count_subscripts + + 1) * sizeof(int)); + if (s_v.subscripts == NULL) + { + PhreeqcPtr->malloc_error(); + } + else + { + s_v.subscripts[s_v.count_subscripts] = i; + s_v.count_subscripts++; + } + } + + /* get other subscripts */ + for (;;) + { + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + LINK->t = LINK->t->next; + j = intexpr(LINK); + if (s_v.subscripts == NULL) + { + s_v.subscripts = (int *) PhreeqcPtr->PHRQ_malloc(sizeof(int)); + if (s_v.subscripts == NULL) + PhreeqcPtr->malloc_error(); + } + s_v.subscripts = + (int *) PhreeqcPtr->PHRQ_realloc(s_v.subscripts, + (size_t) (s_v.count_subscripts + + 1) * sizeof(int)); + if (s_v.subscripts == NULL) + { + PhreeqcPtr->malloc_error(); + } + else + { + s_v.subscripts[s_v.count_subscripts] = j; + s_v.count_subscripts++; + } + } + else + { + /* get right parentheses */ + require(tokrp, LINK); + break; + } + } + if (parse_all) + { + n.UU.val = 1; + } + else + { + s_v_ptr = PhreeqcPtr->save_values_bsearch(&s_v, &k); + if (s_v_ptr == NULL) + { + n.UU.val = 0; + } + else + { + n.UU.val = 1; + } + } + break; + + case tokcharge_balance: + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->cb_x; + break; + + case tokpercent_error: + n.UU.val = (parse_all) ? 1 : 100 * PhreeqcPtr->cb_x / PhreeqcPtr->total_ions_x; + break; + + case toksi: + { + const char * str = stringfactor(STR1, LINK); + if (parse_all) + { + n.UU.val = 1; + } + else + { + PhreeqcPtr->saturation_index(str, &l_dummy, &n.UU.val); + } + } + break; + + case toktot: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->total(str); + } + break; + + case toktotmole: + case toktotmol: + case toktotmoles: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->total_mole(str); + } + break; + + case tokcell_pore_volume: + case tokporevolume: + { + double x1 = (double) PhreeqcPtr->solution_number(); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->basic_callback(x1, x1, "cell_pore_volume"); + } + break; + +/* VP : Density Start */ + case tokrho: + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->calc_dens(); + break; + case tokrho_0: + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->rho_0; + break; +/* VP: Density End */ + case tokcell_volume: + { + double x1 = (double) PhreeqcPtr->solution_number(); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->basic_callback(x1, x1, "cell_volume"); + } + break; + case tokcell_porosity: + { + double x1 = (double) PhreeqcPtr->solution_number(); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->basic_callback(x1, x1, "cell_porosity"); + } + break; + case tokcell_saturation: + { + double x1 = (double) PhreeqcPtr->solution_number(); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->basic_callback(x1, x1, "cell_saturation"); + } + break; + case toksc: + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->calc_SC(); + break; + case tokpr_p: + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->pr_pressure(stringfactor(STR1, LINK)); + break; + case tokpressure: + n.UU.val = PhreeqcPtr->pressure(); + break; + case tokpr_phi: + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->pr_phi(stringfactor(STR1, LINK)); + break; + case tokgas_p: + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->find_gas_p(); + break; + case tokgas_vm: + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->find_gas_vm(); + break; + case tokeps_r: + n.UU.val = PhreeqcPtr->eps_r; + break; + case tokaphi: + n.UU.val = PhreeqcPtr->A0; + break; + case tokdh_a: + n.UU.val = PhreeqcPtr->DH_A; + break; + case tokdh_b: + n.UU.val = PhreeqcPtr->DH_B; + break; + case tokdh_av: + n.UU.val = PhreeqcPtr->DH_Av; + break; + case tokqbrn: + n.UU.val = PhreeqcPtr->QBrn; + break; + case tokkappa: + n.UU.val = PhreeqcPtr->kappa_0; + break; + case tokgfw: + { + const char * str = stringfactor(STR1, LINK); + LDBLE gfw; + PhreeqcPtr->compute_gfw(str, &gfw); + n.UU.val = (parse_all) ? 1 : gfw; + } + break; + case toksoln_vol: + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->calc_solution_volume(); + break; + case tokvm: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->aqueous_vm(str); + } + break; + case tokphase_vm: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->phase_vm(str); + } + break; + case tokviscos: + { + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->viscos; + } + break; + case tokviscos_0: + { + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->viscos_0; + } + break; + case tokcurrent_a: + //n.UU.val = (parse_all) ? 1 : PhreeqcPtr->current_x; + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->current_A; + break; + case tokpot_v: + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->use.Get_solution_ptr()->Get_potV(); + break; + case tokt_sc: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->calc_t_sc(str); + } + break; + + case toklog10: + { + LDBLE t = realfactor(LINK); + //if (t > 0.0) + { + n.UU.val = log10(t); + } + //else + //{ + // n.UU.val = 0; + //} + } + break; + case toksin: + n.UU.val = sin(realfactor(LINK)); + break; + + case tokcos: + n.UU.val = cos(realfactor(LINK)); + break; + + case toktan: + n.UU.val = realfactor(LINK); + n.UU.val = sin(n.UU.val) / cos(n.UU.val); + break; + + case tokarctan: + n.UU.val = atan(realfactor(LINK)); + break; + + case toklog: + n.UU.val = log(realfactor(LINK)); + break; + + case tokexp: + n.UU.val = exp(realfactor(LINK)); + break; + + case tokabs: + n.UU.val = fabs(realfactor(LINK)); + break; + + case toksgn: + n.UU.val = realfactor(LINK); + n.UU.val = (n.UU.val > 0) - (n.UU.val < 0); + break; + + case tokstr_: + n.stringval = true; + n.UU.sval = (char *) PhreeqcPtr->PHRQ_calloc(256, sizeof(char)); + if (n.UU.sval == NULL) + PhreeqcPtr->malloc_error(); + numtostr(n.UU.sval, realfactor(LINK)); + break; + + case tokstr_f_: + { + // left parenthesis + require(toklp, LINK); + + // set number + LDBLE nmbr; + nmbr = realexpr(LINK); + + // set total length + require(tokcomma, LINK); + int length = (int) realexpr(LINK); + + // set total length + require(tokcomma, LINK); + int width = (int) realexpr(LINK); + + // right parenthesis + require(tokrp, LINK); + + // Make work space + int max_length = length < 256 ? 256 : length; + char *token = (char *) PhreeqcPtr->PHRQ_calloc(size_t (max_length + 1), sizeof(char)); + if (token == NULL) PhreeqcPtr->malloc_error(); + + std::string std_num; + { + sprintf(token, "%*.*f", length, width, nmbr); + std_num = token; + } + + // set function value + n.UU.sval = (char *) PhreeqcPtr->PHRQ_calloc(std_num.size() + 2, sizeof(char)); + if (n.UU.sval == NULL) + PhreeqcPtr->malloc_error(); + strcpy(n.UU.sval, std_num.c_str()); + n.stringval = true; + + // free work space + PhreeqcPtr->free_check_null(token); + } + break; + + case tokstr_e_: + { + // left parenthesis + require(toklp, LINK); + + // set number + LDBLE nmbr; + nmbr = realexpr(LINK); + + // set total length + require(tokcomma, LINK); + int length = (int) realexpr(LINK); + + // set total length + require(tokcomma, LINK); + int width = (int) realexpr(LINK); + + // right parenthesis + require(tokrp, LINK); + + // Make work space + int max_length = length < 256 ? 256 : length; + char *token = (char *) PhreeqcPtr->PHRQ_calloc(size_t (max_length + 1), sizeof(char)); + if (token == NULL) PhreeqcPtr->malloc_error(); + + std::string std_num; + { + sprintf(token, "%*.*e", length, width, nmbr); + std_num = token; + } + + // set function value + n.UU.sval = (char *) PhreeqcPtr->PHRQ_calloc(std_num.size() + 2, sizeof(char)); + if (n.UU.sval == NULL) + PhreeqcPtr->malloc_error(); + strcpy(n.UU.sval, std_num.c_str()); + n.stringval = true; + + // free work space + PhreeqcPtr->free_check_null(token); + } + break; + case tokeq_frac: + case tokequiv_frac: + { + // left parenthesis + require(toklp, LINK); + + // species name + std::string species_name(stringfactor(STR1, LINK)); + + require(tokcomma, LINK); + + // equivalents + count_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || count_varrec->stringvar != 0) + snerr(": Cannot find equivalents variable"); + + LINK->t = LINK->t->next; + require(tokcomma, LINK); + + // exchange or surface element + varrec *elt_varrec = NULL; + elt_varrec = LINK->t->UU.vp; + if (LINK->t->kind != tokvar || elt_varrec->stringvar != 1) + snerr(": Cannot find element string variable"); + free_dim_stringvar(elt_varrec); + *elt_varrec->UU.U1.sval = (char *) PhreeqcPtr->free_check_null(*elt_varrec->UU.U1.sval); + + // right parenthesis + LINK->t = LINK->t->next; + require(tokrp, LINK); + + // Make work space + //int max_length = length < 256 ? 256 : length; + //char *token = (char *) PhreeqcPtr->PHRQ_calloc(size_t (max_length + 1), sizeof(char)); + //if (token == NULL) PhreeqcPtr->malloc_error(); + + // set function value + LDBLE eq; + std::string elt_name; + + // return equivalent fraction + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->equivalent_fraction(species_name.c_str(), &eq, elt_name); + + // set equivalents + *count_varrec->UU.U0.val = (parse_all) ? 1 : eq; + + // set element name + size_t l = elt_name.size(); + l = l < 256 ? 256 : l + 1; + char * token = (char *) PhreeqcPtr->PHRQ_malloc( l * sizeof(char)); + strcpy(token, elt_name.c_str()); + *elt_varrec->UU.U1.sval = token; + } + break; + case tokcallback: + { + double x1, x2; + char * str; + + // left parenthesis + require(toklp, LINK); + + // first double arugument + x1 = realfactor(LINK); + require(tokcomma, LINK); + + // second double arugument + x2 = realfactor(LINK); + require(tokcomma, LINK); + + // string arugument + str = strexpr(LINK); + + require(tokrp, LINK); + + // call callback Basic function + + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->basic_callback(x1, x2, str); + + } + break; + + case toksa_declercq: + { + double type, sa, d, m, m0, gfw; + + // left parenthesis + require(toklp, LINK); + + // first double arugument, type + type = realfactor(LINK); + require(tokcomma, LINK); + + // second double arugument, Sa + sa = realfactor(LINK); + require(tokcomma, LINK); + + // third double arugument, Sa + d = realfactor(LINK); + require(tokcomma, LINK); + + // fourth double arugument, m + m = realfactor(LINK); + require(tokcomma, LINK); + + // fifth double arugument, m0 + m0 = realfactor(LINK); + require(tokcomma, LINK); + + // sixth double arugument, gfw + gfw = realfactor(LINK); + require(tokrp, LINK); + + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->sa_declercq(type, sa, d, m, m0, gfw); + } + break; + + case tokdiff_c: + { + const char * str = stringfactor(STR1, LINK); + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->diff_c(str); + } + break; + + case toksetdiff_c: + { + double d; + + require(toklp, LINK); + + const char * str = stringfactor(STR1, LINK); + require(tokcomma, LINK); + + // double arugument + d = realexpr(LINK); + require(tokrp, LINK); + + n.UU.val = (parse_all) ? 1 : PhreeqcPtr->setdiff_c(str, d); + + //PhreeqcPtr->PHRQ_free((void *) str); + } + break; + case tokval: + l_s = strfactor(LINK); + tok1 = LINK->t; + parse(l_s, &LINK->t); + tok = LINK->t; + if (tok == NULL) + n.UU.val = 0.0; + else + n = expr(LINK); + disposetokens(&tok); + LINK->t = tok1; + PhreeqcPtr->PHRQ_free(l_s); + break; + + case tokchr_: + n.stringval = true; + n.UU.sval = (char *) PhreeqcPtr->PHRQ_calloc(256, sizeof(char)); + if (n.UU.sval == NULL) + PhreeqcPtr->malloc_error(); + strcpy(n.UU.sval, " "); + n.UU.sval[0] = (char) intfactor(LINK); + break; + + case tokeol_: + n.stringval = true; + n.UU.sval = (char *) PhreeqcPtr->PHRQ_calloc(256, sizeof(char)); + if (n.UU.sval == NULL) + PhreeqcPtr->malloc_error(); + strcpy(n.UU.sval, "\n"); + break; + + case tokasc: + l_s = strfactor(LINK); + if (*l_s == '\0') + n.UU.val = 0.0; + else + n.UU.val = l_s[0]; + PhreeqcPtr->PHRQ_free(l_s); + break; + + case tokmid_: + n.stringval = true; + require(toklp, LINK); + n.UU.sval = strexpr(LINK); + require(tokcomma, LINK); + i = intexpr(LINK); + if (i < 1) + i = 1; + j = (int) strlen(n.UU.sval); + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + LINK->t = LINK->t->next; + j = intexpr(LINK); + } + { + std::string str = n.UU.sval; + str = str.substr(i - 1, j); + strcpy(n.UU.sval, str.c_str()); + } + require(tokrp, LINK); + break; + case toklen: + l_s = strfactor(LINK); + n.UU.val = (double) strlen(l_s); + PhreeqcPtr->PHRQ_free(l_s); + break; + + case tokpeek: +/* p2c: basic.p, line 1029: Note: Range checking is OFF [216] */ + if (parse_all) + { + intfactor(LINK); + n.UU.val = 1.0; + } + else + { + trick.i = intfactor(LINK); + n.UU.val = *trick.c; + } +/* p2c: basic.p, line 1032: Note: Range checking is ON [216] */ + break; + + default: + snerr(": missing \" or ("); + break; + } + s_v.subscripts = (int *) PhreeqcPtr->free_check_null(s_v.subscripts); + return n; +} + +valrec PBasic:: +upexpr(struct LOC_exec * LINK) +{ + valrec n, n2; + + n = factor(LINK); + while (LINK->t != NULL && LINK->t->kind == tokup) + { + if (n.stringval) + tmerr(": not a number before ^"); + LINK->t = LINK->t->next; + n2 = upexpr(LINK); + if (n2.stringval) + tmerr(": not a number after ^"); + if (n.UU.val >= 0) + { + if (n.UU.val > 0) + { + n.UU.val = exp(n2.UU.val * log(n.UU.val)); + } + continue; + } + if (n2.UU.val != (long) n2.UU.val) + { + tmerr(": negative number cannot be raised to a fractional power."); + } + else + { + n.UU.val = exp(n2.UU.val * log(-n.UU.val)); + if (((long) n2.UU.val) & 1) + n.UU.val = -n.UU.val; + } + } + return n; +} + +valrec PBasic:: +term(struct LOC_exec * LINK) +{ + valrec n, n2; + + int k; + + n = upexpr(LINK); + while (LINK->t != NULL && (unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & ((1L << ((long) toktimes)) | + (1L << ((long) tokdiv)) | (1L << + ((long) tokmod)))) != 0) + { + k = LINK->t->kind; + LINK->t = LINK->t->next; + n2 = upexpr(LINK); + if (n.stringval || n2.stringval) + tmerr(": found char, but need a number for * or /"); + if (k == tokmod) + { + /* n.UU.val = (long)floor(n.UU.val + 0.5) % (long)floor(n2.UU.val + 0.5); */ + if (n.UU.val != 0) + { + n.UU.val = + fabs(n.UU.val) / n.UU.val * fmod(fabs(n.UU.val) + + 1e-14, n2.UU.val); + } + else + { + n.UU.val = 0; + } +/* p2c: basic.p, line 1078: + * Note: Using % for possibly-negative arguments [317] */ + } + else if (k == toktimes) + n.UU.val *= n2.UU.val; + else if (n2.UU.val != 0) + { + n.UU.val /= n2.UU.val; + } + else + { + if (!parse_all) + { + char * error_string = PhreeqcPtr->sformatf( "Zero divide in BASIC line\n %ld %s.\nValue set to zero.", stmtline->num, stmtline->inbuf); + PhreeqcPtr->warning_msg(error_string); + } + n.UU.val = 0; + } + } + return n; +} + +valrec PBasic:: +sexpr(struct LOC_exec * LINK) +{ + valrec n, n2; + + int k, m; + + n = term(LINK); + while (LINK->t != NULL && (unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) tokplus)) | (1L << ((long) tokminus)))) != 0) + { + k = LINK->t->kind; + LINK->t = LINK->t->next; + n2 = term(LINK); + if (n.stringval != n2.stringval) + tmerr(": found char, but need a number for + or - "); + if (k == tokplus) + { + if (n.stringval) + { + m = 1; + if (n.UU.sval) + { + m += (int) strlen(n.UU.sval); + } + if (n2.UU.sval) + { + m += (int) strlen(n2.UU.sval); + } + //m = (int) strlen(n.UU.sval) + (int) strlen(n2.UU.sval) + 1; + if (m < 256) + m = 256; + + n.UU.sval = (char *) PhreeqcPtr->PHRQ_realloc(n.UU.sval, (size_t) m * sizeof(char)); + if (n.UU.sval == NULL) + { + PhreeqcPtr->malloc_error(); + } + else + { + if (n2.UU.sval) + { + strcat(n.UU.sval, n2.UU.sval); + PhreeqcPtr->PHRQ_free(n2.UU.sval); + } + } + } + else + n.UU.val += n2.UU.val; + } + else + { + if (n.stringval) + tmerr(": found char, but need a number for - "); + else + n.UU.val -= n2.UU.val; + } + } + return n; +} + +valrec PBasic:: +relexpr(struct LOC_exec * LINK) +{ + valrec n, n2; + + bool f; + int k; + + n = sexpr(LINK); + while (LINK->t != NULL && (unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) tokne + 1)) - (1L << ((long) tokeq)))) != 0) + { + k = LINK->t->kind; + LINK->t = LINK->t->next; + n2 = sexpr(LINK); + if (n.stringval != n2.stringval) + tmerr(""); + if (n.stringval) + { + f = (bool) ((!strcmp(n.UU.sval, n2.UU.sval) + && (unsigned long) k < 32 + && ((1L << ((long) k)) & + ((1L << ((long) tokeq)) | + (1L << ((long) tokge)) | (1L << + ((long) tokle)))) + != 0) || (strcmp(n.UU.sval, n2.UU.sval) < 0 + && (unsigned long) k < 32 + && ((1L << ((long) k)) & + ((1L << ((long) toklt)) | + (1L << ((long) tokle)) | (1L << + ((long) + tokne)))) + != 0) + || (strcmp(n.UU.sval, n2.UU.sval) > 0 + && (unsigned long) k < 32 + && ((1L << ((long) k)) & + ((1L << ((long) tokgt)) | + (1L << ((long) tokge)) | (1L << + ((long) + tokne)))) != + 0)); + /* p2c: basic.p, line 2175: Note: + * Line breaker spent 0.0+1.00 seconds, 5000 tries on line 1518 [251] */ + PhreeqcPtr->PHRQ_free(n.UU.sval); + PhreeqcPtr->PHRQ_free(n2.UU.sval); + } + else + f = (bool) ((n.UU.val == n2.UU.val && (unsigned long) k < 32 && + ((1L << ((long) k)) & ((1L << ((long) tokeq)) | + (1L << ((long) tokge)) | + (1L << ((long) tokle)))) != + 0) || (n.UU.val < n2.UU.val + && (unsigned long) k < 32 + && ((1L << ((long) k)) & + ((1L << ((long) toklt)) | + (1L << ((long) tokle)) | (1L << + ((long) + tokne)))) + != 0) || (n.UU.val > n2.UU.val + && (unsigned long) k < 32 + && ((1L << ((long) k)) & + ((1L << ((long) tokgt)) | + (1L << ((long) tokge)) | (1L + << + ((long) tokne)))) != 0)); + /* p2c: basic.p, line 2175: Note: + * Line breaker spent 0.0+2.00 seconds, 5000 tries on line 1532 [251] */ + n.stringval = false; + n.UU.val = f; + } + return n; +} + +valrec PBasic:: +andexpr(struct LOC_exec * LINK) +{ + valrec n, n2; + + n = relexpr(LINK); + while (LINK->t != NULL && LINK->t->kind == tokand) + { + LINK->t = LINK->t->next; + n2 = relexpr(LINK); + if (n.stringval || n2.stringval) + tmerr(""); + n.UU.val = ((long) n.UU.val) & ((long) n2.UU.val); + } + return n; +} + +valrec PBasic:: +expr(struct LOC_exec * LINK) +{ + valrec n, n2; + + int k; + + n = andexpr(LINK); + while (LINK->t != NULL && (unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) tokor)) | (1L << ((long) tokxor)))) != 0) + { + k = LINK->t->kind; + LINK->t = LINK->t->next; + n2 = andexpr(LINK); + if (n.stringval || n2.stringval) + tmerr(""); + if (k == tokor) + n.UU.val = ((long) n.UU.val) | ((long) n2.UU.val); + else + n.UU.val = ((long) n.UU.val) ^ ((long) n2.UU.val); + } + return n; +} + +void PBasic:: +checkextra(struct LOC_exec *LINK) +{ + if (LINK->t != NULL) + { + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_EXTRA; + } + errormsg("Extra information on line"); + } +} + +bool PBasic:: +iseos(struct LOC_exec *LINK) +{ + return ((bool) (LINK->t == NULL || LINK->t->kind == (long) tokelse || + LINK->t->kind == (long) tokcolon)); +} + +void PBasic:: +skiptoeos(struct LOC_exec *LINK) +{ + while (!iseos(LINK)) + LINK->t = LINK->t->next; +} + +linerec * PBasic:: +findline(long n) +{ + linerec *l; + + l = linebase; + while (l != NULL && l->num != n) + l = l->next; + return l; +} +#ifdef SKIP +linerec * PBasic:: +mustfindline(long n) +{ + linerec *l; + + l = findline(n); + if (l == NULL) + { + char * error_string = PhreeqcPtr->sformatf( "Undefined line %ld", n); + errormsg(error_string); + } + return l; +} +#endif +linerec * PBasic:: +mustfindline(long n) +{ + linerec *l; + + l = findline(n); + if (phreeqci_gui) + { + if (parse_whole_program) + { + if (l == NULL) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_UNDEF_LINE; + char * error_string = PhreeqcPtr->sformatf( "Undefined line %ld", n); + errormsg(error_string); + } + } + } + else + { + if (l == NULL) + { + char * error_string = PhreeqcPtr->sformatf( "Undefined line %ld", n); + errormsg(error_string); + } + } + return l; +} +void PBasic:: +cmdend(struct LOC_exec *LINK) +{ + stmtline = NULL; + LINK->t = NULL; +} + +void PBasic:: +cmdnew(struct LOC_exec *LINK) +{ + void *p; + int i, k; + + cmdend(LINK); + clearloops(); + restoredata(); + while (linebase != NULL) + { + p = linebase->next; + disposetokens(&linebase->txt); + PhreeqcPtr->PHRQ_free(linebase); + linebase = (linerec *) p; + } + while (varbase != NULL) + { + p = varbase->next; + if (varbase->stringvar) + { + if (varbase->numdims > 0) + { + k = 1; + for (i = 0; i < varbase->numdims; i++) + { + k = k * (varbase->dims[i]); + } + for (i = 0; i < k; i++) + { + PhreeqcPtr->free_check_null(varbase->UU.U1.sarr[i]); + } + PhreeqcPtr->free_check_null(varbase->UU.U1.sarr); + } + else if (*varbase->UU.U1.sval != NULL) + { + *varbase->UU.U1.sval = + (char *) PhreeqcPtr->free_check_null(*varbase->UU.U1.sval); + } + + } + else + { + PhreeqcPtr->free_check_null(varbase->UU.U0.arr); + varbase->UU.U0.arr = NULL; + } + PhreeqcPtr->PHRQ_free(varbase); + varbase = (varrec *) p; + } +} + +void PBasic:: +cmdlist(struct LOC_exec *LINK) +{ + linerec *l; + long n1, n2; + + do + { + n1 = 0; + n2 = LONG_MAX; + if (LINK->t != NULL && LINK->t->kind == toknum) + { + n1 = (long) LINK->t->UU.num; + LINK->t = LINK->t->next; + if (LINK->t == NULL || LINK->t->kind != tokminus) + n2 = n1; + } + if (LINK->t != NULL && LINK->t->kind == tokminus) + { + LINK->t = LINK->t->next; + if (LINK->t != NULL && LINK->t->kind == toknum) + { + n2 = (long) LINK->t->UU.num; + LINK->t = LINK->t->next; + } + else + n2 = LONG_MAX; + } + l = linebase; + while (l != NULL && l->num <= n2) + { + if (l->num >= n1) + { + /* printf("%ld ", l->num); */ + /* listtokens(stdout, l->txt); */ + /* putchar('\n'); */ + output_msg(PhreeqcPtr->sformatf("%ld ", l->num)); + listtokens(NULL, l->txt); + output_msg("\n"); + } + l = l->next; + } + if (!iseos(LINK)) + require(tokcomma, LINK); + } + while (!iseos(LINK)); +} + +void PBasic:: +cmdload(bool merging, char * name, struct LOC_exec *LINK) +{ + FILE *f; + tokenrec *l_buf; + char STR1[256] = {0}; + char *TEMP; + + f = NULL; + if (!merging) + cmdnew(LINK); + if (f != NULL) + { + sprintf(STR1, "%s.TEXT", name); + f = freopen(STR1, "r", f); + } + else + { + sprintf(STR1, "%s.TEXT", name); + f = fopen(STR1, "r"); + } + if (f == NULL) + { + _EscIO(FileNotFound); + return; + } + while (fgets(inbuf, 256, f) != NULL) + { + TEMP = strchr(inbuf, '\n'); + if (TEMP != NULL) + *TEMP = 0; + parseinput(&l_buf); + if (curline == 0) + { + /* printf("Bad line in file\n"); */ + output_msg("Bad line in file\n"); + disposetokens(&l_buf); + } + } + if (f != NULL) + fclose(f); + f = NULL; + if (f != NULL) + fclose(f); +} + +void PBasic:: +cmdrun(struct LOC_exec *LINK) +{ + linerec *l; + long i; + char *l_s; + + l_s = (char *) PhreeqcPtr->PHRQ_calloc(PhreeqcPtr->max_line, sizeof(char)); + if (l_s == NULL) + PhreeqcPtr->malloc_error(); + + l = linebase; + if (!iseos(LINK)) + { + if (LINK->t->kind == toknum) + /*l = mustfindline(intexpr(LINK), LINK); */ + l = mustfindline(intexpr(LINK)); + else + { + stringexpr(l_s, LINK); + i = 0; + if (!iseos(LINK)) + { + require(tokcomma, LINK); + i = intexpr(LINK); + } + checkextra(LINK); + cmdload(false, l_s, LINK); + if (i == 0) + l = linebase; + else + l = mustfindline(i); + } + } + stmtline = l; + LINK->gotoflag = true; + clearvars(); + clearloops(); + restoredata(); + PhreeqcPtr->free_check_null(l_s); + return; +} + +/* PhreeqcPtr->replace basic save command with transport of rate back to calc_kinetic_rate */ +void PBasic:: +cmdsave(struct LOC_exec *LINK) +{ + valrec n; + + while (!iseos(LINK)) + { + if ((unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) toksemi)) | (1L << ((long) tokcomma)))) != 0) + { + LINK->t = LINK->t->next; + continue; + } + n = expr(LINK); + if (n.stringval) + { + snerr(": in SAVE command"); + } + else + { + PhreeqcPtr->rate_moles = n.UU.val; + } + } +} + +void PBasic:: +cmdput(struct LOC_exec *LINK) +{ + int j; + struct save_values s_v; + + s_v.count_subscripts = 0; + s_v.subscripts = (int *) PhreeqcPtr->PHRQ_malloc(sizeof(int)); + + /* get parentheses */ + require(toklp, LINK); + + /* get first argumen */ + s_v.value = realexpr(LINK); + + for (;;) + { + if (LINK->t != NULL && LINK->t->kind == tokcomma) + { + LINK->t = LINK->t->next; + j = intexpr(LINK); + s_v.count_subscripts++; + s_v.subscripts = + (int *) PhreeqcPtr->PHRQ_realloc(s_v.subscripts, + (size_t) s_v.count_subscripts * + sizeof(int)); + if (s_v.subscripts == NULL) + PhreeqcPtr->malloc_error(); + s_v.subscripts[s_v.count_subscripts - 1] = j; + } + else + { + /* get right parentheses */ + require(tokrp, LINK); + break; + } + } + if (!parse_all) + { + PhreeqcPtr->save_values_store(&s_v); + } + s_v.subscripts = (int *) PhreeqcPtr->free_check_null(s_v.subscripts); +} + +void PBasic:: +cmdchange_por(struct LOC_exec *LINK) +{ + int j; + LDBLE TEMP; + require(toklp, LINK); + /* get new porosity */ + TEMP = realexpr(LINK); + require(tokcomma, LINK); + /* get cell_no */ + j = intexpr(LINK); + require(tokrp, LINK); + if (j > 0 && j <= PhreeqcPtr->count_cells * (1 + PhreeqcPtr->stag_data->count_stag) + 1 + && j != PhreeqcPtr->count_cells + 1) + PhreeqcPtr->cell_data[j].por = TEMP; +} + +void PBasic:: +cmdchange_surf(struct LOC_exec *LINK) +{ +/* + change_surf("Hfo", 0.3, "Sfo", 0, 5 ) + (old_name, fraction, new_name, new_Dw, cell_no) + */ + char *c1; + int count; + + PhreeqcPtr->change_surf_count += 1; + count = PhreeqcPtr->change_surf_count; + if (PhreeqcPtr->change_surf[count - 1].next == FALSE) + PhreeqcPtr->change_surf = PhreeqcPtr->change_surf_alloc(count + 1); + + require(toklp, LINK); + /* get surface component name (change affects all comps of the same charge structure) */ + c1 = strexpr(LINK); + PhreeqcPtr->change_surf[count - 1].comp_name = PhreeqcPtr->string_hsave(c1); + PhreeqcPtr->PHRQ_free(c1); + require(tokcomma, LINK); + /* get fraction of comp to change */ + PhreeqcPtr->change_surf[count - 1].fraction = realexpr(LINK); + require(tokcomma, LINK); + /* get new surface component name */ + c1 = strexpr(LINK); + PhreeqcPtr->change_surf[count - 1].new_comp_name = PhreeqcPtr->string_hsave(c1); + PhreeqcPtr->PHRQ_free(c1); + require(tokcomma, LINK); + /* get new Dw (no transport if 0) */ + PhreeqcPtr->change_surf[count - 1].new_Dw = realexpr(LINK); + require(tokcomma, LINK); + /* get cell_no */ + PhreeqcPtr->change_surf[count - 1].cell_no = intexpr(LINK); + require(tokrp, LINK); + + if (PhreeqcPtr->change_surf->cell_no == 0 || PhreeqcPtr->change_surf->cell_no == PhreeqcPtr->count_cells + 1) + PhreeqcPtr->change_surf[count - 1].cell_no = -99; +} + +void PBasic:: +cmdbye(void) +{ + exitflag = true; +} + +void PBasic:: +cmddel(struct LOC_exec *LINK) +{ + linerec *l, *l0, *l1; + long n1, n2; + + do + { + if (iseos(LINK)) + snerr(": no variable name after del"); + n1 = 0; + n2 = LONG_MAX; + if (LINK->t != NULL && LINK->t->kind == toknum) + { + n1 = (long) LINK->t->UU.num; + LINK->t = LINK->t->next; + if (LINK->t == NULL || LINK->t->kind != tokminus) + n2 = n1; + } + if (LINK->t != NULL && LINK->t->kind == tokminus) + { + LINK->t = LINK->t->next; + if (LINK->t != NULL && LINK->t->kind == toknum) + { + n2 = (long) LINK->t->UU.num; + LINK->t = LINK->t->next; + } + else + n2 = LONG_MAX; + } + l = linebase; + l0 = NULL; + while (l != NULL && l->num <= n2) + { + l1 = l->next; + if (l->num >= n1) + { + if (l == stmtline) + { + cmdend(LINK); + clearloops(); + restoredata(); + } + if (l0 == NULL) + linebase = l->next; + else + l0->next = l->next; + disposetokens(&l->txt); + PhreeqcPtr->PHRQ_free(l); + } + else + l0 = l; + l = l1; + } + if (!iseos(LINK)) + require(tokcomma, LINK); + } + while (!iseos(LINK)); +} + +void PBasic:: +cmdrenum(struct LOC_exec *LINK) +{ + linerec *l, *l1; + tokenrec *tok; + long lnum, step; + + lnum = 10; + step = 10; + if (!iseos(LINK)) + { + lnum = intexpr(LINK); + if (!iseos(LINK)) + { + require(tokcomma, LINK); + step = intexpr(LINK); + } + } + l = linebase; + if (l == NULL) + return; + while (l != NULL) + { + l->num2 = lnum; + lnum += step; + l = l->next; + } + l = linebase; + do + { + tok = l->txt; + do + { + if (tok->kind == (long) tokdel || tok->kind == (long) tokrestore + || tok->kind == (long) toklist || tok->kind == (long) tokrun + || tok->kind == (long) tokelse || tok->kind == (long) tokthen + || tok->kind == (long) tokgosub + || tok->kind == (long) tokgoto) + { + while (tok->next != NULL && tok->next->kind == toknum) + { + tok = tok->next; + lnum = (long) floor(tok->UU.num + 0.5); + l1 = linebase; + while (l1 != NULL && l1->num != lnum) + l1 = l1->next; + if (l1 == NULL) + { + /* printf("Undefined line %ld in line %ld\n", lnum, + l->num2); */ + output_msg(PhreeqcPtr->sformatf("Undefined line %ld in line %ld\n", lnum, l->num2)); + } + else + { +#if defined(PHREEQCI_GUI) + if (phreeqci_gui) + { + _snprintf(tok->sz_num, tok->n_sz, "%ld", l1->num2); + } +#endif + tok->UU.num = l1->num2; + } + if (tok->next != NULL && tok->next->kind == tokcomma) + tok = tok->next; + } + } + tok = tok->next; + } + while (tok != NULL); + l = l->next; + } + while (l != NULL); + l = linebase; + while (l != NULL) + { + l->num = l->num2; + l = l->next; + } +} + +void PBasic:: +cmdprint(struct LOC_exec *LINK) +{ + bool semiflag; + valrec n; + + char STR1[256] = {0}; + + semiflag = false; + while (!iseos(LINK)) + { + semiflag = false; + if ((unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) toksemi)) | (1L << ((long) tokcomma)))) != 0) + { + semiflag = true; + LINK->t = LINK->t->next; + continue; + } + n = expr(LINK); + if (n.stringval) + { +/* fputs(n.UU.sval, stdout); */ + output_msg(PhreeqcPtr->sformatf("%s ", n.UU.sval)); + PhreeqcPtr->PHRQ_free(n.UU.sval); + } + else +/* printf("%s ", numtostr(STR1, n.UU.val)); */ + output_msg(PhreeqcPtr->sformatf("%s ", numtostr(STR1, n.UU.val))); + } + if (!semiflag) +/* putchar('\n');*/ + output_msg("\n"); +} + +void PBasic:: +cmdpunch(struct LOC_exec *LINK) +{ + valrec n; + + /* char STR1[256]; */ + + while (!iseos(LINK)) + { + if ((unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) toksemi)) | (1L << ((long) tokcomma)))) != 0) + { + LINK->t = LINK->t->next; + continue; + } + n = expr(LINK); + bool temp_high_precision = (PhreeqcPtr->current_selected_output != NULL) ? + PhreeqcPtr->current_selected_output->Get_high_precision() : + PhreeqcPtr->high_precision; + if (n.stringval) + { +/* fputs(n.UU.sval, stdout); */ + + if (!temp_high_precision) + { + if (strlen(n.UU.sval) <= 12) + { + PhreeqcPtr->fpunchf_user(PhreeqcPtr->n_user_punch_index, "%12.12s\t", n.UU.sval); + } + else + { + PhreeqcPtr->fpunchf_user(PhreeqcPtr->n_user_punch_index, "%s\t", n.UU.sval); + } + } + else + { + if (strlen(n.UU.sval) <= 20) + { + PhreeqcPtr->fpunchf_user(PhreeqcPtr->n_user_punch_index, "%20.20s\t", n.UU.sval); + } + else + { + PhreeqcPtr->fpunchf_user(PhreeqcPtr->n_user_punch_index, "%s\t", n.UU.sval); + } + } + PhreeqcPtr->PHRQ_free(n.UU.sval); + } + else if (!temp_high_precision) + { + PhreeqcPtr->fpunchf_user(PhreeqcPtr->n_user_punch_index, "%12.4e\t", (double) n.UU.val); + } + else + { + PhreeqcPtr->fpunchf_user(PhreeqcPtr->n_user_punch_index, "%20.12e\t", (double) n.UU.val); + } + ++PhreeqcPtr->n_user_punch_index; + } +} + +#if defined PHREEQ98 +void PBasic:: +cmdgraph_x(struct LOC_exec *LINK) +{ + bool semiflag; + valrec n; + + char STR1[256]; + semiflag = false; + while (!iseos(LINK)) + { + semiflag = false; + if ((unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) toksemi)) | (1L << ((long) tokcomma)))) != 0) + { + semiflag = true; + LINK->t = LINK->t->next; + continue; + } + n = expr(LINK); + if (colnr == 0) + { + rownr++; + } + if (n.stringval) + { +/* fputs(n.UU.sval, stdout); */ + GridChar(n.UU.sval, "x"); + PhreeqcPtr->PHRQ_free(n.UU.sval); + } + else + GridChar(numtostr(STR1, n.UU.val), "x"); + colnr++; + } +} + +void PBasic:: +cmdgraph_y(struct LOC_exec *LINK) +{ + bool semiflag; + valrec n; + + char STR1[256]; + semiflag = false; + while (!iseos(LINK)) + { + semiflag = false; + if ((unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) toksemi)) | (1L << ((long) tokcomma)))) != 0) + { + semiflag = true; + LINK->t = LINK->t->next; + continue; + } + n = expr(LINK); + if (colnr == 0) + { + rownr++; + } + if (n.stringval) + { +/* fputs(n.UU.sval, stdout); */ + GridChar(n.UU.sval, "y"); + PhreeqcPtr->PHRQ_free(n.UU.sval); + } + else + GridChar(numtostr(STR1, n.UU.val), "y"); + colnr++; + } +} + +void PBasic:: +cmdgraph_sy(struct LOC_exec *LINK) +{ + bool semiflag; + valrec n; + + char STR1[256]; + semiflag = false; + while (!iseos(LINK)) + { + semiflag = false; + if ((unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) toksemi)) | (1L << ((long) tokcomma)))) != 0) + { + semiflag = true; + LINK->t = LINK->t->next; + continue; + } + n = expr(LINK); + if (colnr == 0) + { + rownr++; + } + if (n.stringval) + { +/* fputs(n.UU.sval, stdout); */ + GridChar(n.UU.sval, "s"); + PhreeqcPtr->PHRQ_free(n.UU.sval); + } + else + GridChar(numtostr(STR1, n.UU.val), "s"); + colnr++; + } +} +#endif + +void PBasic:: +cmdlet(bool implied, struct LOC_exec *LINK) +{ + varrec *v; + char *old, *mynew; + LDBLE d_value; + LDBLE *target; + char **starget; + target = NULL; + starget = NULL; + if (implied) + LINK->t = stmttok; + v = findvar(LINK); + if (v->stringvar) + { + starget = v->UU.U1.sval; + } + else + { + target = v->UU.U0.val; + } + require(tokeq, LINK); + if (!v->stringvar) + { + /* in case array is used on right hand side */ + d_value = realexpr(LINK); + v->UU.U0.val = target; + *v->UU.U0.val = d_value; + /* *v->UU.U0.val = realexpr(LINK); */ + return; + } + mynew = strexpr(LINK); + v->UU.U1.sval = starget; + old = *v->UU.U1.sval; + *v->UU.U1.sval = mynew; + if (old != NULL) + PhreeqcPtr->PHRQ_free(old); +} + +void PBasic:: +cmdgoto(struct LOC_exec *LINK) +{ + stmtline = mustfindline(intexpr(LINK)); + LINK->t = NULL; + LINK->gotoflag = true; +} + +void PBasic:: +cmdif(struct LOC_exec *LINK) +{ + LDBLE n; + long i; + + n = realexpr(LINK); + require(tokthen, LINK); + if (n == 0) + { + i = 0; + do + { + if (LINK->t != NULL) + { + if (LINK->t->kind == tokif) + i++; + if (LINK->t->kind == tokelse) + i--; + LINK->t = LINK->t->next; + } + } + while (LINK->t != NULL && i >= 0); + } + if (LINK->t != NULL && LINK->t->kind == toknum) + cmdgoto(LINK); + else + LINK->elseflag = true; +} + +void PBasic:: +cmdelse(struct LOC_exec *LINK) +{ + LINK->t = NULL; +} + +bool PBasic:: +skiploop(int up, int dn, struct LOC_exec *LINK) +{ + bool Result; + long i; + linerec *saveline; + + saveline = stmtline; + i = 0; + do + { + while (LINK->t == NULL) + { + if (stmtline == NULL || stmtline->next == NULL) + { + Result = false; + stmtline = saveline; + goto _L1; + } + stmtline = stmtline->next; + LINK->t = stmtline->txt; + } + if (LINK->t->kind == up) + i++; + if (LINK->t->kind == dn) + i--; + LINK->t = LINK->t->next; + } + while (i >= 0); + Result = true; + _L1: + return Result; +} + +void PBasic:: +cmdfor(struct LOC_exec *LINK) +{ + looprec *l, lr; + linerec *saveline; + long i, j; + + lr.UU.U0.vp = findvar(LINK); + if (lr.UU.U0.vp->stringvar) + snerr(": error in FOR command"); + require(tokeq, LINK); + *lr.UU.U0.vp->UU.U0.val = realexpr(LINK); + require(tokto, LINK); + lr.UU.U0.max = realexpr(LINK); + if (LINK->t != NULL && LINK->t->kind == tokstep) + { + LINK->t = LINK->t->next; + lr.UU.U0.step = realexpr(LINK); + } + else + lr.UU.U0.step = 1.0; + lr.homeline = stmtline; + lr.hometok = LINK->t; + lr.kind = forloop; + lr.next = loopbase; + if ((lr.UU.U0.step >= 0 && *lr.UU.U0.vp->UU.U0.val > lr.UU.U0.max) || + (lr.UU.U0.step <= 0 && *lr.UU.U0.vp->UU.U0.val < lr.UU.U0.max)) + { + saveline = stmtline; + i = 0; + j = 0; + do + { + while (LINK->t == NULL) + { + if (stmtline == NULL || stmtline->next == NULL) + { + stmtline = saveline; + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_FOR_WO_NEXT; + } + errormsg("FOR without NEXT"); + } + stmtline = stmtline->next; + LINK->t = stmtline->txt; + } + if (LINK->t->kind == tokfor) + { + if (LINK->t->next != NULL && LINK->t->next->kind == tokvar && + LINK->t->next->UU.vp == lr.UU.U0.vp) + j++; + else + i++; + } + if (LINK->t->kind == toknext) + { + if (LINK->t->next != NULL && LINK->t->next->kind == tokvar && + LINK->t->next->UU.vp == lr.UU.U0.vp) + j--; + else + i--; + } + LINK->t = LINK->t->next; + } + while (i >= 0 && j >= 0); + skiptoeos(LINK); + return; + } + l = (looprec *) PhreeqcPtr->PHRQ_calloc(1, sizeof(looprec)); + if (l == NULL) + { + PhreeqcPtr->malloc_error(); + } + else + { + *l = lr; + loopbase = l; + } +} + +void PBasic:: +cmdnext(struct LOC_exec *LINK) +{ + varrec *v; + bool found; + looprec *l, *WITH; + + if (!iseos(LINK)) + v = findvar(LINK); + else + v = NULL; + do + { + if (loopbase == NULL || loopbase->kind == gosubloop) + { + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_NEXT_WO_FOR; + } + errormsg("NEXT without FOR"); + } + found = (bool) (loopbase->kind == forloop && + (v == NULL || loopbase->UU.U0.vp == v)); + if (!found) + { + l = loopbase->next; + PhreeqcPtr->PHRQ_free(loopbase); + loopbase = l; + } + } + while (!found); + WITH = loopbase; + *WITH->UU.U0.vp->UU.U0.val += WITH->UU.U0.step; + if ((WITH->UU.U0.step < 0 + || *WITH->UU.U0.vp->UU.U0.val <= WITH->UU.U0.max) + && (WITH->UU.U0.step > 0 + || *WITH->UU.U0.vp->UU.U0.val >= WITH->UU.U0.max)) + { + stmtline = WITH->homeline; + LINK->t = WITH->hometok; + return; + } + l = loopbase->next; + PhreeqcPtr->PHRQ_free(loopbase); + loopbase = l; +} + +void PBasic:: +cmdwhile(struct LOC_exec *LINK) +{ + looprec *l; + + l = (looprec *) PhreeqcPtr->PHRQ_calloc(1, sizeof(looprec)); + if (l == NULL) + { + PhreeqcPtr->malloc_error(); + return; + } + l->next = loopbase; + loopbase = l; + l->kind = whileloop; + l->homeline = stmtline; + l->hometok = LINK->t; + if (iseos(LINK)) + return; + if (realexpr(LINK) != 0) + return; + if (phreeqci_gui) + { + if (parse_whole_program == TRUE) + { + if (!skiploop(tokwhile, tokwend, LINK)) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_WHILE_WO_WEND; + errormsg("WHILE without WEND"); + } + l = loopbase->next; + PhreeqcPtr->PHRQ_free(loopbase); + loopbase = l; + skiptoeos(LINK); + } + } + else + { + if (!skiploop(tokwhile, tokwend, LINK)) + { + errormsg("WHILE without WEND"); + } + l = loopbase->next; + PhreeqcPtr->PHRQ_free(loopbase); + loopbase = l; + skiptoeos(LINK); + } +} + +void PBasic:: +cmdwend(struct LOC_exec *LINK) +{ + tokenrec *tok; + linerec *tokline; + looprec *l; + bool found; + if (phreeqci_gui && !parse_whole_program) + { + return; + } + do + { + if (loopbase == NULL || loopbase->kind == gosubloop) + { + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_WEND_WO_WHILE; + } + errormsg("WEND without WHILE"); + } + found = (bool) (loopbase->kind == whileloop); + if (!found) + { + l = loopbase->next; + PhreeqcPtr->PHRQ_free(loopbase); + loopbase = l; + } + } + while (!found); + if (!iseos(LINK)) + { + if (realexpr(LINK) != 0) + found = false; + } + tok = LINK->t; + tokline = stmtline; + if (found) + { + stmtline = loopbase->homeline; + LINK->t = loopbase->hometok; + if (!iseos(LINK)) + { + if (realexpr(LINK) == 0) + found = false; + } + } + if (found) + return; + LINK->t = tok; + stmtline = tokline; + l = loopbase->next; + PhreeqcPtr->PHRQ_free(loopbase); + loopbase = l; +} + +void PBasic:: +cmdgosub(struct LOC_exec *LINK) +{ + looprec *l; + + l = (looprec *) PhreeqcPtr->PHRQ_calloc(1, sizeof(looprec)); + if (l == NULL) + { + PhreeqcPtr->malloc_error(); + return; + } + l->next = loopbase; + loopbase = l; + l->kind = gosubloop; + l->homeline = stmtline; + l->hometok = LINK->t; + cmdgoto(LINK); +} + +void PBasic:: +cmdreturn(struct LOC_exec *LINK) +{ + looprec *l; + bool found; + + if (phreeqci_gui && !parse_whole_program) + { + return; + } + + do + { + if (loopbase == NULL) + { + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_RETURN_WO_GOSUB; + } + errormsg("RETURN without GOSUB"); + } + found = (bool) (loopbase->kind == gosubloop); + if (!found) + { + l = loopbase->next; + PhreeqcPtr->PHRQ_free(loopbase); + loopbase = l; + } + } + while (!found); + stmtline = loopbase->homeline; + LINK->t = loopbase->hometok; + l = loopbase->next; + PhreeqcPtr->PHRQ_free(loopbase); + loopbase = l; + skiptoeos(LINK); +} + +void PBasic:: +cmdread(struct LOC_exec *LINK) +{ + varrec *v; + tokenrec *tok; + bool found; + + do + { + v = findvar(LINK); + tok = LINK->t; + LINK->t = datatok; + if (phreeqci_gui) + { + if (parse_whole_program) + { + if (dataline == NULL) + { + dataline = linebase; + LINK->t = dataline->txt; + } + if (LINK->t == NULL || LINK->t->kind != tokcomma) + { + do + { + while (LINK->t == NULL) + { + if (dataline == NULL || dataline->next == NULL) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_OUT_OF_DATA; + errormsg("Out of Data"); + } + dataline = dataline->next; + LINK->t = dataline->txt; + } + found = (bool) (LINK->t->kind == tokdata); + LINK->t = LINK->t->next; + } + while (!found || iseos(LINK)); + } + else + LINK->t = LINK->t->next; + if (v->stringvar) + { + if (*v->UU.U1.sval != NULL) + *v->UU.U1.sval = (char *) PhreeqcPtr->free_check_null(*v->UU.U1.sval); + *v->UU.U1.sval = strexpr(LINK); + } + else + *v->UU.U0.val = realexpr(LINK); + } + } + else + { + if (dataline == NULL) + { + dataline = linebase; + LINK->t = dataline->txt; + } + if (LINK->t == NULL || LINK->t->kind != tokcomma) + { + do + { + while (LINK->t == NULL) + { + if (dataline == NULL || dataline->next == NULL) + errormsg("Out of Data"); + dataline = dataline->next; + LINK->t = dataline->txt; + } + found = (bool) (LINK->t->kind == tokdata); + LINK->t = LINK->t->next; + } + while (!found || iseos(LINK)); + } + else + LINK->t = LINK->t->next; + if (v->stringvar) + { + if (*v->UU.U1.sval != NULL) + *v->UU.U1.sval = (char *) PhreeqcPtr->free_check_null(*v->UU.U1.sval); + *v->UU.U1.sval = strexpr(LINK); + } + else + *v->UU.U0.val = realexpr(LINK); + } + datatok = LINK->t; + LINK->t = tok; + if (!iseos(LINK)) + require(tokcomma, LINK); + + } + while (!iseos(LINK)); +} + +void PBasic:: +cmddata(struct LOC_exec *LINK) +{ + skiptoeos(LINK); +} + +void PBasic:: +cmdrestore(struct LOC_exec *LINK) +{ + if (iseos(LINK)) + restoredata(); + else + { + dataline = mustfindline(intexpr(LINK)); + if (phreeqci_gui) + { + if (parse_whole_program) + { + datatok = dataline->txt; + } + } + else + { + datatok = dataline->txt; + } + } +} + +void PBasic:: +cmdgotoxy(struct LOC_exec *LINK) +{ + intexpr(LINK); + require(tokcomma, LINK); +} + +void PBasic:: +cmdon(struct LOC_exec *LINK) +{ + long i; + looprec *l; + + i = intexpr(LINK); + if (LINK->t != NULL && LINK->t->kind == tokgosub) + { + l = (looprec *) PhreeqcPtr->PHRQ_calloc(1, sizeof(looprec)); + if (l == NULL) + { + PhreeqcPtr->malloc_error(); + } + else + { + l->next = loopbase; + loopbase = l; + l->kind = gosubloop; + l->homeline = stmtline; + l->hometok = LINK->t; + LINK->t = LINK->t->next; + } + } + else + require(tokgoto, LINK); + if (i < 1) + { + skiptoeos(LINK); + return; + } + while (i > 1 && !iseos(LINK)) + { + require(toknum, LINK); + if (!iseos(LINK)) + require(tokcomma, LINK); + i--; + } + if (!iseos(LINK)) + cmdgoto(LINK); +} + +void PBasic:: +cmddim(struct LOC_exec *LINK) +{ + long i, j, k; + varrec *v; + bool done; + + do + { + if (LINK->t == NULL || LINK->t->kind != tokvar) + snerr(": error in DIM command"); + v = LINK->t->UU.vp; + LINK->t = LINK->t->next; + if (v->numdims != 0) + { + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_ARRAY_ALREADY; + } + errormsg("Array already dimensioned before"); + } + j = 1; + i = 0; + require(toklp, LINK); + do + { + k = intexpr(LINK) + 1; + if (k < 1) + badsubscr(); + if (i >= maxdims) + badsubscr(); + i++; + v->dims[i - 1] = k; + j *= k; + done = (bool) (LINK->t != NULL && LINK->t->kind == tokrp); + if (!done) + require(tokcomma, LINK); + } + while (!done); + LINK->t = LINK->t->next; + v->numdims = (char) i; + if (v->stringvar) + { + v->UU.U1.sarr = (char **) PhreeqcPtr->PHRQ_malloc(j * sizeof(char *)); + if (!v->UU.U1.sarr) + { + PhreeqcPtr->malloc_error(); +#if !defined(R_SO) + exit(4); +#endif + } + if (v->UU.U1.sarr == NULL) + PhreeqcPtr->malloc_error(); + for (i = 0; i < j; i++) + v->UU.U1.sarr[i] = NULL; + } + else + { + v->UU.U0.arr = (LDBLE *) PhreeqcPtr->PHRQ_malloc(j * sizeof(LDBLE)); + if (v->UU.U0.arr == NULL) + { + PhreeqcPtr->malloc_error(); + } + else + { + for (i = 0; i < j; i++) + v->UU.U0.arr[i] = 0.0; + } + } + if (!iseos(LINK)) + require(tokcomma, LINK); + } + while (!iseos(LINK)); +} + +void PBasic:: +cmderase(struct LOC_exec *LINK) +{ + varrec *v = NULL; + do + { + if (LINK->t == NULL || LINK->t->kind != tokvar) + { + snerr(": error in DIM command"); + } + else + { + v = LINK->t->UU.vp; + LINK->t = LINK->t->next; + clearvar(v); + if (!iseos(LINK)) require(tokcomma, LINK); + } + } + while (!iseos(LINK)); +} + + +void PBasic:: +cmdpoke(struct LOC_exec *LINK) +{ + union + { + long i; + char *c; + } trick; + +/* p2c: basic.p, line 2073: Note: Range checking is OFF [216] */ + trick.i = intexpr(LINK); + require(tokcomma, LINK); + *trick.c = (char) intexpr(LINK); +/* p2c: basic.p, line 2077: Note: Range checking is ON [216] */ +} + +void PBasic:: +exec(void) +{ + struct LOC_exec V; + V.gotoflag = false; + V.elseflag = false; + V.t = NULL; + char STR1[256] = {0}; + + try + { + do + { + do + { + V.gotoflag = false; + V.elseflag = false; + while (stmttok != NULL && stmttok->kind == tokcolon) + stmttok = stmttok->next; + V.t = stmttok; + if (V.t != NULL) + { + V.t = V.t->next; +#if defined(PHREEQCI_GUI) + if (phreeqci_gui) + { + if (WaitForSingleObject(hInfiniteLoop, 0) == WAIT_OBJECT_0) + { + nIDErrPrompt = IDS_ERR_INFINITE_LOOP; + errormsg("Possible infinite loop"); + } + } +#endif + switch (stmttok->kind) + { + + case tokrem: + /* blank case */ + break; + + case toklist: + cmdlist(&V); + break; + + case tokrun: + cmdrun(&V); + break; + + case toknew: + cmdnew(&V); + break; + + case tokload: + cmdload(false, stringexpr(STR1, &V), &V); + break; + + case tokmerge: + cmdload(true, stringexpr(STR1, &V), &V); + break; + + case toksave: + cmdsave(&V); + break; + + case tokbye: + cmdbye(); + break; + + case tokdel: + cmddel(&V); + break; + + case tokrenum: + cmdrenum(&V); + break; + + case toklet: + cmdlet(false, &V); + break; + + case tokvar: + cmdlet(true, &V); + break; + + case tokprint: + cmdprint(&V); + break; + + case tokpunch: + cmdpunch(&V); + break; + + case tokput: + cmdput(&V); + break; + + case tokchange_por: + cmdchange_por(&V); + break; + + case tokchange_surf: + cmdchange_surf(&V); + break; + +#if defined PHREEQ98 || defined MULTICHART + case tokgraph_x: + cmdgraph_x(&V); + break; + + case tokgraph_y: + cmdgraph_y(&V); + break; + + case tokgraph_sy: + cmdgraph_sy(&V); + break; +#endif +#if defined MULTICHART + case tokplot_xy: + cmdplot_xy(&V); + break; +#endif + + case tokinput: + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_INPUT_NOTLEGAL; + errormsg ("Basic command INPUT is not a legal command in PHREEQC."); + } + else + { + error_msg ("Basic command INPUT is not a legal command in PHREEQC.", STOP); + } + break; + + case tokgoto: + cmdgoto(&V); + break; + + case tokif: + cmdif(&V); + break; + + case tokelse: + cmdelse(&V); + break; + + case tokend: + cmdend(&V); + break; + + case tokstop: + P_escapecode = -20; + throw PBasicStop(); + //goto _Ltry1; + break; + + case tokfor: + cmdfor(&V); + break; + + case toknext: + cmdnext(&V); + break; + + case tokwhile: + cmdwhile(&V); + break; + + case tokwend: + cmdwend(&V); + break; + + case tokgosub: + cmdgosub(&V); + break; + + case tokreturn: + cmdreturn(&V); + break; + + case tokread: + cmdread(&V); + break; + + case tokdata: + cmddata(&V); + break; + + case tokrestore: + cmdrestore(&V); + break; + + case tokgotoxy: + cmdgotoxy(&V); + break; + + case tokon: + cmdon(&V); + break; + + case tokdim: + cmddim(&V); + break; + + case tokerase: + cmderase(&V); + break; + + case tokpoke: + cmdpoke(&V); + break; + + default: + if (phreeqci_gui) + { + _ASSERTE(nIDErrPrompt == 0); + nIDErrPrompt = IDS_ERR_ILLEGAL; + } + errormsg("Illegal command"); + break; + } + } + if (!V.elseflag && !iseos(&V)) + checkextra(&V); + stmttok = V.t; + } + while (V.t != NULL); + if (stmtline != NULL) + { + if (!V.gotoflag) + stmtline = stmtline->next; + if (stmtline != NULL) + stmttok = stmtline->txt; + } + } + while (stmtline != NULL); + } + catch (const PBasicStop&) + { + //_Ltry1: + if (P_escapecode == -20) + PhreeqcPtr->warning_msg("Break"); + /* printf("Break"); */ + else if (P_escapecode != 42) + { + switch (P_escapecode) + { + + case -4: + { + char * error_string = PhreeqcPtr->sformatf( "Integer overflow in BASIC line\n %ld %s", stmtline->num, stmtline->inbuf); + PhreeqcPtr->warning_msg(error_string); + } + break; + + case -5: + { + char * error_string = PhreeqcPtr->sformatf( "Divide by zero in BASIC line\n %ld %s", stmtline->num, stmtline->inbuf); + PhreeqcPtr->warning_msg(error_string); + } + break; + + case -6: + { + char * error_string = PhreeqcPtr->sformatf( "Real math overflow in BASIC line\n %ld %s", stmtline->num, stmtline->inbuf); + PhreeqcPtr->warning_msg(error_string); + } + break; + + case -7: + { + char * error_string = PhreeqcPtr->sformatf( "Real math underflow in BASIC line\n %ld %s", stmtline->num, stmtline->inbuf); + PhreeqcPtr->warning_msg(error_string); + } + break; + + case -8: + case -19: + case -18: + case -17: + case -16: + case -15: + { + char * error_string = PhreeqcPtr->sformatf( "Value range error in BASIC line\n %ld %s", stmtline->num, stmtline->inbuf); + PhreeqcPtr->warning_msg(error_string); + } + break; + + case -10: + { + char * error_string = PhreeqcPtr->sformatf("I/O Error %d", (int) P_ioresult); + PhreeqcPtr->warning_msg(error_string); + } + break; + + default: + if (EXCP_LINE != -1) + { + char * error_string = PhreeqcPtr->sformatf( "%12ld\n", EXCP_LINE); + PhreeqcPtr->warning_msg(error_string); + } + _Escape(P_escapecode); + break; + } + } + if (stmtline != NULL) + { + if (phreeqci_gui) + { + _ASSERTE(nErrLineNumber == 0); + nErrLineNumber = stmtline->num; + } + else + { + char * error_string = PhreeqcPtr->sformatf( " in BASIC line\n %ld %s", stmtline->num, stmtline->inbuf); + error_msg(error_string, CONTINUE); + } + } + } // end catch +} /*exec */ + +int PBasic:: +free_dim_stringvar(varrec *l_varbase) +{ + int i, k; + if (l_varbase->numdims > 0) + { + k = 1; + for (i = 0; i < l_varbase->numdims; i++) + { + k = k * (l_varbase->dims[i]); + } + for (i = 0; i < k; i++) + { + PhreeqcPtr->free_check_null(l_varbase->UU.U1.sarr[i]); + } + l_varbase->UU.U1.sarr = (char **) PhreeqcPtr->free_check_null(l_varbase->UU.U1.sarr); + } + return (OK); +} + +#if defined MULTICHART +void PBasic:: +cmdplot_xy(struct LOC_exec *LINK) +{ + bool semiflag; + valrec n[2]; + + char STR[2][256]; + int i = 0; + semiflag = false; + + while (!iseos(LINK) && i < 2) + { + semiflag = false; + if ((unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) toksemi)) | (1L << ((long) tokcomma)))) != 0) + { + semiflag = true; + LINK->t = LINK->t->next; + i++; + continue; + } + n[i] = expr(LINK); + if (n[i].stringval) + { + strcpy(STR[i], n[i].UU.sval); + PhreeqcPtr->PHRQ_free(n[i].UU.sval); + } + else + numtostr(STR[i], n[i].UU.val); + } + + ChartObject *chart = PhreeqcPtr->chart_handler.Get_current_chart(); + if (chart == NULL) return; + + std::string x_str(STR[0]), y_str(STR[1]); + +// Code formerly in PlotXY, included here + { + + bool new_sim = false, new_trans = false; + + if (chart->Get_FirstCallToUSER_GRAPH() && chart->Get_colnr() == 0) + chart->Set_prev_sim_no(PhreeqcPtr->simulation); + else + chart->Set_prev_sim_no(PhreeqcPtr->simulation); + + // Add a curve if necessary + if ((int) chart->Get_Curves().size() == chart->Get_colnr()) + { + std::string head(""); + if (chart->Get_new_headings().size() > 0) + { + head = chart->Get_new_headings()[0]; + chart->Get_new_headings().erase(chart->Get_new_headings().begin()); + } + if (chart->Get_new_plotxy_curves().size() > 0) + { + // find plotxy curve definitions + chart->Add_curve(true, head, + chart->Get_new_plotxy_curves()[0].Get_line_w(), + chart->Get_new_plotxy_curves()[0].Get_symbol(), + chart->Get_new_plotxy_curves()[0].Get_symbol_size(), + chart->Get_new_plotxy_curves()[0].Get_y_axis(), + chart->Get_new_plotxy_curves()[0].Get_color() + ); + // pop plotxy curve definition + chart->Get_new_plotxy_curves().erase(chart->Get_new_plotxy_curves().begin()); + } + else + { + chart->Add_curve(true, head); + } + chart->Set_curve_added(true); + } + + if (x_str.size() > 0 && y_str.size() > 0) + { + chart->Get_Curves()[chart->Get_colnr()]->Get_x().push_back(atof(x_str.c_str())); + chart->Get_Curves()[chart->Get_colnr()]->Get_y().push_back(atof(y_str.c_str())); + chart->Set_point_added(true); + + // Mark added curve for first point, might have been invisible in DefineCurves + if (chart->Get_Curves()[chart->Get_colnr()]->Get_x().size() == 1) + chart->Set_curve_added(true); + } + } + chart->Set_colnr(chart->Get_colnr() + 1); +} + +void PBasic:: +cmdgraph_x(struct LOC_exec *LINK) +{ + bool semiflag; + valrec n; + + semiflag = false; + + ChartObject *chart = PhreeqcPtr->chart_handler.Get_current_chart(); + if (chart == NULL) return; + + while (!iseos(LINK)) + { + semiflag = false; + if ((unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) toksemi)) | (1L << ((long) tokcomma)))) != 0) + { + semiflag = true; + LINK->t = LINK->t->next; + continue; + } + n = expr(LINK); + + if (n.stringval) + { + if (strlen(n.UU.sval) > 0) + { + chart->Set_graph_x(atof(n.UU.sval)); + } + PhreeqcPtr->PHRQ_free(n.UU.sval); + } + else + chart->Set_graph_x(n.UU.val); + } + if ((int) chart->Get_Curves().size() == chart->Get_colnr()) + { + if (chart->Get_new_headings().size() > 0) + { + // remove x heading + //if (chart->Get_colnr() == chart->Get_ColumnOffset()) + { + chart->Get_new_headings().erase(chart->Get_new_headings().begin()); + } + } + } +} + +void PBasic:: +cmdgraph_y(struct LOC_exec *LINK) +{ + bool semiflag; + valrec n; + + semiflag = false; + + ChartObject *chart = PhreeqcPtr->chart_handler.Get_current_chart(); + if (chart == NULL) return; + + while (!iseos(LINK)) + { + semiflag = false; + if ((unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) toksemi)) | (1L << ((long) tokcomma)))) != 0) + { + semiflag = true; + LINK->t = LINK->t->next; + continue; + } + n = expr(LINK); + + // wait until x value is known before storing in curve + if (n.stringval) + { + if (strlen(n.UU.sval) > 0) + { + chart->Get_graph_y()[chart->Get_colnr()] = atof(n.UU.sval); + } + PhreeqcPtr->PHRQ_free(n.UU.sval); + } + else + { + chart->Get_graph_y()[chart->Get_colnr()] = n.UU.val; + } + chart->Set_point_added(true); + + // Add a new curve if necessary + if ((int) chart->Get_Curves().size() == chart->Get_colnr()) + { + if (chart->Get_new_headings().size() > 0) + { + chart->Add_curve(false, chart->Get_new_headings()[0]); + chart->Get_new_headings().erase(chart->Get_new_headings().begin()); + } + else + { + chart->Add_curve(false); + } + chart->Set_curve_added(true); + } + chart->Set_colnr(chart->Get_colnr() + 1); + } +} + +void PBasic:: +cmdgraph_sy(struct LOC_exec *LINK) +{ + bool semiflag; + valrec n; + + semiflag = false; + + ChartObject *chart = PhreeqcPtr->chart_handler.Get_current_chart(); + if (chart == NULL) return; + + while (!iseos(LINK)) + { + semiflag = false; + if ((unsigned long) LINK->t->kind < 32 && + ((1L << ((long) LINK->t->kind)) & + ((1L << ((long) toksemi)) | (1L << ((long) tokcomma)))) != 0) + { + semiflag = true; + LINK->t = LINK->t->next; + continue; + } + n = expr(LINK); + + chart->Get_secondary_y()[chart->Get_colnr()] = true; + + // wait until x value is known before storing in curve + if (n.stringval) + { + if (strlen(n.UU.sval) > 0) + { + chart->Get_graph_y()[chart->Get_colnr()] = atof(n.UU.sval); + } + PhreeqcPtr->PHRQ_free(n.UU.sval); + } + else + chart->Get_graph_y()[chart->Get_colnr()] = n.UU.val; + chart->Set_point_added(true); + // Add a new curve if necessary + if ((int) chart->Get_Curves().size() == chart->Get_colnr()) + { + if (chart->Get_new_headings().size() > 0) + { + chart->Add_curve(false, chart->Get_new_headings()[0]); + chart->Get_new_headings().erase(chart->Get_new_headings().begin()); + } + else + { + chart->Add_curve(false); + } + chart->Set_curve_added(true); + } + chart->Set_colnr(chart->Get_colnr() + 1); + } +} +#endif // MULTICHART + +/* In case your system lacks these... */ + +long PBasic:: +my_labs(long l_x) +{ + return ((l_x > 0) ? l_x : -l_x); +} + + +/* #define __STDC__ */ /* PHREEQ98 */ + +void * PBasic:: +my_memmove(void * d, Const void * l_s, size_t n) +{ + register char *dd = (char *) d, *ss = (char *) l_s; + if (dd < ss || (unsigned int) (dd - ss) >= n) + { + memcpy(dd, ss, n); + } + else if (n > 0) + { + dd += n; + ss += n; + while (n-- > 0) + *--dd = *--ss; + } + return d; +} + +void * PBasic:: +my_memcpy(void * d, Const void * l_s, size_t n) +{ + register char *ss = (char *) l_s, *dd = (char *) d; + while (n-- > 0) + *dd++ = *ss++; + return d; +} + +int PBasic:: +my_memcmp(Const void * s1, Const void * s2, size_t n) +{ + register char *a = (char *) s1, *b = (char *) s2; + register int i; + while (n-- > 0) + if ((i = (*a++) - (*b++)) != 0) + return i; + return 0; +} + +void * PBasic:: +my_memset(void * d, int c, size_t n) +{ + register char *dd = (char *) d; + while (n-- > 0) + *dd++ = (char) c; + return d; +} + +int PBasic:: +my_toupper(int c) +{ + if (islower(c)) + return _toupper(c); + else + return c; +} + +int PBasic:: +my_tolower(int c) +{ + if (isupper(c)) + return _tolower(c); + else + return c; +} + +long PBasic:: +ipow(long a, long b) +{ + long v; + + if (a == 0 || a == 1) + return a; + if (a == -1) + return (b & 1) ? -1 : 1; + if (b < 0) + return 0; + if (a == 2) + return 1L << b; + v = (b & 1) ? a : 1; + while ((b >>= 1) > 0) + { + a *= a; + if (b & 1) + v *= a; + } + return v; +} + +/* Common string functions: */ + +/* Store in "ret" the substring of length "len" starting from "pos" (1-based). + Store a shorter or null string if out-of-range. Return "ret". */ +char * PBasic:: +strsub(register char *ret, register char *l_s, register int pos, + register int len) +{ + register char *s2; + + if (--pos < 0 || len <= 0) + { + *ret = 0; + return ret; + } + while (pos > 0) + { + if (!*l_s++) + { + *ret = 0; + return ret; + } + pos--; + } + s2 = ret; + while (--len >= 0) + { + if (!(*s2++ = *l_s++)) + return ret; + } + *s2 = 0; + return ret; +} + +/* Return the index of the first occurrence of "pat" as a substring of "s", + starting at index "pos" (1-based). Result is 1-based, 0 if not found. */ + +int PBasic:: +strpos2(char *l_s, register char *pat, register int pos) +{ + register char *cp, ch; + register int slen; + + if (--pos < 0) + return 0; + slen = (int) strlen(l_s) - pos; + cp = l_s + pos; + if (!(ch = *pat++)) + return 0; + pos = (int) strlen(pat); + slen -= pos; + while (--slen >= 0) + { + if (*cp++ == ch && !strncmp(cp, pat, pos)) + return (int) (cp - l_s); + } + return 0; +} + +/* Case-insensitive version of strcmp. */ +int PBasic:: +strcicmp(register char *s1, register char *s2) +{ + register unsigned char c1, c2; + + while (*s1) + { + if (*s1++ != *s2++) + { + if (!s2[-1]) + return 1; + c1 = (unsigned char) toupper(s1[-1]); + c2 = (unsigned char) toupper(s2[-1]); + if (c1 != c2) + return c1 - c2; + } + } + if (*s2) + return -1; + return 0; +} + +/* HP and Turbo Pascal string functions: */ + +/* Trim blanks at left end of string. */ +char * PBasic:: +strltrim(register char *l_s) +{ + while (Isspace((int) *l_s++)); + return l_s - 1; +} + +/* Trim blanks at right end of string. */ +char * PBasic:: +strrtrim(register char *l_s) +{ + register char *s2 = l_s; + + if (!*l_s) + return l_s; + while (*++s2); + while (s2 > l_s && Isspace((int) *--s2)) + *s2 = 0; + return l_s; +} + +/* Store in "ret" string "s" with enough pad chars added to reach "size". */ + +/* Copy the substring of length "len" from index "spos" of "s" (1-based) + to index "dpos" of "d", lengthening "d" if necessary. Length and + indices must be in-range. */ +void PBasic:: +strmove(register int len, register char *l_s, register int spos, + register char *d, register int dpos) +{ + l_s += spos - 1; + d += dpos - 1; + while (*d && --len >= 0) + *d++ = *l_s++; + if (len > 0) + { + while (--len >= 0) + *d++ = *l_s++; + *d = 0; + } +} + +/* Insert string "src" at index "pos" of "dst". */ +void PBasic:: +strinsert(register char *src, register char *dst, register int pos) +{ + register int slen, dlen; + + if (--pos < 0) + return; + dlen = (int) strlen(dst); + dst += dlen; + dlen -= pos; + if (dlen <= 0) + { + strcpy(dst, src); + return; + } + slen = (int) strlen(src); + do + { + dst[slen] = *dst; + --dst; + } + while (--dlen >= 0); + dst++; + while (--slen >= 0) + *dst++ = *src++; +} + +/* File functions */ + +/* Peek at next character of input stream; return EOF at end-of-file. */ +int PBasic:: +P_peek(FILE * f) +{ + int ch; + + ch = getc(f); + if (ch == EOF) + return EOF; + ungetc(ch, f); + return (ch == '\n') ? ' ' : ch; +} + +/* Check if at end of file, using Pascal "eof" semantics. End-of-file for + stdin is broken; remove the special case for it to be broken in a + different way. */ +/*int P_eof(FILE *f)*/ +int PBasic:: +P_eof(void) +{ + return 0; +} + +/* Check if at end of line (or end of entire file). */ +int PBasic:: +P_eoln(FILE * f) +{ + register int ch; + + ch = getc(f); + if (ch == EOF) + return 1; + ungetc(ch, f); + return (ch == '\n'); +} + +/* Read a packed array of characters from a file. */ +void PBasic:: +P_readpaoc(FILE * f, char *l_s, int len) +{ + int ch; + + for (;;) + { + if (len <= 0) + return; + ch = getc(f); + if (ch == EOF || ch == '\n') + break; + *l_s++ = (char) ch; + --len; + } + while (--len >= 0) + *l_s++ = ' '; + if (ch != EOF) + ungetc(ch, f); +} + +void PBasic:: +P_readlnpaoc(FILE * f, char *l_s, int len) +{ + int ch; + + for (;;) + { + ch = getc(f); + if (ch == EOF || ch == '\n') + break; + if (len > 0) + { + *l_s++ = (char) ch; + --len; + } + } + while (--len >= 0) + *l_s++ = ' '; +} + +/* Compute maximum legal "seek" index in file (0-based). */ +long PBasic:: +P_maxpos(FILE * f) +{ + long savepos = ftell(f); + long val; + + if (fseek(f, 0L, SEEK_END)) + return -1; + val = ftell(f); + if (fseek(f, savepos, SEEK_SET)) + return -1; + return val; +} + +/* Use packed array of char for a file name. */ +char * PBasic:: +P_trimname(register char * fn, register int len) +{ + register char *cp = fnbuf; + + while (--len >= 0 && *fn && !isspace((int) *fn)) + *cp++ = *fn++; + *cp = 0; + return fnbuf; +} + +/* Pascal's "memavail" doesn`t make much sense in Unix with virtual memory. + We fix memory size as 10Meg as a reasonable compromise. */ + +long PBasic:: +memavail(void) +{ + return 10000000; /* worry about this later! */ +} + +long PBasic:: +maxavail(void) +{ + return memavail(); +} + +/* Sets are stored as an array of longs. S[0] is the size of the set; + S[N] is the n`th 32-bit chunk of the set. S[0] equals the maximum + I such that S[I] is nonzero. S[0] is zero for an empty set. Within + each long, bits are packed from lsb to msb. The first bit of the + set is the element with ordinal value 0. (Thus, for a "set of 5..99", + the lowest five bits of the first long are unused and always zero.) */ + +/* (Sets with 32 or fewer elements are normally stored as plain longs.) */ +long * PBasic:: +P_setunion(register long *d, register long *s1, register long *s2) /* d := s1 + s2 */ +{ + long *dbase = d++; + register int sz1 = *s1++, sz2 = *s2++; + while (sz1 > 0 && sz2 > 0) + { + *d++ = *s1++ | *s2++; + sz1--, sz2--; + } + while (--sz1 >= 0) + *d++ = *s1++; + while (--sz2 >= 0) + *d++ = *s2++; + *dbase = (int) (d - dbase - 1); + return dbase; +} + +long * PBasic:: +P_setint(register long *d, register long *s1, register long *s2) /* d := s1 * s2 */ +{ + long *dbase = d++; + register int sz1 = *s1++, sz2 = *s2++; + while (--sz1 >= 0 && --sz2 >= 0) + *d++ = *s1++ & *s2++; + while (--d > dbase && !*d); + *dbase = (int) (d - dbase); + return dbase; +} + +long * PBasic:: +P_setdiff(register long *d, register long *s1, register long *s2) /* d := s1 - s2 */ +{ + long *dbase = d++; + register int sz1 = *s1++, sz2 = *s2++; + while (--sz1 >= 0 && --sz2 >= 0) + *d++ = *s1++ & ~*s2++; + if (sz1 >= 0) + { + while (sz1-- >= 0) + *d++ = *s1++; + } + while (--d > dbase && !*d); + *dbase = (int) (d - dbase); + return dbase; +} + +long * PBasic:: +P_setxor(register long *d, register long *s1, register long *s2) /* d := s1 / s2 */ +{ + long *dbase = d++; + register int sz1 = *s1++, sz2 = *s2++; + while (sz1 > 0 && sz2 > 0) + { + *d++ = *s1++ ^ *s2++; + sz1--, sz2--; + } + while (--sz1 >= 0) + *d++ = *s1++; + while (--sz2 >= 0) + *d++ = *s2++; + while (--d > dbase && !*d); + *dbase = (int) (d - dbase); + return dbase; +} + +long * PBasic:: +P_addset(register long *l_s, register unsigned val) /* s := s + [val] */ +{ + register long *sbase = l_s; + register int bit, size; + bit = val % SETBITS; + val /= SETBITS; + size = *l_s; + if ((long) ++val > size) + { + l_s += size; + while ((long) val > size) + *++l_s = 0, size++; + *sbase = size; + } + else + l_s += val; + *l_s |= 1L << bit; + return sbase; +} + +long * PBasic:: +P_addsetr(register long *l_s, register unsigned v1, register unsigned v2) /* s := s + [v1..v2] */ +{ + register long *sbase = l_s; + register int b1, b2, size; + if ((int) v1 > (int) v2) + return sbase; + b1 = v1 % SETBITS; + v1 /= SETBITS; + b2 = v2 % SETBITS; + v2 /= SETBITS; + size = *l_s; + v1++; + if ((int) ++v2 > size) + { + while ((int) v2 > size) + l_s[++size] = 0; + l_s[v2] = 0; + *l_s = v2; + } + l_s += v1; + if (v1 == v2) + { + *l_s |= (~((-2L) << (b2 - b1))) << b1; + } + else + { + *l_s++ |= (-1L) << b1; + while (++v1 < v2) + *l_s++ = -1; + *l_s |= ~((-2L) << b2); + } + return sbase; +} + +long * PBasic:: +P_remset(register long *l_s, register unsigned val) /* s := s - [val] */ +{ + register int bit; + bit = val % SETBITS; + val /= SETBITS; + if ((long) ++val <= *l_s) + { + if (!(l_s[val] &= ~(1L << bit))) + while (*l_s && !l_s[*l_s]) + (*l_s)--; + } + return l_s; +} + +int PBasic:: +P_setequal(register long *s1, register long *s2) /* s1 = s2 */ +{ + register int size = *s1++; + if (*s2++ != size) + return 0; + while (--size >= 0) + { + if (*s1++ != *s2++) + return 0; + } + return 1; +} + +int PBasic:: +P_subset(register long *s1, register long *s2) /* s1 <= s2 */ +{ + register int sz1 = *s1++, sz2 = *s2++; + if (sz1 > sz2) + return 0; + while (--sz1 >= 0) + { + if (*s1++ & ~*s2++) + return 0; + } + return 1; +} + +long * PBasic:: +P_setcpy(register long *d, register long *l_s) /* d := s */ +{ + register long *save_d = d; + +#ifdef SETCPY_MEMCPY + memcpy(d, l_s, (*l_s + 1) * sizeof(long)); +#else + register int i = *l_s + 1; + while (--i >= 0) + *d++ = *l_s++; +#endif + return save_d; +} + +/* s is a "smallset", i.e., a 32-bit or less set stored + directly in a long. */ +long * PBasic:: +P_expset(register long *d, register long l_s) /* d := s */ +{ + if (l_s) + { + d[1] = l_s; + *d = 1; + } + else + *d = 0; + return d; +} + +long PBasic:: +P_packset(register long *l_s) /* convert s to a small-set */ +{ + if (*l_s++) + return *l_s; + else + return 0; +} + +int PBasic:: +_OutMem(void) +{ + return _Escape(-2); +} + +int PBasic:: +_CaseCheck(void) +{ + return _Escape(-9); +} + +int PBasic:: +_NilCheck(void) +{ + return _Escape(-3); +} + +/* The following is suitable for the HP Pascal operating system. + It might want to be revised when emulating another system. */ + +char * PBasic:: +_ShowEscape(char *buf, int code, int ior, char *prefix) +{ + char *bufp; + + if (prefix && *prefix) + { + strcpy(buf, prefix); + strcat(buf, ": "); + bufp = buf + strlen(buf); + } + else + { + bufp = buf; + } + if (code == -10) + { + sprintf(bufp, "Pascal system I/O error %d", ior); + switch (ior) + { + case 3: + strcat(buf, " (illegal I/O request)"); + break; + case 7: + strcat(buf, " (bad file name)"); + break; + case FileNotFound: /*10 */ + strcat(buf, " (file not found)"); + break; + case FileNotOpen: /*13 */ + strcat(buf, " (file not open)"); + break; + case BadInputFormat: /*14 */ + strcat(buf, " (bad input format)"); + break; + case 24: + strcat(buf, " (not open for reading)"); + break; + case 25: + strcat(buf, " (not open for writing)"); + break; + case 26: + strcat(buf, " (not open for direct access)"); + break; + case 28: + strcat(buf, " (string subscript out of range)"); + break; + case EndOfFile: /*30 */ + strcat(buf, " (end-of-file)"); + break; + case FileWriteError: /*38 */ + strcat(buf, " (file write error)"); + break; + } + } + else + { + sprintf(bufp, "Pascal system error %d", code); + switch (code) + { + case -2: + strcat(buf, " (out of memory)"); + break; + case -3: + strcat(buf, " (reference to NIL pointer)"); + break; + case -4: + strcat(buf, " (integer overflow)"); + break; + case -5: + strcat(buf, " (divide by zero)"); + break; + case -6: + strcat(buf, " (real math overflow)"); + break; + case -8: + strcat(buf, " (value range error)"); + break; + case -9: + strcat(buf, " (CASE value range error)"); + break; + case -12: + strcat(buf, " (bus error)"); + break; + case -20: + strcat(buf, " (stopped by user)"); + break; + } + } + return buf; +} + +int PBasic:: +_Escape(int code) +{ + P_escapecode = code; + throw PBasicStop(); + + // following not used +#ifdef SKIP + char l_buf[100]; + char token[200], empty[2] = { "\0" }; + if (code == 0) + /* exit(EXIT_SUCCESS); */ + error_msg("Exit success in Basic", STOP); + if (code == -1) + { + error_msg("Fatal error in Basic interpreter.", CONTINUE); + sprintf(token, "%s", + _ShowEscape(l_buf, P_escapecode, P_ioresult, empty)); + error_msg(token, STOP); + exit(EXIT_FAILURE); + } + /* fprintf(stderr, "%s\n", _ShowEscape(l_buf, P_escapecode, P_ioresult, "")); */ + /* exit(EXIT_FAILURE); */ + error_msg("Fatal error in Basic interpreter.", CONTINUE); + sprintf(token, "%s", _ShowEscape(l_buf, P_escapecode, P_ioresult, empty)); + error_msg(token, STOP); + return (1); +#endif +} + +int PBasic:: +_EscIO(int code) +{ + P_ioresult = code; + return _Escape(-10); +} + +const std::map::value_type temp_tokens[] = { + std::map::value_type("+", PBasic::tokplus), + std::map::value_type("-", PBasic::tokminus), + std::map::value_type("*", PBasic::toktimes), + std::map::value_type("/", PBasic::tokdiv), + std::map::value_type("^", PBasic::tokup), + std::map::value_type("( or [", PBasic::toklp), + std::map::value_type(") or ]", PBasic::tokrp), + std::map::value_type("]", PBasic::tokcomma), + std::map::value_type(";", PBasic::toksemi), + std::map::value_type(":", PBasic::tokcolon), + std::map::value_type("=", PBasic::tokeq), + std::map::value_type("<", PBasic::toklt), + std::map::value_type("<=", PBasic::tokle), + std::map::value_type(">", PBasic::tokgt), + std::map::value_type(">=", PBasic::tokge), + std::map::value_type("and", PBasic::tokand), + std::map::value_type("or", PBasic::tokor), + std::map::value_type("xor", PBasic::tokxor), + std::map::value_type("not", PBasic::toknot), + std::map::value_type("mod", PBasic::tokmod), + std::map::value_type("sqr", PBasic::toksqr), + std::map::value_type("sqrt", PBasic::toksqrt), + std::map::value_type("ceil", PBasic::tokceil), + std::map::value_type("floor", PBasic::tokfloor), + std::map::value_type("sin", PBasic::toksin), + std::map::value_type("cos", PBasic::tokcos), + std::map::value_type("tan", PBasic::toktan), + std::map::value_type("arctan", PBasic::tokarctan), + std::map::value_type("log", PBasic::toklog), + std::map::value_type("exp", PBasic::tokexp), + std::map::value_type("abs", PBasic::tokabs), + std::map::value_type("sgn", PBasic::toksgn), + std::map::value_type("str$", PBasic::tokstr_), + std::map::value_type("val", PBasic::tokval), + std::map::value_type("chr$", PBasic::tokchr_), + std::map::value_type("eol$", PBasic::tokeol_), + std::map::value_type("asc", PBasic::tokasc), + std::map::value_type("len", PBasic::toklen), + std::map::value_type("mid$", PBasic::tokmid_), + std::map::value_type("peek", PBasic::tokpeek), + std::map::value_type("let", PBasic::toklet), + std::map::value_type("print", PBasic::tokprint), + std::map::value_type("punch", PBasic::tokpunch), +#if defined (PHREEQ98) || defined (MULTICHART) + std::map::value_type("graph_x", PBasic::tokgraph_x), + std::map::value_type("graph_y", PBasic::tokgraph_y), + std::map::value_type("graph_sy", PBasic::tokgraph_sy), +#endif +#if defined MULTICHART + std::map::value_type("plot_xy", PBasic::tokplot_xy), +#endif + std::map::value_type("input", PBasic::tokinput), + std::map::value_type("goto", PBasic::tokgoto), + std::map::value_type("go to", PBasic::tokgoto), + std::map::value_type("if", PBasic::tokif), + std::map::value_type("end", PBasic::tokend), + std::map::value_type("stop", PBasic::tokstop), + std::map::value_type("for", PBasic::tokfor), + std::map::value_type("next", PBasic::toknext), + std::map::value_type("while", PBasic::tokwhile), + std::map::value_type("wend", PBasic::tokwend), + std::map::value_type("gosub", PBasic::tokgosub), + std::map::value_type("return", PBasic::tokreturn), + std::map::value_type("read", PBasic::tokread), + std::map::value_type("data", PBasic::tokdata), + std::map::value_type("restore", PBasic::tokrestore), + std::map::value_type("gotoxy", PBasic::tokgotoxy), + std::map::value_type("on", PBasic::tokon), + std::map::value_type("dim", PBasic::tokdim), + std::map::value_type("erase", PBasic::tokerase), + std::map::value_type("poke", PBasic::tokpoke), + std::map::value_type("list", PBasic::toklist), + std::map::value_type("run", PBasic::tokrun), + std::map::value_type("new", PBasic::toknew), + std::map::value_type("load", PBasic::tokload), + std::map::value_type("merge", PBasic::tokmerge), + std::map::value_type("save", PBasic::toksave), + std::map::value_type("bye", PBasic::tokbye), + std::map::value_type("quit", PBasic::tokbye), + std::map::value_type("del", PBasic::tokdel), + std::map::value_type("renum", PBasic::tokrenum), + std::map::value_type("then", PBasic::tokthen), + std::map::value_type("else", PBasic::tokelse), + std::map::value_type("to", PBasic::tokto), + std::map::value_type("step", PBasic::tokstep), + std::map::value_type("tc", PBasic::toktc), + std::map::value_type("tk", PBasic::toktk), + std::map::value_type("time", PBasic::toktime), + std::map::value_type("sim_time", PBasic::toksim_time), + std::map::value_type("total_time", PBasic::toktotal_time), + std::map::value_type("m0", PBasic::tokm0), + std::map::value_type("m", PBasic::tokm), + std::map::value_type("parm", PBasic::tokparm), + std::map::value_type("act", PBasic::tokact), + std::map::value_type("edl", PBasic::tokedl), + std::map::value_type("surf", PBasic::toksurf), + std::map::value_type("equi", PBasic::tokequi), + std::map::value_type("kin", PBasic::tokkin), + std::map::value_type("gas", PBasic::tokgas), + std::map::value_type("s_s", PBasic::toks_s), + std::map::value_type("misc1", PBasic::tokmisc1), + std::map::value_type("misc2", PBasic::tokmisc2), + std::map::value_type("mu", PBasic::tokmu), + std::map::value_type("osmotic", PBasic::tokosmotic), + std::map::value_type("alk", PBasic::tokalk), + std::map::value_type("lk_species", PBasic::toklk_species), + std::map::value_type("lk_named", PBasic::toklk_named), + std::map::value_type("lk_phase", PBasic::toklk_phase), + std::map::value_type("sum_species", PBasic::toksum_species), + std::map::value_type("sum_gas", PBasic::toksum_gas), + std::map::value_type("sum_s_s", PBasic::toksum_s_s), + std::map::value_type("calc_value", PBasic::tokcalc_value), + std::map::value_type("description", PBasic::tokdescription), + std::map::value_type("title", PBasic::toktitle), + std::map::value_type("sys", PBasic::toksys), + std::map::value_type("instr", PBasic::tokinstr), + std::map::value_type("ltrim", PBasic::tokltrim), + std::map::value_type("rtrim", PBasic::tokrtrim), + std::map::value_type("trim", PBasic::toktrim), + std::map::value_type("pad", PBasic::tokpad), + std::map::value_type("rxn", PBasic::tokrxn), + std::map::value_type("dist", PBasic::tokdist), + std::map::value_type("mol", PBasic::tokmol), + std::map::value_type("la", PBasic::tokla), + std::map::value_type("lm", PBasic::toklm), + std::map::value_type("sr", PBasic::toksr), + std::map::value_type("si", PBasic::toksi), + std::map::value_type("step_no", PBasic::tokstep_no), + std::map::value_type("cell_no", PBasic::tokcell_no), + std::map::value_type("sim_no", PBasic::toksim_no), + std::map::value_type("tot", PBasic::toktot), + std::map::value_type("log10", PBasic::toklog10), + std::map::value_type("charge_balance", PBasic::tokcharge_balance), + std::map::value_type("percent_error", PBasic::tokpercent_error), + std::map::value_type("put", PBasic::tokput), + std::map::value_type("get", PBasic::tokget), + std::map::value_type("exists", PBasic::tokexists), + std::map::value_type("rem", PBasic::tokrem), + std::map::value_type("change_por", PBasic::tokchange_por), + std::map::value_type("get_por", PBasic::tokget_por), + std::map::value_type("change_surf", PBasic::tokchange_surf), + std::map::value_type("porevolume", PBasic::tokporevolume), + std::map::value_type("sc", PBasic::toksc), + std::map::value_type("gamma", PBasic::tokgamma), + std::map::value_type("lg", PBasic::toklg), + std::map::value_type("rho", PBasic::tokrho), + std::map::value_type("cell_volume", PBasic::tokcell_volume), + std::map::value_type("cell_pore_volume", PBasic::tokcell_pore_volume), + std::map::value_type("cell_porosity", PBasic::tokcell_porosity), + std::map::value_type("cell_saturation", PBasic::tokcell_saturation), + std::map::value_type("totmole", PBasic::toktotmole), + std::map::value_type("totmol", PBasic::toktotmol), + std::map::value_type("totmoles", PBasic::toktotmoles), + std::map::value_type("iso", PBasic::tokiso), + std::map::value_type("iso_unit", PBasic::tokiso_unit), + std::map::value_type("phase_formula", PBasic::tokphase_formula), + std::map::value_type("phase_formula$", PBasic::tokphase_formula_), + std::map::value_type("list_s_s", PBasic::toklist_s_s), + std::map::value_type("pr_p", PBasic::tokpr_p), + std::map::value_type("pr_phi", PBasic::tokpr_phi), + std::map::value_type("gas_p", PBasic::tokgas_p), + std::map::value_type("gas_vm", PBasic::tokgas_vm), + std::map::value_type("pressure", PBasic::tokpressure), + std::map::value_type("eps_r", PBasic::tokeps_r), + std::map::value_type("vm", PBasic::tokvm), + std::map::value_type("dh_a", PBasic::tokdh_a), + std::map::value_type("dh_b", PBasic::tokdh_b), + std::map::value_type("dh_av", PBasic::tokdh_av), + std::map::value_type("qbrn", PBasic::tokqbrn), + std::map::value_type("kappa", PBasic::tokkappa), + std::map::value_type("gfw", PBasic::tokgfw), + std::map::value_type("soln_vol", PBasic::toksoln_vol), + std::map::value_type("equi_delta", PBasic::tokequi_delta), + std::map::value_type("kin_delta", PBasic::tokkin_delta), + std::map::value_type("kin_time", PBasic::tokkin_time), + std::map::value_type("str_f$", PBasic::tokstr_f_), + std::map::value_type("str_e$", PBasic::tokstr_e_), + std::map::value_type("species_formula", PBasic::tokspecies_formula), + std::map::value_type("species_formula$", PBasic::tokspecies_formula_), + std::map::value_type("eq_frac", PBasic::tokeq_frac), + std::map::value_type("equiv_frac", PBasic::tokeq_frac), + std::map::value_type("callback", PBasic::tokcallback), + std::map::value_type("diff_c", PBasic::tokdiff_c), + std::map::value_type("sa_declercq", PBasic::toksa_declercq), + std::map::value_type("edl_species", PBasic::tokedl_species), + std::map::value_type("viscos", PBasic::tokviscos), + std::map::value_type("viscos_0", PBasic::tokviscos_0), + std::map::value_type("rho_0", PBasic::tokrho_0), + std::map::value_type("kinetics_formula", PBasic::tokkinetics_formula), + std::map::value_type("kinetics_formula$", PBasic::tokkinetics_formula), + std::map::value_type("phase_vm", PBasic::tokphase_vm), + std::map::value_type("current_a", PBasic::tokcurrent_a), + std::map::value_type("pot_v", PBasic::tokpot_v), + std::map::value_type("t_sc", PBasic::tokt_sc), + std::map::value_type("setdiff_c", PBasic::toksetdiff_c), + std::map::value_type("aphi", PBasic::tokaphi) +}; +std::map PBasic::command_tokens(temp_tokens, temp_tokens + sizeof temp_tokens / sizeof temp_tokens[0]); + +/* End. */ + diff --git a/phreeqcpp/PBasic.h b/phreeqcpp/PBasic.h new file mode 100644 index 00000000..45de1df7 --- /dev/null +++ b/phreeqcpp/PBasic.h @@ -0,0 +1,546 @@ +#ifndef _INC_PBasic_H +#define _INC_PBasic_H +#if defined(PHREEQCI_GUI) +#include +#endif +#include +#include +#include +#include +#include +#include +#include "phrqtype.h" +#include "PHRQ_base.h" +#include "global_structures.h" +class Phreeqc; + +class PBasicStop : public std::exception +{ +}; + +#define forloop 0 +#define whileloop 1 +#define gosubloop 2 +#define checking true +#define varnamelen 20 +#define maxdims 4 +#define MAX_LINE 4096 +#define FileNotFound 10 +#define FileNotOpen 13 +#define FileWriteError 38 +#define BadInputFormat 14 +#define EndOfFile 30 +#define SETBITS 32 +#define Const + +typedef char varnamestring[varnamelen + 1]; + +/* Header file for code generated by "p2c", the Pascal-to-C translator */ + +/* "p2c" Copyright (C) 1989, 1990, 1991 Free Software Foundation. + * By Dave Gillespie, daveg@csvax.cs.caltech.edu. Version 1.20. + * This file may be copied, modified, etc. in any way. It is not restricted + * by the licence agreement accompanying p2c itself. + */ + +typedef struct varrec +{ + varnamestring name; + struct varrec *next; + long dims[maxdims]; + char numdims; + bool stringvar; + union + { + struct + { + LDBLE *arr; + LDBLE *val, rv; + } U0; + struct + { + char **sarr; + char **sval, *sv; + } U1; + } UU; +} varrec; + +typedef struct tokenrec +{ + struct tokenrec *next; + int kind; + union + { + struct varrec *vp; + LDBLE num; + char *sp; + char snch; + } UU; +//#ifdef PHREEQCI_GUI + size_t n_sz; + char *sz_num; +//#endif +} tokenrec; + +typedef struct linerec +{ + long num, num2; + tokenrec *txt; + char inbuf[MAX_LINE]; + struct linerec *next; +} linerec; + +class valrec +{ +public: + valrec() + { + stringval = false; + UU.val = 0; + } + ~valrec() {} + bool stringval; + union + { + LDBLE val; + char *sval; + } UU; +}; + +typedef struct looprec +{ + struct looprec *next; + linerec *homeline; + tokenrec *hometok; + int kind; + union + { + struct + { + varrec *vp; + LDBLE max, step; + } U0; + } UU; +} looprec; + +/* variables for exec: */ +struct LOC_exec +{ + bool gotoflag, elseflag; + tokenrec *t; +}; + +class PBasic: public PHRQ_base +{ +public: + PBasic(Phreeqc *ptr, PHRQ_io *phrq_io=NULL); + virtual ~PBasic(); + + enum BASIC_TOKEN + { + tokvar, + toknum, + tokstr, + toksnerr, + tokplus, + tokminus, + toktimes, + tokdiv, + tokup, + toklp, + tokrp, + tokcomma, + toksemi, + tokcolon, + tokeq, + toklt, + tokgt, + tokle, + tokge, + tokne, + tokand, + tokor, + tokxor, + tokmod, + toknot, + toksqr, + toksqrt, + toksin, + tokcos, + toktan, + tokarctan, + toklog, + tokexp, + tokabs, + toksgn, + tokstr_, + tokval, + tokchr_, + tokasc, + toklen, + tokmid_, + tokpeek, + tokrem, + toklet, + tokprint, + tokinput, + tokgoto, + tokif, + tokend, + tokstop, + tokfor, + toknext, + tokwhile, + tokwend, + tokgosub, + tokreturn, + tokread, + tokdata, + tokrestore, + tokgotoxy, + tokon, + tokdim, + tokpoke, + toklist, + tokrun, + toknew, + tokload, + tokmerge, + toksave, + tokbye, + tokdel, + tokrenum, + tokthen, + tokelse, + tokto, + tokstep, + toktc, + tokm0, + tokm, + tokparm, + tokact, + tokmol, + tokla, + toklm, + toksr, + toksi, + toktot, + toktk, + toktime, + toklog10, + toksim_time, + tokequi, + tokgas, + tokpunch, + tokkin, + toks_s, + tokmu, + tokalk, + tokrxn, + tokdist, + tokmisc1, + tokmisc2, + tokedl, + tokstep_no, + toksim_no, + toktotal_time, + tokput, + tokget, + tokcharge_balance, + tokpercent_error, +#if defined (PHREEQ98) || defined (MULTICHART) + tokgraph_x, + tokgraph_y, + tokgraph_sy, +#endif + tokcell_no, + tokexists, + toksurf, + toklk_species, + toklk_named, + toklk_phase, + toksum_species, + toksum_gas, + toksum_s_s, + tokcalc_value, + tokdescription, + toktitle, + toksys, + tokinstr, + tokltrim, + tokrtrim, + toktrim, + tokpad, + tokchange_por, + tokget_por, + tokosmotic, + tokchange_surf, + tokporevolume, + toksc, + tokgamma, + toklg, + tokrho, + tokrho_0, + tokcell_volume, + tokcell_pore_volume, + tokcell_porosity, + tokcell_saturation, +#if defined MULTICHART + tokplot_xy, +#endif + toktotmole, + tokiso, + tokiso_unit, + toktotmol, + toktotmoles, + tokeol_, + tokceil, + tokfloor, + tokkinetics_formula, + tokkinetics_formula_, + tokphase_formula, + tokphase_formula_, + tokspecies_formula, + tokspecies_formula_, + toklist_s_s, + tokpr_p, + tokpr_phi, + tokgas_p, + tokgas_vm, + tokpressure, + tokerase, + tokeps_r, + tokvm, + tokphase_vm, + tokdh_a, + tokdh_b, + tokdh_av, + tokqbrn, + tokkappa, + tokgfw, + toksoln_vol, + tokequi_delta, + tokkin_delta, + tokkin_time, + tokstr_f_, + tokstr_e_, + tokeq_frac, + tokequiv_frac, + tokcallback, + tokdiff_c, + toksetdiff_c, + toksa_declercq, + tokedl_species, + tokviscos, + tokviscos_0, + tokcurrent_a, + tokpot_v, + tokt_sc, + tokaphi + }; + +#if !defined(PHREEQCI_GUI) + enum IDErr + { + IDS_ERR_ARRAY_ALREADY, + IDS_ERR_BAD_SUBSCRIPT, + IDS_ERR_EXTRA, + IDS_ERR_FOR_WO_NEXT, + IDS_ERR_ILLEGAL, + IDS_ERR_INFINITE_LOOP, + IDS_ERR_INPUT_NOTLEGAL, + IDS_ERR_MISMATCH, + IDS_ERR_MISSING_Q, + IDS_ERR_MISSING_RP, + IDS_ERR_NEXT_WO_FOR, + IDS_ERR_OUT_OF_DATA, + IDS_ERR_RETURN_WO_GOSUB, + IDS_ERR_SYNTAX, + IDS_ERR_UNDEF_LINE, + IDS_ERR_WEND_WO_WHILE, + IDS_ERR_WHILE_WO_WEND + }; +#endif + + // Methods + bool Get_phreeqci_gui(void) const {return phreeqci_gui;}; + void Set_phreeqci_gui(bool tf) {phreeqci_gui = tf;}; + bool Get_parse_all(void) const {return parse_all;}; + void Set_parse_all(bool tf) {parse_all = tf;}; + bool Get_parse_whole_program(void) const {return parse_whole_program;}; + void Set_parse_whole_program(bool tf) {parse_whole_program = tf;}; + int Get_nErrLineNumber(void) const {return nErrLineNumber;}; + void Set_nErrLineNumber(int i) {nErrLineNumber = i;}; +#if defined(PHREEQCI_GUI) + UINT Get_nIDErrPrompt(void)const {return nIDErrPrompt;}; + void Set_nIDErrPrompt(UINT u) {nIDErrPrompt = u;}; + int Get_P_escapecode(void)const {return P_escapecode;}; + void Set_P_escapecode(int code) {P_escapecode = code;}; + HANDLE Get_hInfiniteLoop(void) const {return hInfiniteLoop;}; + void Set_hInfiniteLoop(HANDLE h) {hInfiniteLoop = h;}; +#endif + int free_dim_stringvar(varrec *varbase); + + void exec(void); + int basic_renumber(char *commands, void **lnbase, void **vbase, void **lpbase); + void restoredata(void); + void clearloops(void); + void clearvar(varrec * v); + void clearvars(void); + char * numtostr(char * Result, LDBLE n); + void parse(char * inbuf, tokenrec ** buf); + void listtokens(FILE * f, tokenrec * buf); + void disposetokens(tokenrec ** tok); + void parseinput(tokenrec ** buf); + void errormsg(const char * s); + void snerr(const char * s); + void tmerr(const char * s); + void badsubscr(void); + LDBLE realfactor(struct LOC_exec *LINK); + char * strfactor(struct LOC_exec * LINK); + char *stringfactor(char * Result, struct LOC_exec *LINK); + const char *stringfactor(std::string & Result, struct LOC_exec * LINK); + long intfactor(struct LOC_exec *LINK); + LDBLE realexpr(struct LOC_exec *LINK); + char * strexpr(struct LOC_exec * LINK); + char * stringexpr(char * Result, struct LOC_exec * LINK); + long intexpr(struct LOC_exec *LINK); + void require(int k, struct LOC_exec *LINK); + void skipparen(struct LOC_exec *LINK); + varrec * findvar(struct LOC_exec *LINK); + valrec factor(struct LOC_exec *LINK); + valrec upexpr(struct LOC_exec * LINK); + valrec term(struct LOC_exec * LINK); + valrec sexpr(struct LOC_exec * LINK); + valrec relexpr(struct LOC_exec * LINK); + valrec andexpr(struct LOC_exec * LINK); + valrec expr(struct LOC_exec *LINK); + void checkextra(struct LOC_exec *LINK); + bool iseos(struct LOC_exec *LINK); + void skiptoeos(struct LOC_exec *LINK); + linerec * findline(long n); + linerec * mustfindline(long n); + void cmdend(struct LOC_exec *LINK); + void cmdnew(struct LOC_exec *LINK); + void cmdlist(struct LOC_exec *LINK); + void cmdload(bool merging, char * name, struct LOC_exec *LINK); + void cmdrun(struct LOC_exec *LINK); + void cmdsave(struct LOC_exec *LINK); + void cmdput(struct LOC_exec *LINK); + void cmdchange_por(struct LOC_exec *LINK); + void cmdchange_surf(struct LOC_exec *LINK); + void cmdbye(void); + void cmddel(struct LOC_exec *LINK); + void cmdrenum(struct LOC_exec *LINK); + void cmdprint(struct LOC_exec *LINK); + void cmdpunch(struct LOC_exec *LINK); +#if defined PHREEQ98 || defined MULTICHART + void cmdgraph_x(struct LOC_exec *LINK); + void cmdgraph_y(struct LOC_exec *LINK); + void cmdgraph_sy(struct LOC_exec *LINK); +#endif +#if defined MULTICHART + void cmdplot_xy(struct LOC_exec *LINK); +#endif + void cmdlet(bool implied, struct LOC_exec *LINK); + void cmdgoto(struct LOC_exec *LINK); + void cmdif(struct LOC_exec *LINK); + void cmdelse(struct LOC_exec *LINK); + bool skiploop(int up, int dn, struct LOC_exec *LINK); + void cmdfor(struct LOC_exec *LINK); + void cmdnext(struct LOC_exec *LINK); + void cmdwhile(struct LOC_exec *LINK); + void cmdwend(struct LOC_exec *LINK); + void cmdgosub(struct LOC_exec *LINK); + void cmdreturn(struct LOC_exec *LINK); + void cmdread(struct LOC_exec *LINK); + void cmddata(struct LOC_exec *LINK); + void cmdrestore(struct LOC_exec *LINK); + void cmdgotoxy(struct LOC_exec *LINK); + void cmdon(struct LOC_exec *LINK); + void cmddim(struct LOC_exec *LINK); + void cmderase(struct LOC_exec *LINK); + void cmdpoke(struct LOC_exec *LINK); + int basic_main(char *commands); + int basic_compile(char *commands, void **lnbase, void **vbase, void **lpbase); + int basic_run(char *commands, void *lnbase, void *vbase, void *lpbase); + int basic_init(void); +#ifdef PHREEQ98 + void GridChar(char *s, char *a); +#endif + int sget_logical_line(char **ptr, int *l, char *return_line); + long my_labs(long x); + void * my_memmove(void * d, Const void * s, size_t n); + void * my_memcpy(void * d, Const void * s, size_t n); + int my_memcmp(Const void * s1, Const void * s2, size_t n); + void * my_memset(void * d, int c, size_t n); + int my_toupper(int c); + int my_tolower(int c); + long ipow(long a, long b); + char * strsub(register char *ret, register char *s, register int pos, + register int len); + int strpos2(char *s, register char *pat, register int pos); + int strcicmp(register char *s1, register char *s2); + char * strltrim(register char *s); + char * strrtrim(register char *s); + void strmove(register int len, register char *s, register int spos, + register char *d, register int dpos); + void strinsert(register char *src, register char *dst, register int pos); + int P_peek(FILE * f); + int P_eof(void); + int P_eoln(FILE * f); + void P_readpaoc(FILE * f, char *s, int len); + void P_readlnpaoc(FILE * f, char *s, int len); + long P_maxpos(FILE * f); + char * P_trimname(register char * fn, register int len); + long memavail(void); + long maxavail(void); + long * P_setunion(register long *d, register long *s1, register long *s2); + long * P_setint(register long *d, register long *s1, register long *s2); + long * P_setdiff(register long *d, register long *s1, register long *s2); + long * P_setxor(register long *d, register long *s1, register long *s2); + long * P_addset(register long *s, register unsigned val); + long * P_addsetr(register long *s, register unsigned v1, register unsigned v2); + long * P_remset(register long *s, register unsigned val); + int P_setequal(register long *s1, register long *s2); + int P_subset(register long *s1, register long *s2); + long * P_setcpy(register long *d, register long *s); + long * P_expset(register long *d, register long s); + long P_packset(register long *s); + int _OutMem(void); + int _CaseCheck(void); + int _NilCheck(void); + static char * _ShowEscape(char *buf, int code, int ior, char *prefix); + int _Escape(int code); + int _EscIO(int code); + + // data members +protected: + Phreeqc * PhreeqcPtr; + char *inbuf; + linerec *linebase; + varrec *varbase; + looprec *loopbase; + long curline; + linerec *stmtline, *dataline; + tokenrec *stmttok, *datatok, *buf; + bool exitflag; + long EXCP_LINE; + static std::map command_tokens; + int P_escapecode; + int P_ioresult; + + bool phreeqci_gui; + bool parse_all; /* true, most function values set to 1 for testing compilation */ + bool parse_whole_program; + char fnbuf[256]; +#if defined(PHREEQCI_GUI) + HANDLE hInfiniteLoop; + UINT nIDErrPrompt; +#else + IDErr nIDErrPrompt; +#endif + int nErrLineNumber; +}; + +#endif /* _INC_PBasic_H */ diff --git a/phreeqcpp/PHRQ_io_output.cpp b/phreeqcpp/PHRQ_io_output.cpp new file mode 100644 index 00000000..149388af --- /dev/null +++ b/phreeqcpp/PHRQ_io_output.cpp @@ -0,0 +1,401 @@ +#include +#include "Phreeqc.h" +#include "phqalloc.h" + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +warning_msg(const char *err_str) +/* ---------------------------------------------------------------------- */ +{ + if (state == TRANSPORT && transport_warnings == FALSE) + return (OK); + if (state == ADVECTION && advection_warnings == FALSE) + return (OK); + count_warnings++; + if (pr.warnings >= 0) + { + if (count_warnings > pr.warnings) + return (OK); + } + if (phrq_io) + { + if (status_on) + { + phrq_io->screen_msg("\n"); + } + std::ostringstream msg; + msg << "WARNING: " << err_str; + phrq_io->warning_msg(msg.str().c_str()); + status_on = false; + } + + return OK; +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +echo_msg(const char *str) +/* ---------------------------------------------------------------------- */ +{ + if (pr.echo_input == TRUE) + { + if (phrq_io) phrq_io->echo_msg(str); + } +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +set_forward_output_to_log(int value) +/* ---------------------------------------------------------------------- */ +{ + forward_output_to_log = value; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_forward_output_to_log(void) +/* ---------------------------------------------------------------------- */ +{ + return forward_output_to_log; +} + +void Phreeqc:: +fpunchf_heading(const char *name) +{ + if (pr.punch == TRUE && current_selected_output != NULL) + { + punch_msg(name); + } +} +void Phreeqc:: +fpunchf(const char *name, const char *format, double d) +{ + try + { + if (phrq_io) phrq_io->fpunchf(name, format, d); + } + catch(std::bad_alloc) + { + malloc_error(); + } +} +void Phreeqc:: +fpunchf(const char *name, const char *format, char * s) +{ + try + { + if (phrq_io) phrq_io->fpunchf(name, format, s); + } + catch(std::bad_alloc) + { + malloc_error(); + } +} +void Phreeqc:: +fpunchf(const char *name, const char *format, int d) +{ + try + { + if (phrq_io) phrq_io->fpunchf(name, format, d); + } + catch(std::bad_alloc) + { + malloc_error(); + } +} + +void Phreeqc:: +fpunchf_user(int user_index, const char *format, double d) +{ + const char *name; + + if (current_user_punch == NULL) + return; + // check headings + //if (user_index < user_punch_count_headings) + int user_punch_count_headings = (int) current_user_punch->Get_headings().size(); + if (user_index < user_punch_count_headings) + { + //name = user_punch_headings[user_index]; + name = current_user_punch->Get_headings()[user_index].c_str(); + } + else + { + if (fpunchf_user_s_warning == 0) + { + error_string = sformatf( + "USER_PUNCH: Headings count does not match number of calls to PUNCH.\n"); + warning_msg(error_string); + fpunchf_user_s_warning = 1; + } + sprintf(fpunchf_user_buffer, "no_heading_%d", + (user_index - user_punch_count_headings) + 1); + name = fpunchf_user_buffer; + } + try + { + if (phrq_io) phrq_io->fpunchf(name, format, (double) d); + } + catch(std::bad_alloc) + { + malloc_error(); + } +} + +void Phreeqc:: +fpunchf_user(int user_index, const char *format, char * d) +{ + const char *name; + + if (current_user_punch == NULL) + return; + int user_punch_count_headings = (int) current_user_punch->Get_headings().size(); + // check headings + if (user_index < user_punch_count_headings) + { + //name = user_punch_headings[user_index]; + name = current_user_punch->Get_headings()[user_index].c_str(); + } + else + { + if (fpunchf_user_s_warning == 0) + { + error_string = sformatf( + "USER_PUNCH: Headings count does not match number of calls to PUNCH.\n"); + warning_msg(error_string); + fpunchf_user_s_warning = 1; + } + sprintf(fpunchf_user_buffer, "no_heading_%d", + (user_index - user_punch_count_headings) + 1); + name = fpunchf_user_buffer; + } + try + { + if (phrq_io) phrq_io->fpunchf(name, format, d); + } + catch(std::bad_alloc) + { + malloc_error(); + } +} + +int Phreeqc:: +fpunchf_end_row(const char *format) +{ + if (phrq_io) + { + phrq_io->fpunchf_end_row(format); + } + return OK; +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +screen_msg(const char *err_str) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) phrq_io->screen_msg(err_str); +} +// ---------------------------------------------------------------------- */ +// dump file methods +// ---------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- */ +bool Phreeqc:: +dump_open(const char *file_name) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) + return this->phrq_io->dump_open(file_name); + return false; +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +dump_flush(void) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->dump_flush(); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +dump_close(void) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->dump_close(); +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +dump_msg(const char * str) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->dump_msg(str); +} +// ---------------------------------------------------------------------- */ +// error file methods +// ---------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- */ +bool Phreeqc:: +error_open(const char *file_name) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) + return this->phrq_io->error_open(file_name); + return false; +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +error_flush(void) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->error_flush(); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +error_close(void) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->error_close(); +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +error_msg(const char *err_str, bool stop) +/* ---------------------------------------------------------------------- */ +{ + if (get_input_errors() <= 0) + input_error = 1; + if (phrq_io) + { + std::ostringstream msg; + msg << "ERROR: " << err_str << "\n"; + + phrq_io->output_msg(msg.str().c_str()); + phrq_io->log_msg(msg.str().c_str()); + + if (status_on) + { + phrq_io->screen_msg("\n"); + } + status_on = false; + phrq_io->error_msg(msg.str().c_str(), stop); + } + + if (stop) + { + throw PhreeqcStop(); + } +} +// ---------------------------------------------------------------------- */ +// log file methods +// ---------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- */ +bool Phreeqc:: +log_open(const char *file_name) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) + return this->phrq_io->log_open(file_name); + return false; +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +log_flush(void) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->log_flush(); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +log_close(void) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->log_close(); +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +log_msg(const char * str) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->log_msg(str); +} +// ---------------------------------------------------------------------- */ +// output_temp file methods +// ---------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- */ +bool Phreeqc:: +output_open(const char *file_name) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) + return this->phrq_io->output_open(file_name); + return false; +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +output_flush(void) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->output_flush(); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +output_close(void) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->output_close(); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +output_msg(const char * str) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) + { + if (get_forward_output_to_log()) + { + phrq_io->log_msg(str); + } + else + { + phrq_io->output_msg(str); + } + } +} +// ---------------------------------------------------------------------- */ +// punch file methods +// ---------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- */ +bool Phreeqc:: +punch_open(const char *file_name, int n_user) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) + return this->phrq_io->punch_open(file_name, std::ios_base::out, n_user); + return false; +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +punch_flush(void) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->punch_flush(); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +punch_close(void) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->punch_close(); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +punch_msg(const char * str) +/* ---------------------------------------------------------------------- */ +{ + if (phrq_io) this->phrq_io->punch_msg(str); +} diff --git a/phreeqcpp/PPassemblage.cxx b/phreeqcpp/PPassemblage.cxx new file mode 100644 index 00000000..aed9983b --- /dev/null +++ b/phreeqcpp/PPassemblage.cxx @@ -0,0 +1,373 @@ +// PPassemblage.cxx: implementation of the cxxPPassemblage class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "PPassemblage.h" +#include "cxxMix.h" +#include "phqalloc.h" + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxPPassemblage::cxxPPassemblage(PHRQ_io * io) + // + // default constructor for cxxPPassemblage + // +: cxxNumKeyword(io) +{ + new_def = false; + eltList.type = cxxNameDouble::ND_ELT_MOLES; +} + +cxxPPassemblage::cxxPPassemblage(const std::map < int, + cxxPPassemblage > &entities, cxxMix & mix, + int l_n_user, PHRQ_io * io): +cxxNumKeyword(io) +{ + this->n_user = this->n_user_end = l_n_user; + eltList.type = cxxNameDouble::ND_ELT_MOLES; +// +// Mix +// + const std::map < int, LDBLE >&mixcomps = mix.Get_mixComps(); + std::map < int, LDBLE >::const_iterator it; + for (it = mixcomps.begin(); it != mixcomps.end(); it++) + { + if (entities.find(it->first) != entities.end()) + { + const cxxPPassemblage *entity_ptr = + &(entities.find(it->first)->second); + this->add(*entity_ptr, it->second); + } + } +} + +cxxPPassemblage::~cxxPPassemblage() +{ +} + +void +cxxPPassemblage::dump_xml(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // PPassemblage element and attributes + s_oss << indent0; + s_oss << "eltList.dump_xml(s_oss, indent + 1); + + // ppAssemblageComps + s_oss << indent1; + s_oss << "::const_iterator it = + pp_assemblage_comps.begin(); it != pp_assemblage_comps.end(); ++it) + { + (*it).second.dump_xml(s_oss, indent + 2); + } +} + +void +cxxPPassemblage::dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // PPassemblage element and attributes + s_oss << indent0; + int n_user_local = (n_out != NULL) ? *n_out : this->n_user; + s_oss << "EQUILIBRIUM_PHASES_RAW " << n_user_local << " " << this-> + description << "\n"; + + + s_oss << indent1 << "# EXCHANGE_MODIFY candidates; use new_def=true #\n"; + s_oss << indent1 << "-new_def " << 0 << "\n"; + for (std::map < std::string, cxxPPassemblageComp >::const_iterator it = + pp_assemblage_comps.begin(); it != pp_assemblage_comps.end(); ++it) + { + s_oss << indent1; + s_oss << "-component " << (*it).second.Get_name() << "\n"; + (*it).second.dump_raw(s_oss, indent + 2); + } + s_oss << indent1; + s_oss << "-eltList # List of all elements in phases and alternate reactions\n"; + this->eltList.dump_raw(s_oss, indent + 2); + + s_oss << indent1 << "# PPassemblage workspace variables #\n"; + s_oss << indent1 << "-assemblage_totals" << "\n"; + this->assemblage_totals.dump_raw(s_oss, indent + 1); +} + +void +cxxPPassemblage::read_raw(CParser & parser, bool check) +{ + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + bool useLastLine(false); + + // Read PPassemblage number and description + this->read_number_description(parser); + this->Set_new_def(false); + + opt_save = CParser::OPT_ERROR; + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + opt = parser.getOptionFromLastLine(vopts, next_char, true); + } + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + useLastLine = false; + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser. + error_msg("Unknown input in EQUILIBRIUM_PHASES_RAW keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + useLastLine = false; + break; + + case 0: // eltList + if (this->eltList.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg("Expected element name and moles for totals.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 0; + break; + case 1: // component + { + std::string str; + if (!(parser.get_iss() >> str)) + { + parser.incr_input_error(); + parser.error_msg("Expected string value for component name.", + PHRQ_io::OT_CONTINUE); + } + else + { + cxxPPassemblageComp temp_comp(this->io); + temp_comp.Set_name(str.c_str()); + cxxPPassemblageComp *comp_ptr = this->Find(str); + if (comp_ptr) + { + temp_comp = *comp_ptr; + } + temp_comp.read_raw(parser, check); + this->pp_assemblage_comps[str] = temp_comp; + useLastLine = true; + } + } + break; + case 2: // new_def + if (!(parser.get_iss() >> this->new_def)) + { + this->new_def = false; + parser.incr_input_error(); + parser. + error_msg + ("Expected boolean value for new_def in PPassemblage.", + PHRQ_io::OT_CONTINUE); + } + break; + case 3: // assemblage_totals + if (this->assemblage_totals.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected element name and molality for PPassemblage totals.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 3; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } +} + +void +cxxPPassemblage::totalize(Phreeqc * phreeqc_ptr) +{ + this->assemblage_totals.clear(); + // component structures + for (std::map < std::string, cxxPPassemblageComp >::iterator it = + pp_assemblage_comps.begin(); it != pp_assemblage_comps.end(); ++it) + { + (*it).second.totalize(phreeqc_ptr); + this->assemblage_totals.add_extensive((*it).second.Get_totals(), 1.0); + } + return; +} +void +cxxPPassemblage::add(const cxxPPassemblage & addee, LDBLE extensive) + // + // Add to existing ppassemblage to "this" ppassemblage + // +{ + if (extensive == 0.0) + return; + for (std::map < std::string, cxxPPassemblageComp >::const_iterator itadd = addee.pp_assemblage_comps.begin(); + itadd != addee.pp_assemblage_comps.end(); ++itadd) + { + bool found = false; + for (std::map < std::string, cxxPPassemblageComp >::iterator it = + this->pp_assemblage_comps.begin(); + it != this->pp_assemblage_comps.end(); ++it) + { + if ((*it).second.Get_name() == itadd->second.Get_name()) + { + (*it).second.add((*itadd).second, extensive); + found = true; + break; + } + } + if (!found) + { + cxxPPassemblageComp entity = (*itadd).second; + entity.multiply(extensive); + std::string str(entity.Get_name()); + this->pp_assemblage_comps[str] = entity; + } + } + //cxxNameDouble eltList; + this->eltList.add_extensive(addee.eltList, extensive); +} +#ifdef SKIP +cxxPPassemblageComp * cxxPPassemblage:: +Find(const std::string name_in) +{ + std::string name(name_in); + Utilities::str_tolower(name); + + cxxPPassemblageComp * comp = NULL; + std::map::iterator it; + it = this->pp_assemblage_comps.begin(); + for ( ; it != this->pp_assemblage_comps.end(); it++) + { + std::string pname(it->first); + Utilities::str_tolower(pname); + if (name == pname) + { + comp = &it->second; + break; + } + } + return comp; +} +#endif +cxxPPassemblageComp * cxxPPassemblage:: +Find(const std::string name_in) +{ + cxxPPassemblageComp * comp = NULL; + std::map::iterator it; + it = this->pp_assemblage_comps.begin(); + for ( ; it != this->pp_assemblage_comps.end(); it++) + { + if (Utilities::strcmp_nocase(name_in.c_str(), it->first.c_str()) == 0) + { + comp = &it->second; + break; + } + } + return comp; +} +/* ---------------------------------------------------------------------- */ +void +cxxPPassemblage::Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles) +/* ---------------------------------------------------------------------- */ +{ + /* int n_user; */ + ints.push_back(this->n_user); + ints.push_back(this->new_def ? 1 : 0); + ints.push_back((int) this->pp_assemblage_comps.size()); + for (std::map < std::string, cxxPPassemblageComp >::iterator it = + this->pp_assemblage_comps.begin(); it != this->pp_assemblage_comps.end(); + it++) + { + (*it).second.Serialize(dictionary, ints, doubles); + } + this->eltList.Serialize(dictionary, ints, doubles); + this->assemblage_totals.Serialize(dictionary, ints, doubles); +} + +/* ---------------------------------------------------------------------- */ +void +cxxPPassemblage::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +/* ---------------------------------------------------------------------- */ +{ + /* int n_user; */ + this->n_user = ints[ii++]; + this->n_user_end = this->n_user; + this->description = " "; + + this->new_def = (ints[ii++] != 0); + int count = ints[ii++]; + this->pp_assemblage_comps.clear(); + for (int n = 0; n < count; n++) + { + cxxPPassemblageComp ppc; + ppc.Deserialize(dictionary, ints, doubles, ii, dd); + std::string str(ppc.Get_name()); + this->pp_assemblage_comps[str] = ppc; + } + this->eltList.Deserialize(dictionary, ints, doubles, ii, dd); + this->assemblage_totals.Deserialize(dictionary, ints, doubles, ii, dd); +} +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("eltlist"), // 0 + std::vector< std::string >::value_type("component"), // 1 + std::vector< std::string >::value_type("new_def"), // 2 + std::vector< std::string >::value_type("assemblage_totals") // 3 +}; +const std::vector< std::string > cxxPPassemblage::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/PPassemblage.h b/phreeqcpp/PPassemblage.h new file mode 100644 index 00000000..5b735ad3 --- /dev/null +++ b/phreeqcpp/PPassemblage.h @@ -0,0 +1,70 @@ +#if !defined(PPASSEMBLAGE_H_INCLUDED) +#define PPASSEMBLAGE_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NumKeyword.h" +#include "PPassemblageComp.h" +class cxxMix; + +class cxxPPassemblage:public cxxNumKeyword +{ + + public: + cxxPPassemblage(PHRQ_io * io=NULL); + cxxPPassemblage(const std::map < int, cxxPPassemblage > &entity_map, + cxxMix & mx, int n_user, PHRQ_io * io=NULL); + ~cxxPPassemblage(); + + void dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out=NULL) const; + + void read_raw(CParser & parser, bool check = true); + + const cxxNameDouble & Get_assemblage_totals() const + { + return this->assemblage_totals; + }; + const cxxNameDouble & Get_eltList() const + { + return this->eltList; + }; + void Set_eltList(cxxNameDouble & nd) {this->eltList = nd;} + std::map & Get_pp_assemblage_comps() + { + return this->pp_assemblage_comps; + }; + const std::map & Get_pp_assemblage_comps() const + { + return this->pp_assemblage_comps; + }; + void Set_pp_assemblage_comps(std::map & c) + { + this->pp_assemblage_comps = c; + }; + bool Get_new_def(void) const {return this->new_def;} + void Set_new_def(bool tf) {this->new_def = tf;} + + cxxPPassemblageComp *Find(const std::string name); + + void totalize(Phreeqc * phreeqc_ptr); + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + void add(const cxxPPassemblage & addee, LDBLE extensive); + // not written + void dump_xml(std::ostream & os, unsigned int indent = 0) const; + +protected: + bool new_def; + std::map pp_assemblage_comps; + cxxNameDouble eltList; // list of elements in phases (and alternate reactions) + cxxNameDouble assemblage_totals; // after totalize, total moles of elements in the PPassemblage + const static std::vector < std::string > vopts; +}; + +#endif // !defined(PPASSEMBLAGE_H_INCLUDED) diff --git a/phreeqcpp/PPassemblageComp.cxx b/phreeqcpp/PPassemblageComp.cxx new file mode 100644 index 00000000..ea35b2b6 --- /dev/null +++ b/phreeqcpp/PPassemblageComp.cxx @@ -0,0 +1,433 @@ +// PPassemblageComp.cxx: implementation of the cxxPPassemblageComp class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "PPassemblageComp.h" +#include "Dictionary.h" +#include "phqalloc.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxPPassemblageComp::cxxPPassemblageComp(PHRQ_io *io) +: +PHRQ_base(io) + // + // default constructor for cxxPPassemblageComp + // +{ + si = 0; + si_org = 0; + moles = 10; + delta = 0; + initial_moles = 0; + force_equality = false; + dissolve_only = false; + precipitate_only = false; +} + +cxxPPassemblageComp::~cxxPPassemblageComp() +{ +} + +void +cxxPPassemblageComp::dump_xml(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Pure_Phase element and attributes + + s_oss << indent0 << "name=\"" << this->name << "\"" << "\n"; + s_oss << indent0 << "add_formula=\"" << this->add_formula << "\"" << "\n"; + s_oss << indent0 << "si=\"" << this->si << "\"" << "\n"; + s_oss << indent0 << "si_org=\"" << this->si_org << "\"" << "\n"; + s_oss << indent0 << "moles=\"" << this->moles << "\"" << "\n"; + s_oss << indent0 << "delta=\"" << this->delta << "\"" << "\n"; + s_oss << indent0 << "initial_moles=\"" << this-> + initial_moles << "\"" << "\n"; + s_oss << indent0 << "force_equality=\"" << this-> + force_equality << "\"" << "\n"; + s_oss << indent0 << "dissolve_only=\"" << this-> + dissolve_only << "\"" << "\n"; + s_oss << indent0 << "precipitate_only=\"" << this-> + precipitate_only << "\"" << "\n"; + +} + +void +cxxPPassemblageComp::dump_raw(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Pure_Phase element and attributes + s_oss << indent1 << "# EQUILIBRIUM_PHASES_MODIFY candidate identifiers #\n"; + if (this->add_formula.size() != 0) + s_oss << indent1 << "-add_formula " << this->add_formula << "\n"; + s_oss << indent1 << "-si " << this->si << "\n"; + s_oss << indent1 << "-moles " << this->moles << "\n"; + s_oss << indent1 << "-force_equality " << this->force_equality << "\n"; + s_oss << indent1 << "-dissolve_only " << this->dissolve_only << "\n"; + s_oss << indent1 << "-precipitate_only " << this->precipitate_only << "\n"; + + s_oss << indent1 << "# PPassemblage workspace variables #\n"; + s_oss << indent1 << "-si_org " << this->si_org << "\n"; + s_oss << indent1 << "-delta " << this->delta << "\n"; + s_oss << indent1 << "-initial_moles " << this->initial_moles << "\n"; + s_oss << indent1 << "-totals " << "\n"; + this->totals.dump_raw(s_oss, indent + 2); +} + +void +cxxPPassemblageComp::read_raw(CParser & parser, bool check) +{ + std::string str; + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + opt_save = CParser::OPT_ERROR; + bool si_defined(false); + bool moles_defined(false); + bool delta_defined(false); + bool initial_moles_defined(false); + bool dissolve_only_defined(false); + bool force_equality_defined(false); + + for (;;) + { + int opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_KEYWORD; + // Allow return to Exchange for more processing + break; + + case 0: // name + parser.warning_msg("-name ignored. Name is defined with -component."); + break; + + case 1: // add_formula + if (!(parser.get_iss() >> str)) + { + this->add_formula.clear(); + parser.incr_input_error(); + parser.error_msg("Expected string value for add_formula.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->add_formula = str; + } + break; + + case 2: // si + if (!(parser.get_iss() >> this->si)) + { + this->si = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for si.", + PHRQ_io::OT_CONTINUE); + } + si_defined = true; + break; + + case 3: // moles + if (!(parser.get_iss() >> this->moles)) + { + this->moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for moles.", + PHRQ_io::OT_CONTINUE); + } + moles_defined = true; + break; + + case 4: // delta + if (!(parser.get_iss() >> this->delta)) + { + this->delta = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for delta.", + PHRQ_io::OT_CONTINUE); + } + delta_defined = true; + break; + + case 5: // initial_moles + if (!(parser.get_iss() >> this->initial_moles)) + { + this->initial_moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for initial_moles.", + PHRQ_io::OT_CONTINUE); + } + initial_moles_defined = true; + break; + + + case 6: // dissolve_only + if (!(parser.get_iss() >> this->dissolve_only)) + { + this->dissolve_only = false; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for dissolve_only.", + PHRQ_io::OT_CONTINUE); + } + dissolve_only_defined = true; + if (this->dissolve_only) + { + this->precipitate_only = false; + } + break; + + case 7: // force_equality + if (!(parser.get_iss() >> this->force_equality)) + { + this->force_equality = false; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for force_equality.", + PHRQ_io::OT_CONTINUE); + } + force_equality_defined = true; + break; + + case 8: // precipitate_only + if (!(parser.get_iss() >> this->precipitate_only)) + { + this->precipitate_only = false; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for precipitate_only.", + PHRQ_io::OT_CONTINUE); + } + if (this->precipitate_only) + { + this->dissolve_only = false; + } + break; + case 9: // si_org + if (!(parser.get_iss() >> this->si_org)) + { + this->si_org = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for si_org.", + PHRQ_io::OT_CONTINUE); + } + break; + case 10: // totals + if (this->totals.read_raw(parser, next_char) != CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected element name and molality for Surface totals.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 10; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + // members that must be defined + if (check) + { + if (si_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Si not defined for PPassemblageComp input.", + PHRQ_io::OT_CONTINUE); + } + if (moles_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Moles not defined for PPassemblageComp input.", + PHRQ_io::OT_CONTINUE); + } + if (delta_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Delta not defined for PPassemblageComp input.", + PHRQ_io::OT_CONTINUE); + } + if (initial_moles_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Initial_moles not defined for PPassemblageComp input.", + PHRQ_io::OT_CONTINUE); + } + if (dissolve_only_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Dissolve_only not defined for PPassemblageComp input.", + PHRQ_io::OT_CONTINUE); + } + if (force_equality_defined == false) + { + parser.incr_input_error(); + parser.error_msg + ("Force_equality not defined for PPassemblageComp input.", + PHRQ_io::OT_CONTINUE); + } + } +} + +void +cxxPPassemblageComp::totalize(Phreeqc * phreeqc_ptr) +{ + this->totals.clear(); + // component structures + if (this->add_formula.size() != 0) + return; + struct phase *phase_ptr; + int l; + phase_ptr = phreeqc_ptr-> phase_bsearch(this->name.c_str(), &l, FALSE); + if (phase_ptr != NULL) + { + cxxNameDouble phase_formula(phase_ptr->next_elt); + this->totals.add_extensive(phase_formula, this->moles); + } + else + { + assert(false); + } + return; +} + + +void +cxxPPassemblageComp::add(const cxxPPassemblageComp & addee, LDBLE extensive) +{ + LDBLE ext1, ext2, f1, f2; + if (extensive == 0.0) + return; + if (addee.name.size() == 0) + return; + // this and addee must have same name + // otherwise generate a new PPassemblagComp with multiply + + ext1 = this->moles; + ext2 = addee.moles * extensive; + if (ext1 + ext2 != 0) + { + f1 = ext1 / (ext1 + ext2); + f2 = ext2 / (ext1 + ext2); + } + else + { + f1 = 0.5; + f2 = 0.5; + } + + if (this->name.size() == 0 && addee.name.size() == 0) + { + return; + } + assert(this->name == addee.name); + if (this->add_formula != addee.add_formula) + { + std::ostringstream oss; + oss << + "Cannot mix two Equilibrium_phases with differing add_formulae., " + << this->name; + error_msg(oss.str().c_str(), CONTINUE); + return; + } + this->si = this->si * f1 + addee.si * f2; + this->si_org = this->si_org * f1 + addee.si_org * f2; + this->moles += addee.moles * extensive; + this->delta += addee.delta * extensive; + this->initial_moles += addee.initial_moles * extensive; +} + +void +cxxPPassemblageComp::multiply(LDBLE extensive) +{ + this->moles *= extensive; + this->delta *= extensive; + this->initial_moles *= extensive; +} +void +cxxPPassemblageComp::Serialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles) +{ + ints.push_back(dictionary.Find(this->name)); + ints.push_back(dictionary.Find(this->add_formula)); + doubles.push_back(this->si); + doubles.push_back(this->si_org); + doubles.push_back(this->moles); + doubles.push_back(this->delta); + doubles.push_back(this->initial_moles); + ints.push_back( this->force_equality ? 1 : 0); + ints.push_back(this->dissolve_only ? 1 : 0); + ints.push_back(this->precipitate_only ? 1 : 0); + this->totals.Serialize(dictionary, ints, doubles); +} + +void +cxxPPassemblageComp::Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd) +{ + this->name = dictionary.GetWords()[ints[ii++]]; + this->add_formula = dictionary.GetWords()[ints[ii++]]; + this->si = doubles[dd++]; + this->si_org = doubles[dd++]; + this->moles = doubles[dd++]; + this->delta = doubles[dd++]; + this->initial_moles = doubles[dd++]; + this->force_equality = (ints[ii++] != 0); + this->dissolve_only = (ints[ii++] != 0); + this->precipitate_only = (ints[ii++] != 0); + this->totals.Deserialize(dictionary, ints, doubles, ii, dd); +} + + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("name"), // 0 + std::vector< std::string >::value_type("add_formula"), // 1 + std::vector< std::string >::value_type("si"), // 2 + std::vector< std::string >::value_type("moles"), // 3 + std::vector< std::string >::value_type("delta"), // 4 + std::vector< std::string >::value_type("initial_moles"), // 5 + std::vector< std::string >::value_type("dissolve_only"), // 6 + std::vector< std::string >::value_type("force_equality"), // 7 + std::vector< std::string >::value_type("precipitate_only"), // 8 + std::vector< std::string >::value_type("si_org"), // 9 + std::vector< std::string >::value_type("totals") // 10 +}; +const std::vector< std::string > cxxPPassemblageComp::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/PPassemblageComp.h b/phreeqcpp/PPassemblageComp.h new file mode 100644 index 00000000..318707c1 --- /dev/null +++ b/phreeqcpp/PPassemblageComp.h @@ -0,0 +1,83 @@ +#if !defined(PPASSEMBLAGECOMP_H_INCLUDED) +#define PPASSEMBLAGECOMP_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NameDouble.h" + +class cxxPPassemblageComp: public PHRQ_base +{ + + public: + cxxPPassemblageComp(PHRQ_io *io=NULL); + virtual ~cxxPPassemblageComp(); + + void dump_xml(std::ostream & os, unsigned int indent = 0) const; + void dump_raw(std::ostream & s_oss, unsigned int indent) const; + void read_raw(CParser & parser, bool check = true); + + const std::string &Get_name() const {return this->name;} + void Set_name(const char * s) + { + if(s != NULL) + this->name = std::string(s); + else + this->name.clear(); + } + const std::string &Get_add_formula() const {return this->add_formula;} + void Set_add_formula(const char * s) + { + if(s != NULL) + this->add_formula = std::string(s); + else + this->add_formula.clear(); + } + + void totalize(Phreeqc * phreeqc_ptr); + const cxxNameDouble & Get_totals() const {return (this->totals);} + void Get_totals(cxxNameDouble & nd) {this->totals = nd;} + LDBLE Get_si() const {return this->si;} + void Set_si(LDBLE t) {this->si = t;} + LDBLE Get_si_org() const {return this->si_org;} + void Set_si_org(LDBLE t) {this->si_org = t;} + LDBLE Get_moles() const {return this->moles;} + void Set_moles(LDBLE t) {this->moles = t;} + LDBLE Get_delta() const {return this->delta;} + void Set_delta(LDBLE t) {this->delta = t;} + LDBLE Get_initial_moles() const {return this->initial_moles;} + void Set_initial_moles(LDBLE t) {this->initial_moles = t;} + + bool Get_force_equality() const {return this->force_equality;} + void Set_force_equality(bool tf) {this->force_equality = tf;} + bool Get_dissolve_only() const {return this->dissolve_only;} + void Set_dissolve_only(bool tf) {this->dissolve_only = tf;} + bool Get_precipitate_only() const {return this->precipitate_only;} + void Set_precipitate_only(bool tf) {this->precipitate_only = tf;} + + void add(const cxxPPassemblageComp & comp, LDBLE extensive); + void multiply(LDBLE extensive); + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + std::string name; + std::string add_formula; + LDBLE si; + LDBLE si_org; + LDBLE moles; + LDBLE delta; + LDBLE initial_moles; + bool force_equality; + bool dissolve_only; + bool precipitate_only; + cxxNameDouble totals; + const static std::vector < std::string > vopts; +public: + +}; + +#endif // !defined(PPASSEMBLAGECOMP_H_INCLUDED) diff --git a/phreeqcpp/Phreeqc.cpp b/phreeqcpp/Phreeqc.cpp new file mode 100644 index 00000000..55e52146 --- /dev/null +++ b/phreeqcpp/Phreeqc.cpp @@ -0,0 +1,2555 @@ +#include "Phreeqc.h" +#include // std::replace + +#include "NameDouble.h" +#include "Solution.h" +#include "Reaction.h" +#include "PPassemblage.h" +#include "Exchange.h" +#include "Surface.h" +#include "GasPhase.h" +#include "SSassemblage.h" +#include "cxxKinetics.h" +#include "phqalloc.h" +#include "PBasic.h" +#include "Temperature.h" +#include "SSassemblage.h" + +const struct const_iso Phreeqc::iso_defaults[] = { + {"13C", -10, 1}, + {"13C(4)", -10, 1}, + {"13C(-4)", -50, 5}, + {"34S", 10, 1}, + {"34S(6)", 10, 1}, + {"34S(-2)", -30, 5}, + {"2H", -28, 1}, + {"2H(1)", -28, 1}, + {"2H(0)", -28, 1}, + {"18O", -5, .1}, + {"18O(-2)", -5, .1}, + {"18O(0)", -5, .1}, + {"87Sr", .71, .01}, + {"11B", 20, 5} +}; + +const int Phreeqc::count_iso_defaults = (sizeof(iso_defaults) / sizeof(struct const_iso)); + +Phreeqc::~Phreeqc(void) +{ + + clean_up(); + + PHRQ_free_all(); + if (phrq_io == &ioInstance) + { + this->phrq_io->clear_istream(); + this->phrq_io->close_ostreams(); + } +} + +void Phreeqc::set_phast(int tf) +{ + this->phast = tf; +} +size_t Phreeqc::list_components(std::list &list_c) +/* + * Find all elements in any class definition + */ +{ + cxxNameDouble accumulator; + //accumulator.add("H", 1); + //accumulator.add("O", 1); + + // solutions + { + std::map::const_iterator cit = Rxn_solution_map.begin(); + for (; cit != Rxn_solution_map.end(); cit++) + { + cxxSolution entity(cit->second); + accumulator.add_extensive(entity.Get_totals(), 1.0); + } + } + + // irreversible reactions + { + std::map::const_iterator cit = Rxn_reaction_map.begin(); + for (; cit != Rxn_reaction_map.end(); cit++) + { + cxxReaction r_ptr(cit->second); + reaction_calc(&r_ptr); + accumulator.add_extensive(r_ptr.Get_elementList(), 1.0); + } + } + + // pure phases + { + std::map::const_iterator cit = Rxn_pp_assemblage_map.begin(); + for (; cit != Rxn_pp_assemblage_map.end(); cit++) + { + cxxPPassemblage entity = cit->second; + entity.totalize(this); + accumulator.add_extensive(entity.Get_eltList(), 1.0); + } + } + // exchangers + { + std::map::const_iterator cit = Rxn_exchange_map.begin(); + for (; cit != Rxn_exchange_map.end(); cit++) + { + cxxExchange entity = cit->second; + entity.totalize(); + accumulator.add_extensive(entity.Get_totals(), 1.0); + } + } + + // surfaces + { + std::map::const_iterator cit = Rxn_surface_map.begin(); + for (; cit != Rxn_surface_map.end(); cit++) + { + cxxSurface entity = cit->second; + entity.totalize(); + accumulator.add_extensive(entity.Get_totals(), 1.0); + } + } + // gas phases + { + std::map::const_iterator cit = Rxn_gas_phase_map.begin(); + for (; cit != Rxn_gas_phase_map.end(); cit++) + { + cxxGasPhase entity = cit->second; + entity.totalize(this); + accumulator.add_extensive(entity.Get_totals(), 1.0); + } + } + + // solid-solutions + { + std::map::const_iterator cit = Rxn_ss_assemblage_map.begin(); + for (; cit != Rxn_ss_assemblage_map.end(); cit++) + { + cxxSSassemblage entity = cit->second; + entity.totalize(this); + accumulator.add_extensive(entity.Get_totals(), 1.0); + } + } + // kinetics + { + std::map::iterator it = Rxn_kinetics_map.begin(); + for (; it != Rxn_kinetics_map.end(); it++) + { + calc_dummy_kinetic_reaction_tally(&(it->second)); + cxxKinetics entity = it->second; + accumulator.add_extensive(entity.Get_totals(), 1.0); + } + } + // Put in all primaries + cxxNameDouble::iterator it; + for (it = accumulator.begin(); it != accumulator.end(); it++) + { + if (it->first == "Charge") continue; + char string[MAX_LENGTH]; + strcpy(string, it->first.c_str()); + struct master *master_ptr = master_bsearch_primary(string); + if (master_ptr == NULL) continue; + if (master_ptr->type != AQ) continue; + accumulator.add(master_ptr->elt->name, 1); + } + // print list + for (it = accumulator.begin(); it != accumulator.end(); it++) + { + struct master *master_ptr = master_bsearch(it->first.c_str()); + if (master_ptr == NULL) continue; + if (master_ptr->type != AQ) continue; + if (master_ptr->primary == 0) continue; + if (it->first == "Charge") continue; + if (it->first == "O") continue; + if (it->first == "H") continue; + list_c.push_back(it->first); + } + return(list_c.size()); +} +Phreeqc::Phreeqc(PHRQ_io *io) +{ + // phrq_io + if (io) + { + this->phrq_io = io; + } + else + { + this->phrq_io = &this->ioInstance; + } + // auto PHRQ_io ioInstance; + + // initialize data members + init(); + +#if defined(SWIG) || defined(SWIG_IPHREEQC) + basicCallback = NULL; +#endif +} +void Phreeqc::init(void) +{ + same_model = FALSE; + current_tc = NAN; + current_pa = NAN; + current_mu = NAN; + mu_terms_in_logk = true; + current_A = 0.0; + current_x = 0.0; + fix_current = 0.0; + /* ---------------------------------------------------------------------- + * STRUCTURES + * ---------------------------------------------------------------------- */ +/* + * last model + */ + last_model.force_prep = TRUE; + last_model.temperature = -100; + last_model.pressure = 0; + last_model.count_exchange = -1; + last_model.exchange = NULL; + last_model.count_kinetics = -1; + last_model.kinetics = NULL; + last_model.count_gas_phase = -1; + last_model.gas_phase = NULL; + last_model.count_ss_assemblage = -1; + last_model.ss_assemblage = NULL; + last_model.count_pp_assemblage = -1; + last_model.pp_assemblage = NULL; + last_model.add_formula = NULL; + last_model.si = NULL; + last_model.dl_type = cxxSurface::NO_DL; + last_model.surface_type = cxxSurface::UNKNOWN_DL; + last_model.only_counter_ions = FALSE; + last_model.thickness = 1e-8; + last_model.count_surface_comp = -1; + last_model.surface_comp = NULL; + last_model.count_surface_charge = -1; + last_model.surface_charge = NULL; + + current_selected_output = NULL; + current_user_punch = NULL; + high_precision = false; +#ifdef SKIP + //struct punch punch; +/* + * Initialize punch + */ + punch.in = FALSE; + punch.count_totals = 0; + punch.totals = 0; + punch.count_molalities = 0; + punch.molalities = 0; + punch.count_activities = 0; + punch.activities = 0; + punch.count_pure_phases = 0; + punch.pure_phases = 0; + punch.count_si = 0; + punch.si = 0; + punch.count_gases = 0; + punch.gases = 0; + punch.count_s_s = 0; + punch.s_s = 0; + punch.count_kinetics = 0; + punch.kinetics = 0; + punch.count_isotopes = 0; + punch.isotopes = 0; + punch.count_calculate_values = 0; + punch.calculate_values = 0; + punch.inverse = TRUE; + punch.sim = TRUE; + punch.state = TRUE; + punch.soln = TRUE; + punch.dist = TRUE; + punch.time = TRUE; + punch.step = TRUE; + punch.rxn = FALSE; + punch.temp = FALSE; + punch.ph = TRUE; + punch.pe = TRUE; + punch.alk = FALSE; + punch.mu = FALSE; + punch.water = FALSE; + punch.high_precision = FALSE; + punch.user_punch = TRUE; + punch.charge_balance = FALSE; + punch.percent_error = FALSE; +#endif + + MIN_LM = -30.0; /* minimum log molality allowed before molality set to zero */ + LOG_ZERO_MOLALITY = -30; /* molalities <= LOG_ZERO_MOLALITY are considered equal to zero */ + MIN_RELATED_LOG_ACTIVITY = -30; + MIN_TOTAL = 1e-25; + MIN_TOTAL_SS = MIN_TOTAL/100; + MIN_RELATED_SURFACE = MIN_TOTAL*100; + // auto Rxn_temperature_map; + // auto Rxn_pressure_map; + + /* ---------------------------------------------------------------------- + * Surface + * --------------------------------------------------------------------- */ + g_iterations = -1; + G_TOL = 1e-8; + // auto Rxn_surface_map; + // auto charge_group_map; + change_surf_count = 0; + change_surf = NULL; + /* ---------------------------------------------------------------------- + * Exchange + * ---------------------------------------------------------------------- */ + // auto Rxn_exchange_map; + + /* ---------------------------------------------------------------------- + * Kinetics + * ---------------------------------------------------------------------- */ + // auto Rxn_kinetics_map; + + /*---------------------------------------------------------------------- + * Save + *---------------------------------------------------------------------- */ + count_save_values = 0; + save_values = NULL; + save_init(-1); // set initial save values + + // auto use + + // copier structures + copy_solution.n_user = copy_solution.start = copy_solution.end = 0; + copy_solution.count = copy_solution.max = 0; + copy_pp_assemblage.n_user = copy_pp_assemblage.start = copy_pp_assemblage.end = 0; + copy_pp_assemblage.count = copy_pp_assemblage.max = 0; + copy_exchange.n_user = copy_exchange.start = copy_exchange.end = 0; + copy_exchange.count = copy_exchange.max = 0; + copy_surface.n_user = copy_surface.start = copy_surface.end = 0; + copy_surface.count = copy_surface.max = 0; + copy_ss_assemblage.n_user = copy_ss_assemblage.start = copy_ss_assemblage.end = 0; + copy_ss_assemblage.count = copy_ss_assemblage.max = 0; + copy_gas_phase.n_user = copy_gas_phase.start = copy_gas_phase.end = 0; + copy_gas_phase.count = copy_gas_phase.max = 0; + copy_kinetics.n_user = copy_kinetics.start = copy_kinetics.end = 0; + copy_kinetics.count = copy_kinetics.max = 0; + copy_mix.n_user = copy_mix.start = copy_mix.end = 0; + copy_mix.count = copy_mix.max = 0; + copy_reaction.n_user = copy_reaction.start = copy_reaction.end = 0; + copy_reaction.count = copy_reaction.max = 0; + copy_temperature.n_user = copy_temperature.start = copy_temperature.end = 0; + copy_temperature.count = copy_temperature.max = 0; + copy_pressure.n_user = copy_pressure.start = copy_pressure.end = 0; + copy_pressure.count = copy_pressure.max = 0; + /*---------------------------------------------------------------------- + * Inverse + *---------------------------------------------------------------------- */ + inverse = NULL; + count_inverse = 0; + /*---------------------------------------------------------------------- + * Mix + *---------------------------------------------------------------------- */ + // auto Rxn_mix_map; + // auto Dispersion_mix_map; + // auto Rxn_solution_mix_map; + // auto Rxn_exchange_mix_map; + // auto Rxn_gas_phase_mix_map; + // auto Rxn_kinetics_mix_map; + // auto Rxn_pp_assemblage_mix_map; + // auto Rxn_ss_assemblage_mix_map; + // auto Rxn_surface_mix_map; + /*---------------------------------------------------------------------- + * Irreversible reaction + *---------------------------------------------------------------------- */ + run_cells_one_step = false; + // auto Rxn_reaction_map; + /*---------------------------------------------------------------------- + * Gas phase + *---------------------------------------------------------------------- */ + // auto Rxn_gas_phase_map; + /*---------------------------------------------------------------------- + * Solid solution + *---------------------------------------------------------------------- */ + // auto Rxn_ss_assemblage_map; + /*---------------------------------------------------------------------- + * Pure-phase assemblage + *---------------------------------------------------------------------- */ + // auto Rxn_pp_assemblage_map; + /*---------------------------------------------------------------------- + * Species_list + *---------------------------------------------------------------------- */ + count_species_list = 0; + max_species_list = 0; + species_list = NULL; + /*---------------------------------------------------------------------- + * Jacobian and Mass balance lists + *---------------------------------------------------------------------- */ + count_sum_jacob0 = 0; + max_sum_jacob0 = 0; + sum_jacob0 = NULL; + count_sum_mb1 = 0; + max_sum_mb1 = 0; + sum_mb1 = NULL; + count_sum_jacob1 = 0; + max_sum_jacob1 = 0; + sum_jacob1 = NULL; + count_sum_mb2 = 0; + max_sum_mb2 = 0; + sum_mb2 = NULL; + count_sum_jacob2 = 0; + max_sum_jacob2 = 0; + sum_jacob2 = NULL; + count_sum_delta = 0; + max_sum_delta = 0; + sum_delta = NULL; + /*---------------------------------------------------------------------- + * Solution + *---------------------------------------------------------------------- */ + // auto Rxn_solution_map; + // auto unnumbered_solutions; + save_species = false; + /*---------------------------------------------------------------------- + * Global solution + *---------------------------------------------------------------------- */ + title_x = NULL; + new_x = FALSE; + description_x = NULL; + tc_x = 0; + tk_x = 0; + patm_x = 1; + last_patm_x = 1; + potV_x = 0; + numerical_fixed_volume = false; + force_numerical_fixed_volume = false; + //switch_numerical = false; + ph_x = 0; + solution_pe_x = 0; + mu_x = 0; + ah2o_x = 1.0; + density_x = 0; + total_h_x = 0; + total_o_x = 0; + cb_x = 0; + total_ions_x = 0; + mass_water_aq_x = 0; + mass_water_surfaces_x = 0; + mass_water_bulk_x = 0; + units_x = NULL; + // auto pe_x + // auto isotopes_x + // auto default_pe_x + dl_type_x = cxxSurface::NO_DL; + total_carbon = 0; + total_co2 = 0; + total_alkalinity = 0; + gfw_water = 0; + step_x = 0; + kin_time_x = 0; + /*---------------------------------------------------------------------- + * Transport data + *---------------------------------------------------------------------- */ + count_cells = 1; + cell_data_max_cells = count_cells; + count_shifts = 1; + ishift = 1; + bcon_first = bcon_last = 3; + correct_disp = FALSE; + tempr = 2.0; + timest = 0.0; + simul_tr = 0; + diffc = 0.3e-9; + heat_diffc = -0.1; + cell = 0; + mcd_substeps = 1.0; + stag_data = NULL; + print_modulus = 1; + punch_modulus = 1; + dump_in = FALSE; + dump_modulus = 0; + transport_warnings = TRUE; + cell_data = NULL; + old_cells = 0; + max_cells = 0; + all_cells = 0; + multi_Dflag = FALSE; + interlayer_Dflag = FALSE; + default_Dw = 0; + correct_Dw = 0; + multi_Dpor = 0; + interlayer_Dpor = 0.1; + multi_Dpor_lim = 0; + interlayer_Dpor_lim = 0; + multi_Dn = 0; + interlayer_tortf = 100.0; + cell_no = 0; + fix_current = 0.0; + /*---------------------------------------------------------------------- + * Advection data + *---------------------------------------------------------------------- */ + count_ad_cells = 1; + count_ad_shifts = 1; + print_ad_modulus = 1; + punch_ad_modulus = 1; + advection_punch = NULL; + advection_kin_time = 0.0; + advection_kin_time_defined = FALSE; + advection_print = NULL; + advection_warnings = TRUE; + /*---------------------------------------------------------------------- + * Tidy data + *---------------------------------------------------------------------- */ + new_model = TRUE; + new_exchange = FALSE; + new_pp_assemblage = FALSE; + new_surface = FALSE; + new_reaction = FALSE; + new_temperature = FALSE; + new_mix = FALSE; + new_solution = FALSE; + new_gas_phase = FALSE; + new_inverse = FALSE; + new_punch = FALSE; + new_ss_assemblage = FALSE; + new_kinetics = FALSE; + new_copy = FALSE; + new_pitzer = FALSE; + /*---------------------------------------------------------------------- + * Elements + *---------------------------------------------------------------------- */ + elements = NULL; + count_elements = 0; + max_elements = MAX_ELEMENTS; + element_h_one = NULL; + /*---------------------------------------------------------------------- + * Element List + *---------------------------------------------------------------------- */ + elt_list = NULL; + count_elts = 0; + max_elts = MAX_ELTS; + /*---------------------------------------------------------------------- + * Species + *---------------------------------------------------------------------- */ + logk = NULL; + count_logk = 0; + max_logk = MAX_S; + moles_per_kilogram_string= NULL; + pe_string = NULL; + s = NULL; + count_s = 0; + max_s = MAX_S; + // auto s_diff_layer; + s_x = NULL; + count_s_x = 0; + max_s_x = 0; + s_h2o = NULL; + s_hplus = NULL; + s_h3oplus = NULL; + s_eminus = NULL; + s_co3 = NULL; + s_h2 = NULL; + s_o2 = NULL; + /*---------------------------------------------------------------------- + * Phases + *---------------------------------------------------------------------- */ + phases = NULL; + count_phases = 0; + max_phases = MAX_PHASES; + /*---------------------------------------------------------------------- + * Master species + *---------------------------------------------------------------------- */ + master = NULL; + dbg_master = NULL; + count_master = 0; + max_master = MAX_MASTER; + /*---------------------------------------------------------------------- + * Unknowns + *---------------------------------------------------------------------- */ + x = NULL; + count_unknowns = 0; + max_unknowns = 0; + ah2o_unknown = NULL; + alkalinity_unknown = NULL; + carbon_unknown = NULL; + charge_balance_unknown = NULL; + exchange_unknown = NULL; + mass_hydrogen_unknown = NULL; + mass_oxygen_unknown = NULL; + mb_unknown = NULL; + mu_unknown = NULL; + pe_unknown = NULL; + ph_unknown = NULL; + pure_phase_unknown = NULL; + solution_phase_boundary_unknown = NULL; + surface_unknown = NULL; + gas_unknown = NULL; + ss_unknown = NULL; + // auto gas_unknowns; + /*---------------------------------------------------------------------- + * Reaction work space + *---------------------------------------------------------------------- */ + // struct trxn; + trxn.token = 0; + for (int i = 0; i < MAX_LOG_K_INDICES; i++) + { + trxn.logk[i] = 0; + } + for (int i = 0; i < 3; i++) + { + trxn.dz[i] = 0; + } + count_trxn = 0; + max_trxn = MAX_TRXN; + + mb_unknowns = NULL; + count_mb_unknowns = 0; + max_mb_unknowns = MAX_TRXN; + /* ---------------------------------------------------------------------- + * Print + * ---------------------------------------------------------------------- */ + pr.all = TRUE; + pr.initial_solutions = TRUE; + pr.initial_exchangers = TRUE; + pr.reactions = TRUE; + pr.gas_phase = TRUE; + pr.ss_assemblage = TRUE; + pr.pp_assemblage = TRUE; + pr.surface = TRUE; + pr.exchange = TRUE; + pr.kinetics = TRUE; + pr.totals = TRUE; + pr.eh = TRUE; + pr.species = TRUE; + pr.saturation_indices = TRUE; + pr.irrev = TRUE; + pr.mix = TRUE; + pr.reaction = TRUE; + pr.use = TRUE; + pr.logfile = FALSE; + pr.punch = TRUE; + pr.status = TRUE; + pr.inverse = TRUE; + pr.dump = TRUE; + pr.user_print = TRUE; + pr.headings = TRUE; + pr.user_graph = TRUE; + pr.echo_input = TRUE; + pr.warnings = 100; + pr.initial_isotopes = TRUE; + pr.isotope_ratios = TRUE; + pr.isotope_alphas = TRUE; + pr.hdf = FALSE; + pr.alkalinity = FALSE; + status_on = true; +#ifdef NPP + status_interval = 40; +#else + status_interval = 250; +#endif + status_timer = clock(); + count_warnings = 0; + /* ---------------------------------------------------------------------- + * RATES + * ---------------------------------------------------------------------- */ + rates = NULL; + count_rates = 0; + rate_m = 0; + rate_m0 = 0; + rate_time = 0; + rate_kin_time = 1.0; + rate_sim_time_start = 0; + rate_sim_time_end = 0; + rate_sim_time = 0; + rate_moles = 0; + initial_total_time = 0; + // auto rate_p + count_rate_p = 0; + /* ---------------------------------------------------------------------- + * USER PRINT COMMANDS + * ---------------------------------------------------------------------- */ + user_print = NULL; +#ifdef SKIP + user_punch = NULL; + user_punch_headings = NULL; + user_punch_count_headings = 0; +#endif + n_user_punch_index = 0; + fpunchf_user_s_warning = 0; + fpunchf_user_buffer[0] = 0; + +#if defined PHREEQ98 + struct rate *user_graph; + char **user_graph_headings; + int user_graph_count_headings; +#endif +#if defined MULTICHART + // auto chart_handler; + chart_handler.Set_io(phrq_io); +#endif + /* ---------------------------------------------------------------------- + * GLOBAL DECLARATIONS + * ---------------------------------------------------------------------- */ + error_string = NULL; + simulation = 0; + state = INITIALIZE; + reaction_step = 0; + transport_step = 0; + transport_start = 0; + advection_step = 0; + stop_program = FALSE; + incremental_reactions = FALSE; + count_strings = 0; + array = NULL; + delta = NULL; + residual = NULL; + input_error = 0; + next_keyword = Keywords::KEY_NONE; + parse_error = 0; + paren_count = 0; + iterations = 0; + gamma_iterations = 0; + run_reactions_iterations= 0; + max_line = MAX_LINE; + line = NULL; + line_save = NULL; + LOG_10 = log(10.0); + debug_model = FALSE; + debug_prep = FALSE; + debug_set = FALSE; + debug_diffuse_layer = FALSE; + debug_inverse = FALSE; +#ifdef USE_LONG_DOUBLE + /* from float.h, sets tolerance for cl1 routine */ + inv_tol_default = pow((long double) 10, (long double) -LDBL_DIG + 5); +#else + inv_tol_default = pow((double) 10, (double) -DBL_DIG + 5); +#endif + itmax = 100; + max_tries = 1000; +#ifdef USE_LONG_DOUBLE + /* from float.h, sets tolerance for cl1 routine */ + ineq_tol = pow((long double) 10, (long double) -LDBL_DIG); +#elif NPP +// appt: + ineq_tol = pow((double) 10, (double) -DBL_DIG + 2); +#else + ineq_tol = pow((double) 10, (double) -DBL_DIG); +#endif + convergence_tolerance = 1e-8; + step_size = 100.; + pe_step_size = 10.; + step_size_now = step_size; + pe_step_size_now = pe_step_size; + pp_scale = 1.0; + pp_column_scale = 1.0; + diagonal_scale = FALSE; + mass_water_switch = FALSE; + delay_mass_water = FALSE; + equi_delay = 0; + dampen_ah2o = false; + censor = 0.0; + aqueous_only = 0; + negative_concentrations = FALSE; + calculating_deriv = FALSE; + numerical_deriv = FALSE; + count_total_steps = 0; + phast = FALSE; + llnl_temp = 0; + llnl_count_temp = 0; + llnl_adh = 0; + llnl_count_adh = 0; + llnl_bdh = 0; + llnl_count_bdh = 0; + llnl_bdot = 0; + llnl_count_bdot = 0; + llnl_co2_coefs = 0; + llnl_count_co2_coefs = 0; + //selected_output_file_name = NULL; + dump_file_name = NULL; + remove_unstable_phases = FALSE; + // auto screen_string; + spread_length = 10; + /* ---------------------------------------------------------------------- */ + /* + * Hash definitions + */ + // auto strings_map; +#ifdef HASH + // auto strings_hash; +#endif + elements_hash_table = NULL; + species_hash_table = NULL; + phases_hash_table = NULL; + logk_hash_table = NULL; + master_isotope_hash_table = NULL; + /* ---------------------------------------------------------------------- + * ISOTOPES + * ---------------------------------------------------------------------- */ + count_master_isotope = 0; + master_isotope = NULL; + max_master_isotope = MAX_ELTS; + initial_solution_isotopes = FALSE; + count_calculate_value = 0; + calculate_value = NULL; + max_calculate_value = MAX_ELTS; + calculate_value_hash_table = NULL; + count_isotope_ratio = 0; + isotope_ratio = 0; + max_isotope_ratio = MAX_ELTS; + isotope_ratio_hash_table = 0; + count_isotope_alpha = 0; + isotope_alpha = 0; + max_isotope_alpha = MAX_ELTS; + isotope_alpha_hash_table = 0; + + + phreeqc_mpi_myself = 0; + first_read_input = TRUE; + user_database = NULL; + //have_punch_name = FALSE; + print_density = 0; + print_viscosity = 0; + zeros = NULL; + zeros_max = 1; + cell_pore_volume = 0; + cell_volume = 0; + cell_porosity = 0; + cell_saturation = 0; + sys = NULL; + count_sys = 0; + max_sys = 0; + sys_tot = 0; + + V_solutes = 0.0; + viscos = 0.0; + viscos_0 = 0.0; + rho_0 = 0; + kappa_0 = 0.0; + p_sat = 0.0; + eps_r = EPSILON; + DH_A = 0.0; + DH_B = 0.0; + DH_Av = 0.0; + QBrn = 0.0; + ZBrn = 0.0; + dgdP = 0.0; + + need_temp_msg = 0; + solution_mass = 0; + solution_volume = 0; + /* phqalloc.cpp ------------------------------- */ + s_pTail = NULL; + /* Basic */ + basic_interpreter = NULL; + basic_callback_ptr = NULL; + basic_callback_cookie = NULL; + basic_fortran_callback_ptr = NULL; + + /* cl1.cpp ------------------------------- */ + x_arg = NULL; + res_arg = NULL; + scratch = NULL; + x_arg_max = 0; + res_arg_max = 0; + scratch_max = 0; +#ifdef SKIP + /* dw.cpp ------------------------------- */ + /* COMMON /QQQQ/ */ + Q0 = 0; + Q5 = 0; + GASCON = 0.461522e0; + TZ = 647.073e0; + AA = 1.e0; + Z = 0; + DZ = 0; + Y = 0; + G1 = 11.e0; + G2 = 44.333333333333e0; + GF = 3.5e0; + B1 = 0; + B2 = 0; + B1T = 0; + B2T = 0; + B1TT = 0; + B2TT = 0; +#endif + /* gases.cpp ------------------------------- */ + a_aa_sum = 0; + b2 = 0; + b_sum = 0; + R_TK = 0; + /* input.cpp ------------------------------- */ + check_line_return = 0; + reading_db = FALSE; + /* integrate.cpp ------------------------------- */ + midpoint_sv = 0; + z_global = 0; + xd_global = 0; + alpha_global = 0; + /* integrate.cpp ------------------------------- */ + max_row_count = 50; + max_column_count = 50; + carbon = FALSE; + col_name = NULL; + row_name = NULL; + count_rows = 0; + count_optimize = 0; + col_phases = 0; + col_redox = 0; + col_epsilon = 0; + col_ph = 0; + col_water = 0; + col_isotopes = 0; + col_phase_isotopes = 0; + row_mb = 0; + row_fract = 0; + row_charge = 0; + row_carbon = 0; + row_isotopes = 0; + row_epsilon = 0; + row_isotope_epsilon = 0; + row_water = 0; + inv_zero = NULL; + array1 = 0; + inv_res = NULL; + inv_delta1 = NULL; + delta2 = NULL; + delta3 = NULL; + inv_cu = NULL; + delta_save = NULL; + min_delta = NULL; + max_delta = NULL; + inv_iu = NULL; + inv_is = NULL; + klmd = 0; + nklmd = 0; + n2d = 0; + kode = 0; + iter = 0; + toler = 0; + error = 0; + max_pct = 0; + scaled_error = 0; + master_alk = NULL; + row_back = NULL; + col_back = NULL; + good = NULL; + bad = NULL; + minimal = NULL; + max_good = 0; + max_bad = 0; + max_minimal = 0; + count_good = 0; + count_bad = 0; + count_minimal = 0; + count_calls = 0; + soln_bits = 0; + phase_bits = 0; + current_bits = 0; + temp_bits = 0; + netpath_file = NULL; + count_inverse_models = 0; + count_pat_solutions = 0; + for (int i = 0; i < 32; i++) + { + min_position[i] = 0; + max_position[i] = 0; + now[i] = 0; + } + /* kinetics.cpp ------------------------------- */ + count_pp = count_pg = count_ss = 0; + cvode_kinetics_ptr = NULL; + cvode_test = FALSE; + cvode_error = FALSE; + cvode_n_user = -99; + cvode_n_reactions = -99; + cvode_step_fraction = 0.0; + cvode_rate_sim_time = 0.0; + cvode_rate_sim_time_start = 0.0; + cvode_last_good_time = 0.0; + cvode_prev_good_time = 0.0; + cvode_last_good_y = NULL; + cvode_prev_good_y = NULL; + kinetics_machEnv = NULL; + kinetics_y = NULL; + kinetics_abstol = NULL; + kinetics_cvode_mem = NULL; + cvode_pp_assemblage_save= NULL; + cvode_ss_assemblage_save= NULL; + m_original = NULL; + m_temp = NULL; + rk_moles = NULL; + set_and_run_attempt = 0; + x0_moles = NULL; + /* model.cpp ------------------------------- */ + gas_in = FALSE; + min_value = 1e-10; + normal = NULL; + ineq_array = NULL; + res = NULL; + cu = NULL; + zero = NULL; + delta1 = NULL; + iu = NULL; + is = NULL; + back_eq = NULL; + normal_max = 0; + ineq_array_max = 0; + res_max = 0; + cu_max = 0; + zero_max = 0; + delta1_max = 0; + iu_max = 0; + is_max = 0; + back_eq_max = 0; + /* phrq_io_output.cpp ------------------------------- */ + forward_output_to_log = 0; + /* phreeqc_files.cpp ------------------------------- */ + default_data_base = string_duplicate("phreeqc.dat"); +#ifdef PHREEQ98 + int outputlinenr; + char *LogFileNameC; + char progress_str[512]; +#endif + /* Pitzer */ + pitzer_model = FALSE; + sit_model = FALSE; + pitzer_pe = FALSE; + full_pitzer = FALSE; + always_full_pitzer = FALSE; + ICON = TRUE; + IC = -1; + COSMOT = 0; + AW = 0; + VP = 0; + DW0 = 0; + pitz_params = NULL; + count_pitz_param = 0; + max_pitz_param = 100; + // auto pitz_param_map + theta_params = 0; + count_theta_param = 0; + max_theta_param = 100; + use_etheta = TRUE; + OTEMP = -100.; + OPRESS = -100.; + A0 = 0; + aphi = NULL; + spec = NULL; + cations = NULL; + anions = NULL; + neutrals = NULL; + count_cations = 0; + count_anions = 0; + count_neutrals = 0; + MAXCATIONS = 0; + FIRSTANION = 0; + MAXNEUTRAL = 0; + mcb0 = NULL; + mcb1 = NULL; + mcc0 = NULL; + IPRSNT = NULL; + M = NULL; + LGAMMA = NULL; + for (int i = 0; i < 23; i++) + { + BK[i] = 0.0; + DK[i] = 0.0; + } +#ifdef PHREEQ98 + int connect_simulations, graph_initial_solutions; + int shifts_as_points; + int chart_type; + int ShowChart; + int RowOffset, ColumnOffset; +#endif + dummy = 0; + /* print.cpp ------------------------------- */ + sformatf_buffer = (char *) PHRQ_malloc(256 * sizeof(char)); + if (sformatf_buffer == NULL) + malloc_error(); + sformatf_buffer_size = 256; +#ifdef PHREEQ98 + int colnr, rownr; + int graph_initial_solutions; + int prev_advection_step, prev_transport_step; /*, prev_reaction_step */ + /* int shifts_as_points; */ + int chart_type; + int AddSeries; + int FirstCallToUSER_GRAPH; +#endif + /* read.cpp */ + prev_next_char = NULL; +#if defined PHREEQ98 + int shifts_as_points; +#endif + /* read_class.cxx */ + // auto dump_info + // auto delete_info + // auto run_info + run_info.Set_io(phrq_io); + /* readtr.cpp */ + // auto dump_file_name_cpp; + /* sit.cpp ------------------------------- */ + sit_params = NULL; + count_sit_param = 0; + max_sit_param = 100; + // auto sit_param_map + sit_A0 = 0; + sit_count_cations = 0; + sit_count_anions = 0; + sit_count_neutrals = 0; + sit_MAXCATIONS = 0; + sit_FIRSTANION = 0; + sit_MAXNEUTRAL = 0; + sit_IPRSNT = NULL; + sit_M = NULL; + sit_LGAMMA = NULL; + /* tidy.cpp ------------------------------- */ + a0 = 0; + a1 = 0; + kc = 0; + kb = 0; + /* tally.cpp ------------------------------- */ + t_buffer = NULL; + tally_count_component = 0; + tally_table = NULL; + count_tally_table_columns = 0; + count_tally_table_rows = 0; + /* transport.cpp ------------------------------- */ + sol_D = NULL; + sol_D_dbg = NULL; + J_ij = NULL; + J_ij_il = NULL; + J_ij_count_spec = 0; + m_s = NULL; + count_m_s = 0; + tot1_h = 0; + tot1_o = 0; + tot2_h = 0; + tot2_o = 0; + diffc_max = 0; + diffc_tr = 0; + J_ij_sum = 0; + transp_surf = FALSE; + heat_mix_array = NULL; + temp1 = NULL; + temp2 = NULL; + nmix = 0; + heat_nmix = 0; + heat_mix_f_imm = 0; + heat_mix_f_m = 0; + warn_MCD_X = 0; + warn_fixed_Surf = 0; +#ifdef PHREEQ98 + int AutoLoadOutputFile, CreateToC; + int ProcessMessages, ShowProgress, ShowProgressWindow, ShowChart; + int outputlinenr; + int stop_calculations; + char err_str98[80]; +#endif + /* utilities.cpp ------------------------------- */ + spinner = 0; + // keycount; + for (int i = 0; i < Keywords::KEY_COUNT_KEYWORDS; i++) + { + keycount.push_back(0); + } + + return; +} +/*-----------------------------------------------------*/ +Phreeqc::Phreeqc(const Phreeqc &src) +{ + this->phrq_io = src.phrq_io; + this->init(); + this->initialize(); + InternalCopy(&src); +} +void +Phreeqc::InternalCopy(const Phreeqc *pSrc) +{ + // phrq_io + /* + if (io) + { + this->phrq_io = io; + } + else + { + this->phrq_io = &this->ioInstance; + } + */ + + same_model = FALSE; + current_tc = pSrc->current_tc; + current_pa = pSrc->current_pa; + current_mu = pSrc->current_mu; + mu_terms_in_logk = pSrc->mu_terms_in_logk; + + MIN_LM = pSrc->MIN_LM; /* minimum log molality allowed before molality set to zero */ + LOG_ZERO_MOLALITY = pSrc->LOG_ZERO_MOLALITY; /* molalities <= LOG_ZERO_MOLALITY are considered equal to zero */ + MIN_RELATED_LOG_ACTIVITY = pSrc->MIN_RELATED_LOG_ACTIVITY; + MIN_TOTAL = pSrc->MIN_TOTAL; + MIN_TOTAL_SS = pSrc->MIN_TOTAL_SS; + MIN_RELATED_SURFACE = pSrc->MIN_RELATED_SURFACE; + /* ---------------------------------------------------------------------- + * STRUCTURES + * ---------------------------------------------------------------------- */ +/* + * last model + */ + //-- skip last model, accept init + +/* + * Initialize punch + */ + //-- skip punch, accept init + high_precision = pSrc->high_precision; + + Rxn_temperature_map = pSrc->Rxn_temperature_map; + Rxn_pressure_map = pSrc->Rxn_pressure_map; + + /* ---------------------------------------------------------------------- + * Surface + * --------------------------------------------------------------------- */ + g_iterations = -1; + G_TOL = 1e-8; + Rxn_surface_map = pSrc->Rxn_surface_map; + // auto charge_group_map; + /* + change_surf_count = 0; + change_surf = NULL; + */ + /* ---------------------------------------------------------------------- + * Exchange + * ---------------------------------------------------------------------- */ + Rxn_exchange_map = pSrc->Rxn_exchange_map; + + /* ---------------------------------------------------------------------- + * Kinetics + * ---------------------------------------------------------------------- */ + Rxn_kinetics_map = pSrc->Rxn_kinetics_map; + + /*---------------------------------------------------------------------- + * Save + *---------------------------------------------------------------------- */ + count_save_values = 0; + /* + save_values = NULL; + save_init(-1); // set initial save values + */ + + // auto use + + // copier structures + //-- skip copier, accept init + + /*---------------------------------------------------------------------- + * Inverse + *---------------------------------------------------------------------- */ + + /* + inverse = NULL; + */ + count_inverse = 0; + /*---------------------------------------------------------------------- + * Mix + *---------------------------------------------------------------------- */ + // Should be empty after each END + // auto Rxn_mix_map; + // auto Dispersion_mix_map; + // auto Rxn_solution_mix_map; + // auto Rxn_exchange_mix_map; + // auto Rxn_gas_phase_mix_map; + // auto Rxn_kinetics_mix_map; + // auto Rxn_pp_assemblage_mix_map; + // auto Rxn_ss_assemblage_mix_map; + // auto Rxn_surface_mix_map; + /*---------------------------------------------------------------------- + * Irreversible reaction + *---------------------------------------------------------------------- */ + Rxn_reaction_map = pSrc->Rxn_reaction_map; + run_cells_one_step = pSrc->run_cells_one_step; + /*---------------------------------------------------------------------- + * Gas phase + *---------------------------------------------------------------------- */ + Rxn_gas_phase_map = pSrc->Rxn_gas_phase_map; + /*---------------------------------------------------------------------- + * Solid solution + *---------------------------------------------------------------------- */ + Rxn_ss_assemblage_map = pSrc->Rxn_ss_assemblage_map; + /*---------------------------------------------------------------------- + * Pure-phase assemblage + *---------------------------------------------------------------------- */ + Rxn_pp_assemblage_map = pSrc->Rxn_pp_assemblage_map; + /*---------------------------------------------------------------------- + * Species_list + *---------------------------------------------------------------------- */ + /* + count_species_list = 0; + max_species_list = 0; + species_list = NULL; + */ + /*---------------------------------------------------------------------- + * Jacobian and Mass balance lists + *---------------------------------------------------------------------- */ + /* + count_sum_jacob0 = 0; + max_sum_jacob0 = 0; + sum_jacob0 = NULL; + count_sum_mb1 = 0; + max_sum_mb1 = 0; + sum_mb1 = NULL; + count_sum_jacob1 = 0; + max_sum_jacob1 = 0; + sum_jacob1 = NULL; + count_sum_mb2 = 0; + max_sum_mb2 = 0; + sum_mb2 = NULL; + count_sum_jacob2 = 0; + max_sum_jacob2 = 0; + sum_jacob2 = NULL; + count_sum_delta = 0; + max_sum_delta = 0; + sum_delta = NULL; + */ + /*---------------------------------------------------------------------- + * Solution + *---------------------------------------------------------------------- */ + Rxn_solution_map = pSrc->Rxn_solution_map; + save_species = pSrc->save_species; + // auto Rxn_solution_map; + // auto unnumbered_solutions; + /*---------------------------------------------------------------------- + * Global solution + *---------------------------------------------------------------------- */ + /* + title_x = NULL; + new_x = FALSE; + description_x = NULL; + tc_x = 0; + tk_x = 0; + patm_x = 1; + last_patm_x = 1; + numerical_fixed_volume = false; + force_numerical_fixed_volume = false; + //switch_numerical = false; + ph_x = 0; + solution_pe_x = 0; + mu_x = 0; + ah2o_x = 1.0; + density_x = 0; + total_h_x = 0; + total_o_x = 0; + cb_x = 0; + total_ions_x = 0; + mass_water_aq_x = 0; + mass_water_surfaces_x = 0; + mass_water_bulk_x = 0; + units_x = NULL; + */ + // auto pe_x + // auto isotopes_x + // auto default_pe_x + /* + dl_type_x = cxxSurface::NO_DL; + total_carbon = 0; + total_co2 = 0; + total_alkalinity = 0; + gfw_water = 0; + step_x = 0; + kin_time_x = 0; + */ + /*---------------------------------------------------------------------- + * Transport data + *---------------------------------------------------------------------- */ + count_cells = pSrc->count_cells; + cell_data_max_cells = pSrc->cell_data_max_cells; + count_shifts = pSrc->count_shifts; + ishift = pSrc->ishift; + bcon_first = pSrc->bcon_first; + bcon_last = pSrc->bcon_last; + correct_disp = pSrc->correct_disp; + tempr = pSrc->tempr; + timest = pSrc->timest; + simul_tr = pSrc->simul_tr; + diffc = pSrc->diffc; + heat_diffc = pSrc->heat_diffc; + cell = pSrc->cell; + mcd_substeps = pSrc->mcd_substeps; + /* stag_data */ + memcpy(stag_data, pSrc->stag_data, sizeof(struct stag_data)); + print_modulus = pSrc->print_modulus; + punch_modulus = pSrc->punch_modulus; + dump_in = pSrc->dump_in; + dump_modulus = pSrc->dump_modulus; + transport_warnings = pSrc->transport_warnings; + /* cell_data */ + if (count_cells > 0) + { + cell_data = (struct cell_data *) free_check_null(cell_data); + cell_data = (struct cell_data *) PHRQ_malloc((size_t) ((count_cells + 2) * sizeof(struct cell_data))); + if (cell_data == NULL) malloc_error(); + memcpy(cell_data, pSrc->cell_data, ((size_t) ((count_cells + 2) * sizeof(struct cell_data)))); + } + old_cells = pSrc->old_cells; + max_cells = pSrc->max_cells; + all_cells = pSrc->all_cells; + multi_Dflag = pSrc->multi_Dflag; + interlayer_Dflag = pSrc->interlayer_Dflag; + default_Dw = pSrc->default_Dw; + multi_Dpor = pSrc->multi_Dpor; + interlayer_Dpor = pSrc->interlayer_Dpor; + multi_Dpor_lim = pSrc->multi_Dpor_lim; + interlayer_Dpor_lim = pSrc->interlayer_Dpor_lim; + multi_Dn = pSrc->multi_Dn; + interlayer_tortf = pSrc->interlayer_tortf; + cell_no = pSrc->cell_no; + fix_current = pSrc->fix_current; + /*---------------------------------------------------------------------- + * Advection data + *---------------------------------------------------------------------- */ + count_ad_cells = pSrc->count_ad_cells; + count_ad_shifts = pSrc->count_ad_shifts; + print_ad_modulus = pSrc->print_ad_modulus; + punch_ad_modulus = pSrc->punch_ad_modulus; + /* advection_punch */ + if (count_ad_cells > 0) + { + advection_punch = (int *) free_check_null(advection_punch); + advection_punch = (int *) PHRQ_malloc((size_t) (count_ad_cells * sizeof(int))); + if (advection_punch == NULL) malloc_error(); + memcpy(advection_punch, pSrc->advection_punch, (size_t) (count_ad_cells * sizeof(int))); + } + /* advection_print */ + if (count_ad_cells > 0) + { + advection_print = (int *) free_check_null(advection_print); + advection_print = (int *) PHRQ_malloc((size_t) (count_ad_cells * sizeof(int))); + if (advection_print == NULL) malloc_error(); + memcpy(advection_print, pSrc->advection_print, (size_t) (count_ad_cells * sizeof(int))); + } + advection_kin_time = pSrc->advection_kin_time; + advection_kin_time_defined = pSrc->advection_kin_time_defined; + advection_warnings = pSrc->advection_warnings; + /*---------------------------------------------------------------------- + * Tidy data + *---------------------------------------------------------------------- */ + /* + new_model = TRUE; + new_exchange = FALSE; + new_pp_assemblage = FALSE; + new_surface = FALSE; + new_reaction = FALSE; + new_temperature = FALSE; + new_mix = FALSE; + new_solution = FALSE; + new_gas_phase = FALSE; + new_inverse = FALSE; + new_punch = FALSE; + new_ss_assemblage = FALSE; + new_kinetics = FALSE; + new_copy = FALSE; + new_pitzer = FALSE; + */ + /*---------------------------------------------------------------------- + * Elements + *---------------------------------------------------------------------- */ + for (int i = 0; i < pSrc->count_elements; i++) + { + string_hsave(pSrc->elements[i]->name); + struct element *elt_ptr = element_store(pSrc->elements[i]->name); + elt_ptr->gfw = pSrc->elements[i]->gfw; + } + element_h_one = element_store("H(1)"); + /* + elements = NULL; + count_elements = 0; + max_elements = MAX_ELEMENTS; + element_h_one = NULL; + */ + /*---------------------------------------------------------------------- + * Element List + *---------------------------------------------------------------------- */ + /* + elt_list = NULL; + count_elts = 0; + max_elts = MAX_ELTS; + */ + /*---------------------------------------------------------------------- + * Species + *---------------------------------------------------------------------- */ + /* + logk = NULL; + count_logk = 0; + max_logk = MAX_S; + moles_per_kilogram_string= NULL; + pe_string = NULL; + s = NULL; + count_s = 0; + max_s = MAX_S; + // auto s_diff_layer; + s_x = NULL; + count_s_x = 0; + max_s_x = 0; + s_h2o = NULL; + s_hplus = NULL; + s_h3oplus = NULL; + s_eminus = NULL; + s_co3 = NULL; + s_h2 = NULL; + s_o2 = NULL; + */ + // logk + for (int i = 0; i < pSrc->count_logk; i++) + { + char * name = string_duplicate(pSrc->logk[i]->name); + struct logk *logk_ptr = logk_store(name, FALSE); + free_check_null(name); + memcpy(logk_ptr, pSrc->logk[i], sizeof(struct logk)); + logk_ptr->name = string_hsave(pSrc->logk[i]->name); + logk_ptr->add_logk = NULL; + if (logk_ptr->count_add_logk > 0) + { + logk_ptr->add_logk = (struct name_coef *) PHRQ_malloc((size_t) pSrc->logk[i]->count_add_logk * sizeof(struct name_coef)); + if (logk[i]->add_logk == NULL) malloc_error(); + for (int j = 0; j < logk_ptr->count_add_logk; j++) + { + logk_ptr->add_logk[j].coef = pSrc->logk[i]->add_logk[j].coef; + logk_ptr->add_logk[j].name = string_hsave( pSrc->logk[i]->add_logk[j].name); + } + } + } + // s, species + for (int i = 0; i < pSrc->count_s; i++) + { + struct species *s_ptr = s_store(pSrc->s[i]->name, pSrc->s[i]->z, FALSE); + memcpy(s_ptr, pSrc->s[i], sizeof(struct species)); + s_ptr->name = string_hsave(pSrc->s[i]->name); + // fix up all pointers + s_ptr->mole_balance = NULL; + if (pSrc->s[i]->mole_balance != NULL) + { + s_ptr->mole_balance = string_hsave(pSrc->s[i]->mole_balance); + } + s_ptr->primary = NULL; + s_ptr->secondary = NULL; + //add_logk + s_ptr->add_logk = NULL; + if (s_ptr->count_add_logk > 0) + { + s_ptr->add_logk = (struct name_coef *) PHRQ_malloc((size_t) s_ptr->count_add_logk * sizeof(struct name_coef)); + if (s_ptr->add_logk == NULL) malloc_error(); + for (int j = 0; j < s_ptr->count_add_logk; j++) + { + s_ptr->add_logk[j].coef = pSrc->s[i]->add_logk[j].coef; + s_ptr->add_logk[j].name = string_hsave( pSrc->s[i]->add_logk[j].name); + } + } + //next_elt + s_ptr->next_elt = NULL; + if (pSrc->s[i]->next_elt) + { + cxxNameDouble next_elt(pSrc->s[i]->next_elt); + s_ptr->next_elt = NameDouble2elt_list(next_elt); + } + //next_secondary + s_ptr->next_secondary = NULL; + if (pSrc->s[i]->next_secondary) + { + cxxNameDouble next_secondary(pSrc->s[i]->next_secondary); + s_ptr->next_secondary = NameDouble2elt_list(next_secondary); + } + //next_sys_total + s_ptr->next_sys_total = NULL; + if (pSrc->s[i]->next_sys_total) + { + cxxNameDouble next_sys_total(pSrc->s[i]->next_sys_total); + s_ptr->next_sys_total = NameDouble2elt_list(next_sys_total); + } + //rxn + s_ptr->rxn = NULL; + if (pSrc->s[i]->rxn != NULL) + { + cxxChemRxn rxn(pSrc->s[i]->rxn); + s_ptr->rxn = cxxChemRxn2rxn(rxn); + //s_ptr->rxn = rxn_copy_operator(pSrc->s[i]->rxn); + } + //rxn_s + s_ptr->rxn_s = NULL; + if (pSrc->s[i]->rxn_s != NULL) + { + cxxChemRxn rxn_s(pSrc->s[i]->rxn_s); + s_ptr->rxn_s = cxxChemRxn2rxn(rxn_s); + } + //rxn_x + s_ptr->rxn_x = NULL; + if (pSrc->s[i]->rxn_x != NULL) + { + cxxChemRxn rxn_x(pSrc->s[i]->rxn_x); + s_ptr->rxn_x = cxxChemRxn2rxn(rxn_x); + } + } + s_h2o = s_search("H2O"); + s_hplus = s_search("H+"); + s_h3oplus = s_search("H3O+"); + s_eminus = s_search("e-"); + s_co3 = s_search("CO3-2"); + s_h2 = s_search("H2"); + s_o2 = s_search("O2"); + /*---------------------------------------------------------------------- + * Phases + *---------------------------------------------------------------------- */ + /* + phases = NULL; + count_phases = 0; + max_phases = MAX_PHASES; + */ + for (int i = 0; i < pSrc->count_phases; i++) + { + struct phase *phase_ptr = phase_store(pSrc->phases[i]->name); + memcpy(phase_ptr, pSrc->phases[i], sizeof(struct phase)); + // clean up pointers + phase_ptr->name = string_hsave(pSrc->phases[i]->name); + phase_ptr->formula = string_hsave(pSrc->phases[i]->formula); + //add_logk + phase_ptr->add_logk = NULL; + if (phase_ptr->count_add_logk > 0) + { + phase_ptr->add_logk = (struct name_coef *) PHRQ_malloc((size_t) pSrc->phases[i]->count_add_logk * sizeof(struct name_coef)); + if (phase_ptr->add_logk == NULL) malloc_error(); + for (int j = 0; j < phase_ptr->count_add_logk; j++) + { + phase_ptr->add_logk[j].coef = pSrc->phases[i]->add_logk[j].coef; + phase_ptr->add_logk[j].name = string_hsave( pSrc->phases[i]->add_logk[j].name); + } + } + //next_elt + phase_ptr->next_elt = NULL; + if (pSrc->phases[i]->next_elt) + { + cxxNameDouble next_elt(pSrc->phases[i]->next_elt); + phase_ptr->next_elt = NameDouble2elt_list(next_elt); + } + //next_sys_total + phase_ptr->next_sys_total = NULL; + if (pSrc->phases[i]->next_sys_total) + { + cxxNameDouble next_sys_total(pSrc->phases[i]->next_sys_total); + phase_ptr->next_sys_total = NameDouble2elt_list(next_sys_total); + } + //rxn + phase_ptr->rxn = NULL; + if (pSrc->phases[i]->rxn != NULL) + { + cxxChemRxn rxn(pSrc->phases[i]->rxn); + phase_ptr->rxn = cxxChemRxn2rxn(rxn); + } + //rxn_s + phase_ptr->rxn_s = NULL; + if (pSrc->phases[i]->rxn_s != NULL) + { + cxxChemRxn rxn_s(pSrc->phases[i]->rxn_s); + phase_ptr->rxn_s = cxxChemRxn2rxn(rxn_s); + } + //rxn_x + phase_ptr->rxn_x = NULL; + if (pSrc->phases[i]->rxn_x != NULL) + { + cxxChemRxn rxn_x(pSrc->phases[i]->rxn_x); + phase_ptr->rxn_x = cxxChemRxn2rxn(rxn_x); + } + } + /*---------------------------------------------------------------------- + * Master species + *---------------------------------------------------------------------- */ + /* + master = NULL; + dbg_master = NULL; + count_master = 0; + max_master = MAX_MASTER; + */ + count_master = pSrc->count_master; + max_master = pSrc->max_master; + master = (struct master **) free_check_null(master); + master = (struct master **) PHRQ_malloc((size_t) max_master * sizeof(struct master *)); + if (master == NULL) malloc_error(); + dbg_master = master; + for (int i = 0; i < count_master; i++) + { + master[i] = (struct master *) PHRQ_malloc( sizeof(struct master)); + if (master[i] == NULL) malloc_error(); + memcpy(master[i], pSrc->master[i], sizeof(struct master)); + // clean up pointers + master[i]->gfw_formula = NULL; + if (pSrc->master[i]->gfw_formula != NULL) + { + master[i]->gfw_formula = string_hsave(pSrc->master[i]->gfw_formula); + } + master[i]->elt = element_store(pSrc->master[i]->elt->name); + master[i]->unknown = NULL; + master[i]->s = s_store(pSrc->master[i]->s->name, pSrc->master[i]->s->z, false); + //rxn_primary + master[i]->rxn_primary = NULL; + if (pSrc->master[i]->rxn_primary != NULL) + { + cxxChemRxn rxn_primary(pSrc->master[i]->rxn_primary); + master[i]->rxn_primary = cxxChemRxn2rxn(rxn_primary); + } + //rxn_secondary + master[i]->rxn_secondary = NULL; + if (pSrc->master[i]->rxn_secondary != NULL) + { + cxxChemRxn rxn_secondary(pSrc->master[i]->rxn_secondary); + master[i]->rxn_secondary = cxxChemRxn2rxn(rxn_secondary); + } + } + /*---------------------------------------------------------------------- + * Unknowns + *---------------------------------------------------------------------- */ + /* + x = NULL; + count_unknowns = 0; + max_unknowns = 0; + ah2o_unknown = NULL; + alkalinity_unknown = NULL; + carbon_unknown = NULL; + charge_balance_unknown = NULL; + exchange_unknown = NULL; + mass_hydrogen_unknown = NULL; + mass_oxygen_unknown = NULL; + mb_unknown = NULL; + mu_unknown = NULL; + pe_unknown = NULL; + ph_unknown = NULL; + pure_phase_unknown = NULL; + solution_phase_boundary_unknown = NULL; + surface_unknown = NULL; + gas_unknown = NULL; + ss_unknown = NULL; + */ + // auto gas_unknowns; + /*---------------------------------------------------------------------- + * Reaction work space + *---------------------------------------------------------------------- */ + // struct trxn; + /* + trxn.token = 0; + for (int i = 0; i < MAX_LOG_K_INDICES; i++) + { + trxn.logk[i] = 0; + } + for (int i = 0; i < 3; i++) + { + trxn.dz[i] = 0; + } + count_trxn = 0; + max_trxn = MAX_TRXN; + */ + /* + mb_unknowns = NULL; + count_mb_unknowns = 0; + max_mb_unknowns = MAX_TRXN; + */ + /* ---------------------------------------------------------------------- + * Print + * ---------------------------------------------------------------------- */ + /* + pr.all = TRUE; + pr.initial_solutions = TRUE; + pr.initial_exchangers = TRUE; + pr.reactions = TRUE; + pr.gas_phase = TRUE; + pr.ss_assemblage = TRUE; + pr.pp_assemblage = TRUE; + pr.surface = TRUE; + pr.exchange = TRUE; + pr.kinetics = TRUE; + pr.totals = TRUE; + pr.eh = TRUE; + pr.species = TRUE; + pr.saturation_indices = TRUE; + pr.irrev = TRUE; + pr.mix = TRUE; + pr.reaction = TRUE; + pr.use = TRUE; + pr.logfile = FALSE; + pr.punch = TRUE; + pr.status = TRUE; + pr.inverse = TRUE; + pr.dump = TRUE; + pr.user_print = TRUE; + pr.headings = TRUE; + pr.user_graph = TRUE; + pr.echo_input = TRUE; + pr.warnings = 100; + pr.initial_isotopes = TRUE; + pr.isotope_ratios = TRUE; + pr.isotope_alphas = TRUE; + pr.hdf = FALSE; + pr.alkalinity = FALSE; + */ + pr = pSrc->pr; + status_on = pSrc->status_on; + status_interval = pSrc->status_interval; + status_timer = clock(); + count_warnings = 0; + /* ---------------------------------------------------------------------- + * RATES + * ---------------------------------------------------------------------- */ + /* + rates = NULL; + count_rates = 0; + rate_m = 0; + rate_m0 = 0; + rate_time = 0; + rate_kin_time = 1.0; + rate_sim_time_start = 0; + rate_sim_time_end = 0; + rate_sim_time = 0; + rate_moles = 0; + initial_total_time = 0; + // auto rate_p + count_rate_p = 0; + */ + rates = (struct rate *) free_check_null(rates); + count_rates = pSrc->count_rates; + if (count_rates > 0) + { + rates = (struct rate *) PHRQ_malloc((size_t) count_rates * sizeof(struct rate)); + if (rates == NULL) malloc_error(); + for (int i = 0; i < count_rates; i++) + { + rates[i].name = string_hsave(pSrc->rates[i].name); + rates[i].commands = string_duplicate(pSrc->rates[i].commands); + rates[i].new_def = TRUE; + rates[i].linebase = NULL; + rates[i].varbase = NULL; + rates[i].loopbase = NULL; + } + } + /* ---------------------------------------------------------------------- + * USER PRINT COMMANDS + * ---------------------------------------------------------------------- */ + /* + user_print = NULL; + */ + { + user_print->name = NULL; + user_print->commands = NULL; + if (pSrc->user_print->commands != NULL) + { + user_print->commands = string_duplicate(pSrc->user_print->commands); + } + user_print->new_def = TRUE; + user_print->linebase = NULL; + user_print->varbase = NULL; + user_print->loopbase = NULL; + } + + // For now, User Punch is not copied +#ifdef SKIP + /* + user_punch = NULL; + */ + { + user_punch->name = NULL; + user_punch->commands = NULL; + if (pSrc->user_punch->commands != NULL) + { + user_punch->commands = string_duplicate(pSrc->user_punch->commands); + } + user_punch->new_def = TRUE; + user_punch->linebase = NULL; + user_punch->varbase = NULL; + user_punch->loopbase = NULL; + } + /* + user_punch_headings = NULL; + user_punch_count_headings = 0; + */ + user_punch_count_headings = pSrc->user_punch_count_headings; + if (user_punch_count_headings > 0) + { + user_punch_headings = (const char **) free_check_null(user_punch_headings); + user_punch_headings = (const char **) PHRQ_malloc((size_t) user_punch_count_headings * sizeof(char *)); + if (user_punch_headings == NULL) malloc_error(); + for (int i = 0; i < user_punch_count_headings; i++) + { + user_punch_headings[i] = string_hsave(pSrc->user_punch_headings[i]); + } + } +#endif + n_user_punch_index = pSrc->n_user_punch_index; + fpunchf_user_s_warning = pSrc->fpunchf_user_s_warning; + //fpunchf_user_buffer[0] = 0; + +#if defined PHREEQ98 + struct rate *user_graph; + char **user_graph_headings; + int user_graph_count_headings; +#endif +#if defined MULTICHART + // auto chart_handler; + chart_handler.Set_io(phrq_io); +#endif + /* ---------------------------------------------------------------------- + * GLOBAL DECLARATIONS + * ---------------------------------------------------------------------- */ + /* + error_string = NULL; + simulation = 0; + int state = INITIALIZE; + reaction_step = 0; + transport_step = 0; + transport_start = 0; + advection_step = 0; + stop_program = FALSE; + incremental_reactions = FALSE; + count_strings = 0; + array = NULL; + delta = NULL; + residual = NULL; + input_error = 0; + next_keyword = Keywords::KEY_NONE; + parse_error = 0; + paren_count = 0; + iterations = 0; + gamma_iterations = 0; + run_reactions_iterations= 0; + max_line = MAX_LINE; + line = NULL; + line_save = NULL; + LOG_10 = log(10.0); + debug_model = FALSE; + debug_prep = FALSE; + debug_set = FALSE; + debug_diffuse_layer = FALSE; + debug_inverse = FALSE; + */ + inv_tol_default = pSrc->inv_tol_default; + itmax = pSrc->itmax; + max_tries = pSrc->max_tries; + ineq_tol = pSrc->ineq_tol; + convergence_tolerance = pSrc->convergence_tolerance; + step_size = pSrc->step_size; + pe_step_size = pSrc->pe_step_size; + step_size_now = step_size; + pe_step_size_now = pe_step_size; + pp_scale = pSrc->pp_scale; + pp_column_scale = pSrc->pp_column_scale; + diagonal_scale = pSrc->diagonal_scale; + mass_water_switch = pSrc->mass_water_switch; + delay_mass_water = pSrc->delay_mass_water; + equi_delay = pSrc->equi_delay; + dampen_ah2o = pSrc->dampen_ah2o; + censor = pSrc->censor; + aqueous_only = pSrc->aqueous_only; + negative_concentrations = pSrc->negative_concentrations; + calculating_deriv = FALSE; + numerical_deriv = FALSE; + count_total_steps = 0; + phast = FALSE; + /* + llnl_temp = 0; + llnl_count_temp = 0; + llnl_adh = 0; + llnl_count_adh = 0; + llnl_bdh = 0; + llnl_count_bdh = 0; + llnl_bdot = 0; + llnl_count_bdot = 0; + llnl_co2_coefs = 0; + llnl_count_co2_coefs = 0; + */ + llnl_count_temp = pSrc->llnl_count_temp; + if (llnl_count_temp > 0) + { + llnl_temp = (LDBLE *) free_check_null(llnl_temp); + llnl_temp = (LDBLE *) PHRQ_malloc((size_t) llnl_count_temp * sizeof(LDBLE)); + if (llnl_temp == NULL) malloc_error(); + memcpy(llnl_temp, pSrc->llnl_temp, (size_t) llnl_count_temp * sizeof(LDBLE)); + } + llnl_count_adh = pSrc->llnl_count_adh; + if (llnl_count_adh > 0) + { + llnl_adh = (LDBLE *) free_check_null(llnl_adh); + llnl_adh = (LDBLE *) PHRQ_malloc((size_t) llnl_count_adh * sizeof(LDBLE)); + if (llnl_adh == NULL) malloc_error(); + memcpy(llnl_adh, pSrc->llnl_adh, (size_t) llnl_count_adh * sizeof(LDBLE)); + } + llnl_count_bdh = pSrc->llnl_count_bdh; + if (llnl_count_bdh > 0) + { + llnl_bdh = (LDBLE *) free_check_null(llnl_bdh); + llnl_bdh = (LDBLE *) PHRQ_malloc((size_t) llnl_count_bdh * sizeof(LDBLE)); + if (llnl_bdh == NULL) malloc_error(); + memcpy(llnl_bdh, pSrc->llnl_bdh, (size_t) llnl_count_bdh * sizeof(LDBLE)); + } + llnl_count_bdot = pSrc->llnl_count_bdot; + if (llnl_count_bdot > 0) + { + llnl_bdot = (LDBLE *) free_check_null(llnl_bdot); + llnl_bdot = (LDBLE *) PHRQ_malloc((size_t) llnl_count_bdot * sizeof(LDBLE)); + if (llnl_bdot == NULL) malloc_error(); + memcpy(llnl_bdot, pSrc->llnl_bdot, (size_t) llnl_count_bdot * sizeof(LDBLE)); + } + llnl_count_co2_coefs = pSrc->llnl_count_co2_coefs; + if (llnl_count_co2_coefs > 0) + { + llnl_co2_coefs = (LDBLE *) free_check_null(llnl_co2_coefs); + llnl_co2_coefs = (LDBLE *) PHRQ_malloc((size_t) llnl_count_co2_coefs * sizeof(LDBLE)); + if (llnl_co2_coefs == NULL) malloc_error(); + memcpy(llnl_co2_coefs, pSrc->llnl_co2_coefs, (size_t) llnl_count_co2_coefs * sizeof(LDBLE)); + } + + // Not implemented for now + //SelectedOutput_map = pSrc->SelectedOutput_map; + SelectedOutput_map.clear(); + + //selected_output_file_name = NULL; + //dump_file_name = NULL; + //remove_unstable_phases = FALSE; + // auto screen_string; + spread_length = 10; + /* ---------------------------------------------------------------------- */ + /* + * Hash definitions + */ + // auto strings_map; +#ifdef HASH + // auto strings_hash; +#endif + /* + elements_hash_table = NULL; + species_hash_table = NULL; + phases_hash_table = NULL; + logk_hash_table = NULL; + master_isotope_hash_table = NULL; + */ + /* ---------------------------------------------------------------------- + * ISOTOPES + * ---------------------------------------------------------------------- */ + /* + count_master_isotope = 0; + master_isotope = NULL; + max_master_isotope = MAX_ELTS; + */ + for (int i = 0; i < pSrc->count_master_isotope; i++) + { + struct master_isotope *master_isotope_ptr = master_isotope_store(pSrc->master_isotope[i]->name, FALSE); + memcpy(master_isotope_ptr, pSrc->master_isotope[i], sizeof(struct master_isotope)); + master_isotope_ptr->name = string_hsave(pSrc->master_isotope[i]->name); + int n; + master_isotope_ptr->master = NULL; + if (pSrc->master_isotope[i]->master) + { + char * name = string_duplicate(pSrc->master_isotope[i]->master->elt->name); + master_isotope_ptr->master = master_search(name, &n); + free_check_null(name); + } + if (master_isotope_ptr->master == NULL) + { + //error_msg("Error in copy constructor for master_isotope.", STOP); + } + master_isotope_ptr->elt = NULL; + if (pSrc->master_isotope[i]->elt) + { + master_isotope_ptr->elt = element_store(pSrc->master_isotope[i]->elt->name); + } + master_isotope_ptr->units = NULL; + if (pSrc->master_isotope[i]->units) + { + master_isotope_ptr->units = string_hsave(pSrc->master_isotope[i]->units); + } + } + initial_solution_isotopes = pSrc->initial_solution_isotopes; + /* + count_calculate_value = 0; + calculate_value = NULL; + max_calculate_value = MAX_ELTS; + calculate_value_hash_table = NULL; + */ + for (int i = 0; i < pSrc->count_calculate_value; i++) + { + struct calculate_value *calculate_value_ptr = calculate_value_store(pSrc->calculate_value[i]->name, FALSE); + memcpy(calculate_value_ptr, pSrc->calculate_value[i], sizeof(struct calculate_value)); + calculate_value_ptr->value = pSrc->calculate_value[i]->value; + calculate_value_ptr->commands = NULL; + if (pSrc->calculate_value[i]->commands) + { + calculate_value_ptr->commands = string_duplicate(pSrc->calculate_value[i]->commands); + } + calculate_value_ptr->new_def = TRUE; + calculate_value_ptr->calculated = FALSE; + calculate_value_ptr->linebase = NULL; + calculate_value_ptr->varbase = NULL; + calculate_value_ptr->loopbase = NULL; + } + /* + count_isotope_ratio = 0; + isotope_ratio = 0; + max_isotope_ratio = MAX_ELTS; + isotope_ratio_hash_table = 0; + */ + for (int i = 0; i < pSrc->count_isotope_ratio; i++) + { + struct isotope_ratio *isotope_ratio_ptr = isotope_ratio_store(pSrc->isotope_ratio[i]->name, FALSE); + isotope_ratio_ptr->name = string_hsave(pSrc->isotope_ratio[i]->name); + isotope_ratio_ptr->isotope_name = string_hsave(pSrc->isotope_ratio[i]->isotope_name); + isotope_ratio_ptr->ratio = pSrc->isotope_ratio[i]->ratio; + isotope_ratio_ptr->converted_ratio = pSrc->isotope_ratio[i]->converted_ratio; + } + /* + count_isotope_alpha = 0; + isotope_alpha = 0; + max_isotope_alpha = MAX_ELTS; + isotope_alpha_hash_table = 0; + */ + for (int i = 0; i < pSrc->count_isotope_alpha; i++) + { + struct isotope_alpha *isotope_alpha_ptr = isotope_alpha_store(pSrc->isotope_alpha[i]->name, FALSE); + isotope_alpha_ptr->named_logk = string_hsave(pSrc->isotope_alpha[i]->named_logk); + isotope_alpha_ptr->value = pSrc->isotope_alpha[i]->value; + } + + phreeqc_mpi_myself = 0; + first_read_input = TRUE; + user_database = string_duplicate(pSrc->user_database); + //have_punch_name = pSrc->have_punch_name; + print_density = pSrc->print_density; +#ifdef SKIP + zeros = NULL; + zeros_max = 1; + cell_pore_volume = 0; + cell_volume = 0; + cell_porosity = 0; + cell_saturation = 0; + sys = NULL; + count_sys = 0; + max_sys = 0; + sys_tot = 0; + + V_solutes = 0.0; + rho_0 = 0; + kappa_0 = 0.0; + p_sat = 0.0; + eps_r = EPSILON; + DH_A = 0.0; + DH_B = 0.0; + DH_Av = 0.0; + QBrn = 0.0; + ZBrn = 0.0; + dgdP = 0.0; + + need_temp_msg = 0; + solution_mass = 0; + solution_volume = 0; + /* phqalloc.cpp ------------------------------- */ + s_pTail = NULL; + /* Basic */ + basic_interpreter = NULL; + /* cl1.cpp ------------------------------- */ + x_arg = NULL; + res_arg = NULL; + scratch = NULL; + x_arg_max = 0; + res_arg_max = 0; + scratch_max = 0; + /* dw.cpp ------------------------------- */ + /* COMMON /QQQQ/ */ + Q0 = 0; + Q5 = 0; + GASCON = 0.461522e0; + TZ = 647.073e0; + AA = 1.e0; + Z = 0; + DZ = 0; + Y = 0; + G1 = 11.e0; + G2 = 44.333333333333e0; + GF = 3.5e0; + B1 = 0; + B2 = 0; + B1T = 0; + B2T = 0; + B1TT = 0; + B2TT = 0; + /* gases.cpp ------------------------------- */ + a_aa_sum = 0; + b2 = 0; + b_sum = 0; + R_TK = 0; + /* input.cpp ------------------------------- */ + check_line_return = 0; + reading_db = FALSE; + /* integrate.cpp ------------------------------- */ + midpoint_sv = 0; + z_global = 0; + xd_global = 0; + alpha_global = 0; + /* inverse.cpp ------------------------------- */ + max_row_count = 50; + max_column_count = 50; + carbon = FALSE; + col_name = NULL; + row_name = NULL; + count_rows = 0; + count_optimize = 0; + col_phases = 0; + col_redox = 0; + col_epsilon = 0; + col_ph = 0; + col_water = 0; + col_isotopes = 0; + col_phase_isotopes = 0; + row_mb = 0; + row_fract = 0; + row_charge = 0; + row_carbon = 0; + row_isotopes = 0; + row_epsilon = 0; + row_isotope_epsilon = 0; + row_water = 0; + inv_zero = NULL; + array1 = 0; + inv_res = NULL; + inv_delta1 = NULL; + delta2 = NULL; + delta3 = NULL; + inv_cu = NULL; + delta_save = NULL; + min_delta = NULL; + max_delta = NULL; + inv_iu = NULL; + inv_is = NULL; + klmd = 0; + nklmd = 0; + n2d = 0; + kode = 0; + iter = 0; + toler = 0; + error = 0; + max_pct = 0; + scaled_error = 0; + master_alk = NULL; + row_back = NULL; + col_back = NULL; + good = NULL; + bad = NULL; + minimal = NULL; + max_good = 0; + max_bad = 0; + max_minimal = 0; + count_good = 0; + count_bad = 0; + count_minimal = 0; + count_calls = 0; + soln_bits = 0; + phase_bits = 0; + current_bits = 0; + temp_bits = 0; + netpath_file = NULL; + count_inverse_models = 0; + count_pat_solutions = 0; + for (int i = 0; i < 32; i++) + { + min_position[i] = 0; + max_position[i] = 0; + now[i] = 0; + } + /* kinetics.cpp ------------------------------- */ + count_pp = count_pg = count_ss = 0; + cvode_kinetics_ptr = NULL; + cvode_test = FALSE; + cvode_error = FALSE; + cvode_n_user = -99; + cvode_n_reactions = -99; + cvode_step_fraction = 0.0; + cvode_rate_sim_time = 0.0; + cvode_rate_sim_time_start = 0.0; + cvode_last_good_time = 0.0; + cvode_prev_good_time = 0.0; + cvode_last_good_y = NULL; + cvode_prev_good_y = NULL; + kinetics_machEnv = NULL; + kinetics_y = NULL; + kinetics_abstol = NULL; + kinetics_cvode_mem = NULL; + cvode_pp_assemblage_save= NULL; + cvode_ss_assemblage_save= NULL; + m_original = NULL; + m_temp = NULL; + rk_moles = NULL; + set_and_run_attempt = 0; + x0_moles = NULL; + /* model.cpp ------------------------------- */ + gas_in = FALSE; + min_value = 1e-10; + normal = NULL; + ineq_array = NULL; + res = NULL; + cu = NULL; + zero = NULL; + delta1 = NULL; + iu = NULL; + is = NULL; + back_eq = NULL; + normal_max = 0; + ineq_array_max = 0; + res_max = 0; + cu_max = 0; + zero_max = 0; + delta1_max = 0; + iu_max = 0; + is_max = 0; + back_eq_max = 0; + /* phrq_io_output.cpp ------------------------------- */ + forward_output_to_log = 0; + /* phreeqc_files.cpp ------------------------------- */ + default_data_base = string_duplicate("phreeqc.dat"); +#ifdef PHREEQ98 + int outputlinenr; + char *LogFileNameC; + char progress_str[512]; +#endif +#endif + /* Pitzer */ + pitzer_model = pSrc->pitzer_model; + sit_model = pSrc->sit_model; + pitzer_pe = pSrc->pitzer_pe; + + //full_pitzer = FALSE; + //always_full_pitzer = FALSE; + //ICON = TRUE; + //IC = -1; + //COSMOT = 0; + //AW = 0; + //VP = 0; + //DW0 = 0; + + ICON = pSrc->ICON; + /* + pitz_params = NULL; + count_pitz_param = 0; + max_pitz_param = 100; + */ + for (int i = 0; i < pSrc->count_pitz_param; i++) + { + pitz_param_store(pSrc->pitz_params[i], true); + } + + // auto pitz_param_map + /* + theta_params = 0; + count_theta_param = 0; + max_theta_param = 100; + use_etheta = TRUE; + OTEMP = -100.0; + OPRESS = -100.0; + A0 = 0; + spec = NULL; + cations = NULL; + anions = NULL; + neutrals = NULL; + count_cations = 0; + count_anions = 0; + count_neutrals = 0; + MAXCATIONS = 0; + FIRSTANION = 0; + MAXNEUTRAL = 0; + mcb0 = NULL; + mcb1 = NULL; + mcc0 = NULL; + IPRSNT = NULL; + M = NULL; + LGAMMA = NULL; + for (int i = 0; i < 23; i++) + { + BK[i] = 0.0; + DK[i] = 0.0; + } + */ + +#ifdef PHREEQ98 + int connect_simulations, graph_initial_solutions; + int shifts_as_points; + int chart_type; + int ShowChart; + int RowOffset, ColumnOffset; +#endif + dummy = 0; + /* print.cpp ------------------------------- */ + /* + sformatf_buffer = (char *) PHRQ_malloc(256 * sizeof(char)); + if (sformatf_buffer == NULL) + malloc_error(); + sformatf_buffer_size = 256; + */ +#ifdef PHREEQ98 + int colnr, rownr; + int graph_initial_solutions; + int prev_advection_step, prev_transport_step; /*, prev_reaction_step */ + /* int shifts_as_points; */ + int chart_type; + int AddSeries; + int FirstCallToUSER_GRAPH; +#endif + /* read.cpp */ + prev_next_char = NULL; +#if defined PHREEQ98 + int shifts_as_points; +#endif + /* read_class.cxx */ + // auto dump_info + // auto delete_info + // auto run_info + /* + run_info.Set_io(phrq_io); + */ + /* readtr.cpp */ + // auto dump_file_name_cpp; + /* sit.cpp ------------------------------- */ +/* + sit_params = NULL; + count_sit_param = 0; + max_sit_param = 100; + // auto sit_param_map + sit_A0 = 0; + sit_count_cations = 0; + sit_count_anions = 0; + sit_count_neutrals = 0; + sit_MAXCATIONS = 0; + sit_FIRSTANION = 0; + sit_MAXNEUTRAL = 0; + sit_IPRSNT = NULL; + sit_M = NULL; + sit_LGAMMA = NULL; +*/ + + for (int i = 0; i < pSrc->count_sit_param; i++) + { + sit_param_store(pSrc->sit_params[i], true); + } + /* tidy.cpp ------------------------------- */ + //a0 = 0; + //a1 = 0; + //kc = 0; + //kb = 0; + /* tally.cpp ------------------------------- */ + //t_buffer = NULL; + //tally_count_component = 0; + //tally_table = NULL; + //count_tally_table_columns = 0; + //count_tally_table_rows = 0; + + /* transport.cpp ------------------------------- */ + /* storage is created and freed in tranport.cpp */ + sol_D = NULL; + sol_D_dbg = NULL; + J_ij = NULL; + J_ij_il = NULL; + J_ij_count_spec = pSrc->J_ij_count_spec; + m_s = NULL; + count_m_s = pSrc->count_m_s; + tot1_h = pSrc->tot1_h; + tot1_o = pSrc->tot1_o; + tot2_h = pSrc->tot2_h; + tot2_o = pSrc->tot2_o; + diffc_max = pSrc->diffc_max; + diffc_tr = pSrc->diffc_tr; + J_ij_sum = pSrc->J_ij_sum; + transp_surf = pSrc->transp_surf; + heat_mix_array = NULL; + temp1 = NULL; + temp2 = NULL; + nmix = pSrc->nmix; + heat_nmix = pSrc->heat_nmix; + heat_mix_f_imm = pSrc->heat_mix_f_imm; + heat_mix_f_m = pSrc->heat_mix_f_m; + warn_MCD_X = pSrc->warn_MCD_X; + warn_fixed_Surf = pSrc->warn_fixed_Surf; + +#ifdef PHREEQ98 + int AutoLoadOutputFile, CreateToC; + int ProcessMessages, ShowProgress, ShowProgressWindow, ShowChart; + int outputlinenr; + int stop_calculations; + char err_str98[80]; +#endif + /* utilities.cpp ------------------------------- */ + //spinner = 0; + //// keycount; + //for (int i = 0; i < Keywords::KEY_COUNT_KEYWORDS; i++) + //{ + // keycount.push_back(0); + //} + + // make sure new_model gets set + this->keycount[Keywords::KEY_SOLUTION_SPECIES] = 1; + this->tidy_model(); + return; +} +// Operator overloaded using a member function +Phreeqc &Phreeqc::operator=(const Phreeqc &rhs) +{ + if (this == &rhs) // Same object? + return *this; + + // clean up this here + this->clean_up(); + + this->PHRQ_free_all(); + if (this->phrq_io == &this->ioInstance) + { + this->phrq_io->clear_istream(); + this->phrq_io->close_ostreams(); + } + + // copy Phreeqc object to this + this->phrq_io = rhs.phrq_io; + this->init(); + this->initialize(); + this->InternalCopy(&rhs); + return *this; +} + +int Phreeqc::next_user_number(Keywords::KEYWORDS key) +{ + switch (key) + { + case Keywords::KEY_REACTION_TEMPERATURE: + return Utilities::Rxn_next_user_number(Rxn_temperature_map); + break; + case Keywords::KEY_REACTION_PRESSURE: + return Utilities::Rxn_next_user_number(Rxn_pressure_map); + break; + case Keywords::KEY_SURFACE: + return Utilities::Rxn_next_user_number(Rxn_surface_map); + break; + case Keywords::KEY_EXCHANGE: + return Utilities::Rxn_next_user_number(Rxn_exchange_map); + break; + case Keywords::KEY_KINETICS: + return Utilities::Rxn_next_user_number(Rxn_kinetics_map); + break; + case Keywords::KEY_MIX: + return Utilities::Rxn_next_user_number(Rxn_mix_map); + break; + case Keywords::KEY_REACTION: + return Utilities::Rxn_next_user_number(Rxn_reaction_map); + break; + case Keywords::KEY_GAS_PHASE: + return Utilities::Rxn_next_user_number(Rxn_gas_phase_map); + break; + case Keywords::KEY_SOLID_SOLUTIONS: + return Utilities::Rxn_next_user_number(Rxn_ss_assemblage_map); + break; + case Keywords::KEY_EQUILIBRIUM_PHASES: + return Utilities::Rxn_next_user_number(Rxn_pp_assemblage_map); + break; + case Keywords::KEY_SOLUTION: + return Utilities::Rxn_next_user_number(Rxn_solution_map); + break; + default: + assert(false); + return -999; + } +} diff --git a/phreeqcpp/Phreeqc.h b/phreeqcpp/Phreeqc.h new file mode 100644 index 00000000..d02e2326 --- /dev/null +++ b/phreeqcpp/Phreeqc.h @@ -0,0 +1,2248 @@ +#ifndef _INC_PHREEQC_H +#define _INC_PHREEQC_H +#if defined(WIN32) +#include +#endif +#if defined(WIN32_MEMORY_DEBUG) +#define _CRTDBG_MAP_ALLOC +#include +#endif +#ifndef boolean +typedef unsigned char boolean; +#endif +/* ---------------------------------------------------------------------- +* INCLUDE FILES +* ---------------------------------------------------------------------- */ +#include +#include +#include +#include +#ifdef HASH +#include +#endif +#include +#include +#include +#include +#include +#include +#include "phrqtype.h" +#include "cvdense.h" +#include "runner.h" +#include "dumper.h" +#include "PHRQ_io.h" +#include "SelectedOutput.h" +#include "UserPunch.h" +#ifdef MULTICHART +#include "ChartHandler.h" +#endif +#include "Keywords.h" +#include "Pressure.h" +#include "cxxMix.h" +#include "Use.h" +#include "Surface.h" +#ifdef SWIG_SHARED_OBJ +#include "thread.h" +#endif + +class cxxNameDouble; +class cxxKinetics; +//class cxxMix; +class cxxKineticsComp; +class cxxExchange; +class cxxExchComp; +class cxxGasPhase; +class cxxTemperature; +class cxxPPassemblage; +class cxxPPassemblageComp; +class cxxReaction; +class cxxSolution; +class cxxISolutionComp; +class cxxSolutionIsotope; +class cxxSSassemblage; +class cxxSS; +class cxxStorageBin; + +#include "global_structures.h" +class PBasic; + +class Phreeqc +{ +public: + Phreeqc(PHRQ_io *io = NULL); + Phreeqc(const Phreeqc &src); + void InternalCopy(const Phreeqc *pSrc); + Phreeqc &operator=(const Phreeqc &rhs); + ~Phreeqc(void); + +public: + // + // Phreeqc class methods + // + + // advection.cpp ------------------------------- + int advection(void); + + // basicsubs.cpp ------------------------------- + int basic_compile(char *commands, void **lnbase, void **vbase, void **lpbase); + int basic_run(char *commands, void *lnbase, void *vbase, void *lpbase); + void basic_free(void); +#ifdef IPHREEQC_NO_FORTRAN_MODULE + double basic_callback(double x1, double x2, char * str); +#else + double basic_callback(double x1, double x2, const char * str); +#endif + void register_basic_callback(double ( *fcn)(double x1, double x2, const char *str, void *cookie), void *cookie1); +#ifdef IPHREEQC_NO_FORTRAN_MODULE + void register_fortran_basic_callback(double ( *fcn)(double *x1, double *x2, char *str, size_t l)); +#else + void register_fortran_basic_callback(double ( *fcn)(double *x1, double *x2, const char *str, int l)); +#endif + + LDBLE activity(const char *species_name); + LDBLE activity_coefficient(const char *species_name); + LDBLE log_activity_coefficient(const char *species_name); + LDBLE aqueous_vm(const char *species_name); + LDBLE phase_vm(const char *phase_name); + LDBLE diff_c(const char *species_name); + LDBLE setdiff_c(const char *species_name, double d); + LDBLE sa_declercq(double type, double sa, double d, double m, double m0, double gfw); + LDBLE calc_SC(void); + /* VP: Density Start */ + LDBLE calc_dens(void); + /* VP: Density End */ + LDBLE calc_logk_n(const char *name); + LDBLE calc_logk_p(const char *name); + LDBLE calc_logk_s(const char *name); + LDBLE calc_surface_charge(const char *surface_name); + LDBLE calc_t_sc(const char *name); + LDBLE diff_layer_total(const char *total_name, const char *surface_name); + LDBLE edl_species(const char *surf_name, LDBLE * count, char ***names, LDBLE ** moles, LDBLE * area, LDBLE * thickness); + int get_edl_species(cxxSurfaceCharge & charge_ref); + LDBLE equi_phase(const char *phase_name); + LDBLE equi_phase_delta(const char *phase_name); + LDBLE equivalent_fraction(const char *name, LDBLE *eq, std::string &elt_name); + LDBLE find_gas_comp(const char *gas_comp_name); + LDBLE find_gas_p(void); + LDBLE find_gas_vm(void); + LDBLE find_misc1(const char *ss_name); + LDBLE find_misc2(const char *ss_name); + LDBLE find_ss_comp(const char *ss_comp_name); + LDBLE get_calculate_value(const char *name); + char * iso_unit(const char *total_name); + LDBLE iso_value(const char *total_name); + LDBLE kinetics_moles(const char *kinetics_name); + LDBLE kinetics_moles_delta(const char *kinetics_name); + LDBLE log_activity(const char *species_name); + LDBLE log_molality(const char *species_name); + LDBLE molality(const char *species_name); + LDBLE pressure(void); + LDBLE pr_pressure(const char *phase_name); + LDBLE pr_phi(const char *phase_name); + LDBLE saturation_ratio(const char *phase_name); + int saturation_index(const char *phase_name, LDBLE * iap, LDBLE * si); + int solution_number(void); + LDBLE solution_sum_secondary(const char *total_name); + LDBLE sum_match_gases(const char *stemplate, const char *name); + LDBLE sum_match_species(const char *stemplate, const char *name); + LDBLE sum_match_ss(const char *stemplate, const char *name); + int match_elts_in_species(const char *name, const char *stemplate); + int extract_bracket(char **string, char *bracket_string); + LDBLE surf_total(const char *total_name, const char *surface_name); + LDBLE surf_total_no_redox(const char *total_name, const char *surface_name); + static int system_species_compare(const void *ptr1, const void *ptr2); + LDBLE system_total(const char *total_name, LDBLE * count, char ***names, + char ***types, LDBLE ** moles); + std::string kinetics_formula(std::string kinetics_name, cxxNameDouble &stoichiometry); + std::string phase_formula(std::string phase_name, cxxNameDouble &stoichiometry); + std::string species_formula(std::string phase_name, cxxNameDouble &stoichiometry); + LDBLE list_ss(std::string ss_name, cxxNameDouble &composition); + int system_total_elements(void); + int system_total_si(void); + int system_total_aq(void); + int system_total_ex(void); + int system_total_surf(void); + int system_total_gas(void); + int system_total_equi(void); + int system_total_kin(void); + int system_total_ss(void); + int system_total_elt(const char *total_name); + int system_total_elt_secondary(const char *total_name); + LDBLE total(const char *total_name); + LDBLE total_mole(const char *total_name); + int system_total_solids(cxxExchange *exchange_ptr, + cxxPPassemblage *pp_assemblage_ptr, + cxxGasPhase *gas_phase_ptr, + cxxSSassemblage *ss_assemblage_ptr, + cxxSurface *surface_ptr); + + static LDBLE f_rho(LDBLE rho_old, void *cookie); + static LDBLE f_Vm(LDBLE v1, void *cookie); + LDBLE calc_solution_volume(void); + + // chart.cpp +#if defined PHREEQ98 + void DeleteCurves(void); + void ExtractCurveInfo(char *line, int curvenr); + void GridChar(char *s, char *a); + void MallocCurves(int nc, int ncxy); + int OpenCSVFile(char file_name[MAX_LENGTH]); + void SaveCurvesToFile(char file_name[MAX_LENGTH]); + void PlotXY(char *x, char *y); + void ReallocCurves(int new_nc); + void ReallocCurveXY(int i); + void SetAxisScale(char *a, int j, char *token, int true_); + void SetAxisTitles(char *s, int i); + void SetChartTitle(char *s); + void start_chart(bool end); +#endif + + // cl1.cpp ------------------------------- + int cl1(int k, int l, int m, int n, + int nklmd, int n2d, + LDBLE * q, + int *kode, LDBLE toler, + int *iter, LDBLE * x, LDBLE * res, LDBLE * error, + LDBLE * cu, int *iu, int *s, int check); + void cl1_space(int check, int n2d, int klm, int nklmd); + + // cl1mp.cpp ------------------------------- + int cl1mp(int k, int l, int m, int n, + int nklmd, int n2d, + LDBLE * q_arg, + int *kode, LDBLE toler, + int *iter, LDBLE * x_arg, LDBLE * res_arg, LDBLE * error, + LDBLE * cu_arg, int *iu, int *s, int check, LDBLE censor_arg); + + // class_main.cpp ------------------------------- + int write_banner(void); + + /* default.cpp */ +public: + int close_input_files(void); + int close_output_files(void); + static int istream_getc(void *cookie); + int process_file_names(int argc, char *argv[], std::istream **db_cookie, + std::istream **input_cookie, int log); + + /* PHRQ_io_output.cpp */ + void screen_msg(const char * str); + + void echo_msg(const char *err_str); + int warning_msg(const char *err_str); + void set_forward_output_to_log(int value); + int get_forward_output_to_log(void); + + // dump_ostream + bool dump_open(const char *file_name); + void dump_flush(void); + void dump_close(void); + void dump_msg(const char * str); + + // log_ostream + bool log_open(const char *file_name); + void log_flush(void); + void log_close(void); + void log_msg(const char * str); + + // error_ostream + bool error_open(const char *file_name); + void error_flush(void); + void error_close(void); + void error_msg(const char * str, bool stop=false); + + // output_ostream + bool output_open(const char *file_name); + void output_flush(void); + void output_close(void); + void output_msg(const char * str); + + // punch_ostream + bool punch_open(const char *file_name, int n_user); + void punch_flush(void); + void punch_close(void); + void punch_msg(const char * str); + + void fpunchf_heading(const char *name); + void fpunchf(const char *name, const char *format, double d); + void fpunchf(const char *name, const char *format, char * d); + void fpunchf(const char *name, const char *format, int d); + void fpunchf_user(int user_index, const char *format, double d); + void fpunchf_user(int user_index, const char *format, char * d); + int fpunchf_end_row(const char *format); +#ifdef SKIP + // dw.cpp ------------------------------- + int BB(LDBLE T); + LDBLE PS(LDBLE T); + LDBLE VLEST(LDBLE T); + int DFIND(LDBLE * DOUT, LDBLE P, LDBLE D, LDBLE T); + int QQ(LDBLE T, LDBLE D); + LDBLE BASE(LDBLE D); +#endif + // input.cpp ------------------------------- + int reading_database(void); + void set_reading_database(int reading_database); + int check_line(const char *string, int allow_empty, int allow_eof, + int allow_keyword, int print); + int add_char_to_line(int *i, char c); + int check_line_impl(const char *string, int allow_empty, + int allow_eof, int allow_keyword, int print); + int get_line(void); + int get_logical_line(void *cookie, int *l); + int read_database(void); + int run_simulations(void); + + // integrate.cpp ------------------------------- + int calc_all_g(void); + int calc_init_g(void); + int initial_surface_water(void); + int sum_diffuse_layer(cxxSurfaceCharge *surface_charge_ptr1); + int calc_all_donnan(void); + int calc_init_donnan(void); + LDBLE g_function(LDBLE x_value); + LDBLE midpnt(LDBLE x1, LDBLE x2, int n); + void polint(LDBLE * xa, LDBLE * ya, int n, LDBLE xv, LDBLE * yv, + LDBLE * dy); + LDBLE qromb_midpnt(cxxSurfaceCharge *charge_ptr, LDBLE x1, LDBLE x2); + LDBLE calc_psi_avg(cxxSurfaceCharge *charge_ptr, LDBLE surf_chrg_eq); + int calc_all_donnan_music(void); + int calc_init_donnan_music(void); + + // inverse.cpp ------------------------------- + int inverse_models(void); + int add_to_file(const char *filename, const char *string); + int bit_print(unsigned long bits, int l); + int carbon_derivs(struct inverse *inv_ptr); + int check_isotopes(struct inverse *inv_ptr); + int check_solns(struct inverse *inv_ptr); + int count_isotope_unknowns(struct inverse *inv_ptr, + struct isotope **isotope_unknowns); + cxxSolutionIsotope *get_isotope(cxxSolution *solution_ptr, const char *elt); + LDBLE get_inv_total(cxxSolution *solution_ptr, const char *elt); + int isotope_balance_equation(struct inverse *inv_ptr, int row, int n); + int post_mortem(void); + bool test_cl1_solution(void); + unsigned long get_bits(unsigned long bits, int position, int number); + unsigned long minimal_solve(struct inverse *inv_ptr, + unsigned long minimal_bits); + void dump_netpath(struct inverse *inv_ptr); + int dump_netpath_pat(struct inverse *inv_ptr); + int next_set_phases(struct inverse *inv_ptr, int first_of_model_size, + int model_size); + int phase_isotope_inequalities(struct inverse *inv_ptr); + int print_model(struct inverse *inv_ptr); + int punch_model_heading(struct inverse *inv_ptr); + int punch_model(struct inverse *inv_ptr); + void print_isotope(FILE * netpath_file, cxxSolution *solution_ptr, + const char *elt, const char *string); + void print_total(FILE * netpath_file, cxxSolution *solution_ptr, + const char *elt, const char *string); + void print_total_multi(FILE * netpath_file, cxxSolution *solution_ptr, + const char *string, const char *elt0, + const char *elt1, const char *elt2, const char *elt3, + const char *elt4); + + void print_total_pat(FILE * netpath_file, const char *elt, + const char *string); + int range(struct inverse *inv_ptr, unsigned long cur_bits); + int save_bad(unsigned long bits); + int save_good(unsigned long bits); + int save_minimal(unsigned long bits); + unsigned long set_bit(unsigned long bits, int position, int value); + int setup_inverse(struct inverse *inv_ptr); + int set_initial_solution(int n_user_old, int n_user_new); + int set_ph_c(struct inverse *inv_ptr, + int i, cxxSolution *soln_ptr_orig, int n_user_new, + LDBLE d_alk, LDBLE ph_factor, LDBLE alk_factor); + int shrink(struct inverse *inv_ptr, LDBLE * array_in, + LDBLE * array_out, int *k, int *l, int *m, int *n, + unsigned long cur_bits, LDBLE * delta_l, int *col_back_l, + int *row_back_l); + int solve_inverse(struct inverse *inv_ptr); + int solve_with_mask(struct inverse *inv_ptr, unsigned long cur_bits); + int subset_bad(unsigned long bits); + int subset_minimal(unsigned long bits); + int superset_minimal(unsigned long bits); + int write_optimize_names(struct inverse *inv_ptr); + + // isotopes.cpp ------------------------------- + int add_isotopes(cxxSolution &solution_ptr); + int calculate_values(void); + int calculate_isotope_moles(struct element *elt_ptr, + cxxSolution *solution_ptr, LDBLE total_moles); + LDBLE convert_isotope(struct master_isotope *master_isotope_ptr, LDBLE ratio); + int from_pcil(struct master_isotope *master_isotope_ptr); + int from_permil(struct master_isotope *master_isotope_ptr, LDBLE major_total); + int from_pct(struct master_isotope *master_isotope_ptr, LDBLE major_total); + int from_tu(struct master_isotope *master_isotope_ptr); + struct calculate_value *calculate_value_alloc(void); + int calculate_value_free(struct calculate_value *calculate_value_ptr); + struct calculate_value *calculate_value_search(const char *name); + struct calculate_value *calculate_value_store(const char *name, + int replace_if_found); + struct isotope_alpha *isotope_alpha_alloc(void); + struct isotope_alpha *isotope_alpha_search(const char *name); + struct isotope_alpha *isotope_alpha_store(const char *name, + int replace_if_found); + struct isotope_ratio *isotope_ratio_alloc(void); + struct isotope_ratio *isotope_ratio_search(const char *name); + struct isotope_ratio *isotope_ratio_store(const char *name, + int replace_if_found); + struct master_isotope *master_isotope_store(const char *name, + int replace_if_found); + struct master_isotope *master_isotope_alloc(void); + struct master_isotope *master_isotope_search(const char *name); + int print_initial_solution_isotopes(void); + int print_isotope_ratios(void); + int print_isotope_alphas(void); + int punch_isotopes(void); + int punch_calculate_values(void); + int read_calculate_values(void); + int read_isotopes(void); + int read_isotope_ratios(void); + int read_isotope_alphas(void); + int calculate_value_init(struct calculate_value *calculate_value_ptr); + int isotope_alpha_init(struct isotope_alpha *isotope_alpha_ptr); + int isotope_ratio_init(struct isotope_ratio *isotope_ratio_ptr); + int master_isotope_init(struct master_isotope *master_isotope_ptr); + + // kinetics.cpp ------------------------------- + void cvode_init(void); + bool cvode_update_reactants(int i, int nsaver, bool save_it); + int run_reactions(int i, LDBLE kin_time, int use_mix, LDBLE step_fraction); + int set_and_run(int i, int use_mix, int use_kinetics, int nsaver, + LDBLE step_fraction); + int set_and_run_wrapper(int i, int use_mix, int use_kinetics, int nsaver, + LDBLE step_fraction); + int set_advection(int i, int use_mix, int use_kinetics, int nsaver); + int free_cvode(void); +public: + static void f(integertype N, realtype t, N_Vector y, N_Vector ydot, + void *f_data); + static void Jac(integertype N, DenseMat J, RhsFn f, void *f_data, realtype t, + N_Vector y, N_Vector fy, N_Vector ewt, realtype h, + realtype uround, void *jac_data, long int *nfePtr, + N_Vector vtemp1, N_Vector vtemp2, N_Vector vtemp3); + + int calc_final_kinetic_reaction(cxxKinetics *kinetics_ptr); + int calc_kinetic_reaction(cxxKinetics *kinetics_ptr, + LDBLE time_step); + bool limit_rates(cxxKinetics *kinetics_ptr); + int rk_kinetics(int i, LDBLE kin_time, int use_mix, int nsaver, + LDBLE step_fraction); + int set_reaction(int i, int use_mix, int use_kinetics); + int set_transport(int i, int use_mix, int use_kinetics, int nsaver); + int store_get_equi_reactants(int k, int kin_end); + + // mainsubs.cpp ------------------------------- + std::ifstream * open_input_stream(char *query, char *default_name, std::ios_base::openmode mode, bool batch); + std::ofstream * open_output_stream(char *query, char *default_name, std::ios_base::openmode mode, bool batch); + int copy_entities(void); + void do_mixes(void); + void initialize(void); + int initial_exchangers(int print); + int initial_gas_phases(int print); + int initial_solutions(int print); + int solution_mix(void); + int step_save_exch(int n_user); + int step_save_surf(int n_user); + int initial_surfaces(int print); + int reactions(void); + int saver(void); + int xsolution_save(int k_user); + int xexchange_save(int n_user); + int xgas_save(int n_user); + int xpp_assemblage_save(int n_user); + int xss_assemblage_save(int n_user); + int xsurface_save(int n_user); + int do_initialize(void); + int do_status(void); + void save_init(int i); + int copy_use(int i); + int set_use(void); + + // model.cpp ------------------------------- + int check_residuals(void); + int free_model_allocs(void); + int ineq(int kode); + int model(void); + int jacobian_sums(void); + int mb_gases(void); + int mb_ss(void); + int mb_sums(void); + int molalities(int allow_overflow); + int reset(void); + int residuals(void); + int set(int initial); + int sum_species(void); + int surface_model(void); + LDBLE ss_root(LDBLE a0, LDBLE a1, LDBLE kc, LDBLE kb, LDBLE xcaq, + LDBLE xbaq); + LDBLE ss_halve(LDBLE a0, LDBLE a1, LDBLE x0, LDBLE x1, LDBLE kc, + LDBLE kb, LDBLE xcaq, LDBLE xbaq); + LDBLE ss_f(LDBLE xb, LDBLE a0, LDBLE a1, LDBLE kc, LDBLE kb, + LDBLE xcaq, LDBLE xbaq); + int numerical_jacobian(void); + void set_inert_moles(void); + void unset_inert_moles(void); +#ifdef SLNQ + int add_trivial_eqns(int rows, int cols, LDBLE * matrix); + //int slnq(int n, LDBLE * a, LDBLE * delta, int ncols, int print); +#endif + int calc_gas_pressures(void); + int calc_fixed_volume_gas_pressures(void); + int calc_ss_fractions(void); + int gammas(LDBLE mu); + int initial_guesses(void); + int revise_guesses(void); + int ss_binary(cxxSS *ss_ptr); + int ss_ideal(cxxSS *ss_ptr); + void ineq_init(int max_row_count, int max_column_count); + + // parse.cpp ------------------------------- + int check_eqn(int association); + int get_charge(char *charge, LDBLE * z); + int get_elt(char **t_ptr, char *element, int *i); + int get_elts_in_species(char **t_ptr, LDBLE coef); + int get_num(char **t_ptr, LDBLE * num); + int get_secondary_in_species(char **t_ptr, LDBLE coef); + int parse_eq(char *eqn, struct elt_list **elt_ptr, int association); + int get_coef(LDBLE * coef, char **eqnaddr); + int get_secondary(char **t_ptr, char *element, int *i); + int get_species(char **ptr); + + // phqalloc.cpp ------------------------------- +public: +#if !defined(NDEBUG) + void *PHRQ_malloc(size_t, const char *, int); + void *PHRQ_calloc(size_t, size_t, const char *, int); + void *PHRQ_realloc(void *, size_t, const char *, int); +#else + void *PHRQ_malloc(size_t); + void *PHRQ_calloc(size_t, size_t); + void *PHRQ_realloc(void *, size_t); +#endif + void PHRQ_free(void *ptr); + void PHRQ_free_all(void); + +public: + + // pitzer.cpp ------------------------------- + struct pitz_param *pitz_param_read(char *string, int n); + void pitz_param_store(struct pitz_param *pzp_ptr, bool force_copy); + void sit_param_store(struct pitz_param *pzp_ptr, bool force_copy); + struct theta_param *theta_param_search(LDBLE zj, LDBLE zk); + struct theta_param *theta_param_alloc(void); + int theta_param_init(struct theta_param *theta_param_ptr); + void pitzer_make_lists(void); + int gammas_pz(void); + int model_pz(void); + int pitzer(void); + int pitzer_clean_up(void); + int pitzer_init(void); + int pitzer_tidy(void); + int read_pitzer(void); + int set_pz(int initial); + int calc_pitz_param(struct pitz_param *pz_ptr, LDBLE TK, LDBLE TR); + int check_gammas_pz(void); +#ifdef SKIP + LDBLE DC(LDBLE T); + int DW(LDBLE T); +#endif + int ISPEC(const char *name); + LDBLE G(LDBLE Y); + LDBLE GP(LDBLE Y); + int ETHETAS(LDBLE ZJ, LDBLE ZK, LDBLE I, LDBLE * etheta, + LDBLE * ethetap); + void ETHETA_PARAMS(LDBLE X, LDBLE& JAY, LDBLE& JPRIME ); + //int BDK(LDBLE X); + int pitzer_initial_guesses(void); + int pitzer_revise_guesses(void); + int PTEMP(LDBLE TK); + //LDBLE JAY(LDBLE X); + //LDBLE JPRIME(LDBLE Y); + int jacobian_pz(void); + + // pitzer_structures.cpp ------------------------------- + struct pitz_param *pitz_param_alloc(void); + int pitz_param_init(struct pitz_param *pitz_param_ptr); + struct pitz_param *pitz_param_duplicate(struct pitz_param *old_ptr); + int pitz_param_copy(struct pitz_param *old_ptr, + struct pitz_param *new_ptr); + + // prep.cpp ------------------------------- + int add_potential_factor(void); + int add_cd_music_factors(int n); + int add_surface_charge_balance(void); + int add_cd_music_charge_balances(int i); + int build_gas_phase(void); + int build_fixed_volume_gas(void); + int build_jacobian_sums(int k); + int build_mb_sums(void); + int build_min_exch(void); + int build_model(void); + int build_pure_phases(void); + int build_ss_assemblage(void); + int build_solution_phase_boundaries(void); + int build_species_list(int n); + int build_min_surface(void); + LDBLE calc_lk_phase(phase * p_ptr, LDBLE TK, LDBLE pa); + LDBLE calc_delta_v(reaction * r_ptr, bool phase); + LDBLE calc_PR(std::vector phase_ptrs, LDBLE P, LDBLE TK, LDBLE V_m); + LDBLE calc_PR(); + int calc_vm(LDBLE tc, LDBLE pa); + int change_hydrogen_in_elt_list(LDBLE charge); + int clear(void); + //int convert_units(struct solution *solution_ptr); + int convert_units(cxxSolution *solution_ptr); + LDBLE f_Vm(LDBLE v1); + struct unknown *find_surface_charge_unknown(std::string &str_ptr, int plane); + struct master **get_list_master_ptrs(char *ptr, + struct master *master_ptr); + int inout(void); + int is_special(struct species *spec); + int mb_for_species_aq(int n); + int mb_for_species_ex(int n); + int mb_for_species_surf(int n); + int quick_setup(void); + int resetup_master(void); + int save_model(void); + int setup_exchange(void); + int setup_gas_phase(void); + int setup_fixed_volume_gas(void); + int setup_master_rxn(struct master **master_ptr_list, + const std::string &pe_rxn); + int setup_pure_phases(void); + int adjust_setup_pure_phases(void); + int setup_related_surface(void); + int setup_ss_assemblage(void); + int setup_solution(void); + int adjust_setup_solution(void); + int setup_surface(void); + int setup_unknowns(void); + int store_dn(int k, LDBLE * source, int row, LDBLE coef_in, + LDBLE * gamma_source); + int store_jacob(LDBLE * source, LDBLE * target, LDBLE coef); + int store_jacob0(int row, int column, LDBLE coef); + int store_mb(LDBLE * source, LDBLE * target, LDBLE coef); + int store_mb_unknowns(struct unknown *unknown_ptr, LDBLE * LDBLE_ptr, + LDBLE coef, LDBLE * gamma_ptr); + int store_sum_deltas(LDBLE * source, LDBLE * target, LDBLE coef); + int tidy_redox(void); + struct master **unknown_alloc_master(void); + int write_mb_eqn_x(void); + int write_mb_for_species_list(int n); + int write_mass_action_eqn_x(int stop); + + int check_same_model(void); + int k_temp(LDBLE tc, LDBLE pa); + LDBLE k_calc(LDBLE * logk, LDBLE tempk, LDBLE presPa); + int prep(void); + int reprep(void); + int rewrite_master_to_secondary(struct master *master_ptr1, + struct master *master_ptr2); + int switch_bases(void); + int write_phase_sys_total(int n); + + // print.cpp ------------------------------- + char *sformatf(const char *format, ...); + int array_print(LDBLE * array_l, int row_count, int column_count, + int max_column_count); + int set_pr_in_false(void); + int print_all(void); + int print_exchange(void); + int print_gas_phase(void); + int print_master_reactions(void); + int print_reaction(struct reaction *rxn_ptr); + int print_species(void); + int print_surface(void); + int print_user_print(void); + int punch_all(void); + int print_alkalinity(void); + int print_diffuse_layer(cxxSurfaceCharge *surface_charge_ptr); + int print_eh(void); + int print_reaction(void); + int print_kinetics(void); + int print_mix(void); + int print_pp_assemblage(void); + int print_ss_assemblage(void); + int print_saturation_indices(void); + int print_surface_cd_music(void); + int print_totals(void); + int print_using(void); + /*int print_user_print(void);*/ + int punch_gas_phase(void); + int punch_identifiers(void); + int punch_kinetics(void); + int punch_molalities(void); + int punch_activities(void); + int punch_pp_assemblage(void); + int punch_ss_assemblage(void); + int punch_saturation_indices(void); + int punch_totals(void); + int punch_user_punch(void); +#if defined MULTICHART + int punch_user_graph(void); +#endif + + // read.cpp ------------------------------- + int read_input(void); + int read_conc(cxxSolution *solution_ptr, int count_mass_balance, char *str); + int *read_list_ints_range(char **ptr, int *count_ints, int positive, + int *int_list); + int read_log_k_only(char *ptr, LDBLE * log_k); + int read_t_c_only(char *ptr, LDBLE *t_c); + int read_p_c_only(char *ptr, LDBLE * p_c); + int read_omega_only(char *ptr, LDBLE *omega); + int read_number_description(char *ptr, int *n_user, int *n_user_end, + char **description, int allow_negative=FALSE); + int check_key(const char *str); + int check_units(std::string &tot_units, bool alkalinity, bool check_compatibility, + const char *default_units, bool print); + int find_option(const char *item, int *n, const char **list, int count_list, + int exact); + int get_option(const char **opt_list, int count_opt_list, char **next_char); + int get_true_false(char *string, int default_value); + + int add_psi_master_species(char *token); + int read_advection(void); + int read_analytical_expression_only(char *ptr, LDBLE * log_k); + /* VP: Density Start */ + int read_millero_abcdef (char *ptr, LDBLE * abcdef); + /* VP: Density End */ + int read_viscosity_parms(char *ptr, LDBLE * Jones_Dole); + int read_copy(void); + int read_debug(void); + int read_delta_h_only(char *ptr, LDBLE * delta_h, + DELTA_H_UNIT * units); + int read_aq_species_vm_parms(char *ptr, LDBLE * delta_v); + int read_vm_only(char *ptr, LDBLE * delta_v, + DELTA_V_UNIT * units); + int read_phase_vm(char *ptr, LDBLE * delta_v, + DELTA_V_UNIT * units); + int read_llnl_aqueous_model_parameters(void); + int read_exchange(void); + int read_exchange_master_species(void); + int read_exchange_species(void); + int read_gas_phase(void); + int read_incremental_reactions(void); + int read_inverse(void); + int read_inv_balances(struct inverse *inverse_ptr, char *next_char); + int read_inv_isotopes(struct inverse *inverse_ptr, char *ptr); + int read_inv_phases(struct inverse *inverse_ptr, char *next_char); + int read_kinetics(void); + int read_line_doubles(char *next_char, LDBLE ** d, int *count_d, + int *count_alloc); + int read_lines_doubles(char *next_char, LDBLE ** d, int *count_d, + int *count_alloc, const char **opt_list, + int count_opt_list, int *opt); + LDBLE *read_list_doubles(char **ptr, int *count_doubles); + int *read_list_ints(char **ptr, int *count_ints, int positive); + int *read_list_t_f(char **ptr, int *count_ints); + int read_master_species(void); + int read_mix(void); + int read_entity_mix(std::map &mix_map); + //int read_solution_mix(void); + int read_named_logk(void); + int read_phases(void); + int read_print(void); + int read_pp_assemblage(void); + int read_rates(void); + int read_reaction(void); + int read_reaction_reactants(cxxReaction *reaction_ptr); + int read_reaction_steps(cxxReaction *reaction_ptr); + int read_solid_solutions(void); + int read_temperature(void); + int read_reaction_temps(struct temperature *temperature_ptr); + int read_reaction_pressure(void); + int read_reaction_pressure_raw(void); + int read_save(void); + int read_selected_output(void); + int read_solution(void); + int read_species(void); + int read_surface(void); + int read_surface_master_species(void); + int read_surface_species(void); + int read_use(void); + int read_title(void); + int read_user_print(void); + int read_user_punch(void); +#if defined PHREEQ98 + int read_user_graph(void); +#endif +#if defined MULTICHART + int read_user_graph_handler(); +#endif + int next_keyword_or_option(const char **opt_list, int count_opt_list); + int cleanup_after_parser(CParser &parser); + + // ReadClass.cxx + int read_dump(void); + int read_delete(void); + int read_run_cells(void); + int streamify_to_next_keyword(std::istringstream & lines); + int dump_entities(void); + int delete_entities(void); + int run_as_cells(void); + void dump_ostream(std::ostream& os); + + // readtr.cpp ------------------------------- + int read_transport(void); + int dump(void); + int dump_exchange(int k); + int dump_gas_phase(int k); + int dump_kinetics(int k); + int dump_mix(int k); + int dump_pp_assemblage(int k); + int dump_reaction(int k); + int dump_ss_assemblage(int k); + int dump_solution(int k); + int dump_surface(int k); + int dump_cpp(void); + int read_line_LDBLEs(char *next_char, LDBLE ** d, int *count_d, + int *count_alloc); + + // sit.cpp ------------------------------- + int gammas_sit(void); + int model_sit(void); + int sit(void); + int sit_clean_up(void); + int sit_init(void); + int sit_tidy(void); + int read_sit(void); + int set_sit(int initial); + int calc_sit_param(struct pitz_param *pz_ptr, LDBLE TK, LDBLE TR); + int check_gammas_sit(void); + int sit_ISPEC(const char *name); + /*int DH_AB (LDBLE TK, LDBLE *A, LDBLE *B);*/ + int sit_initial_guesses(void); + int sit_revise_guesses(void); + int PTEMP_SIT(LDBLE tk); + void sit_make_lists(void); + int jacobian_sit(void); + + // spread.cpp ------------------------------- + int read_solution_spread(void); + int copy_token_tab(char *token_ptr, char **ptr, int *length); + int get_option_string(const char **opt_list, int count_opt_list, + char **next_char); + int spread_row_free(struct spread_row *spread_row_ptr); + int spread_row_to_solution(struct spread_row *heading, + struct spread_row *units, + struct spread_row *data, + struct defaults defaults); + struct spread_row *string_to_spread_row(char *string); +#ifdef PHREEQCI_GUI + void add_row(struct spread_row *spread_row_ptr); + void copy_defaults(struct defaults *dest_ptr, + struct defaults *src_ptr); + void free_spread(void); + struct spread_row *copy_row(struct spread_row *spread_row_ptr); +#endif + + // step.cpp ------------------------------- + int step(LDBLE step_fraction); + int xsolution_zero(void); + int add_exchange(cxxExchange *exchange_ptr); + int add_gas_phase(cxxGasPhase *gas_phase_ptr); + int add_kinetics(cxxKinetics *kinetics_ptr); + int add_mix(cxxMix * mix_ptr); + int add_pp_assemblage(cxxPPassemblage *pp_assemblage_ptr); + int add_reaction(cxxReaction *reaction_ptr, int step_number, LDBLE step_fraction); + int add_ss_assemblage(cxxSSassemblage *ss_assemblage_ptr); + int add_solution(cxxSolution *solution_ptr, LDBLE extensive, + LDBLE intensive); + int add_surface(cxxSurface *surface_ptr); + int check_pp_assemblage(cxxPPassemblage *pp_assemblage_ptr); + int gas_phase_check(cxxGasPhase *gas_phase_ptr); + int pp_assemblage_check(cxxPPassemblage *pp_assemblage_ptr); + int reaction_calc(cxxReaction *reaction_ptr); + int solution_check(void); + int ss_assemblage_check(cxxSSassemblage *ss_assemblage_ptr); + + // structures.cpp ------------------------------- + int clean_up(void); + int reinitialize(void); + int copier_add(struct copier *copier_ptr, int n_user, int start, int end); + int copier_free(struct copier *copier_ptr); + int copier_init(struct copier *copier_ptr); + static int element_compare(const void *ptr1, const void *ptr2); +public: + struct element *element_store(const char *element); + int elt_list_combine(void); + static int elt_list_compare(const void *ptr1, const void *ptr2); +protected: + struct elt_list *elt_list_dup(struct elt_list *elt_list_ptr_old); + int elt_list_print(struct elt_list *elt_list_ptr); + struct elt_list *elt_list_save(void); + cxxNameDouble elt_list_NameDouble(void); + struct elt_list * NameDouble2elt_list(const cxxNameDouble &nd); +public: + enum entity_type get_entity_enum(char *name); + struct inverse *inverse_alloc(void); + int inverse_delete(int i); + static int inverse_isotope_compare(const void *ptr1, const void *ptr2); + struct inverse *inverse_search(int n_user, int *n); + int inverse_sort(void); +protected: + struct logk *logk_alloc(void); + int logk_copy2orig(struct logk *logk_ptr); + struct logk *logk_store(char *name, int replace_if_found); + struct logk *logk_search(const char *name); + struct master *master_alloc(void); + static int master_compare(const void *ptr1, const void *ptr2); + int master_delete(char *ptr); +public: + struct master *master_bsearch(const char *ptr); + struct master *master_bsearch_primary(const char *ptr); + struct master *master_bsearch_secondary(char *ptr); + struct master *master_search(char *ptr, int *n); + struct pe_data *pe_data_alloc(void); +public: + struct pe_data *pe_data_dup(struct pe_data *pe_ptr_old); + struct pe_data *pe_data_free(struct pe_data *pe_data_ptr); +protected: + int pe_data_store(struct pe_data **pe, const char *token); +public: + struct phase *phase_bsearch(const char *ptr, int *j, int print); +protected: + static int phase_compare(const void *ptr1, const void *ptr2); + int phase_delete(int i); + struct phase *phase_store(const char *name); +public: + struct rate *rate_bsearch(char *ptr, int *j); + int rate_free(struct rate *rate_ptr); + struct rate *rate_search(const char *name, int *n); + int rate_sort(void); + struct reaction *rxn_alloc(int ntokens); + struct reaction *rxn_dup(struct reaction *rxn_ptr_old); + struct reaction * cxxChemRxn2rxn(cxxChemRxn &cr); + LDBLE rxn_find_coef(struct reaction *r_ptr, const char *str); + int rxn_free(struct reaction *rxn_ptr); + int rxn_print(struct reaction *rxn_ptr); + static int s_compare(const void *ptr1, const void *ptr2); + int s_delete(int i); + struct species *s_search(const char *name); + struct species *s_store(const char *name, LDBLE z, int replace_if_found); +protected: + struct save_values *save_values_bsearch(struct save_values *k, int *n); + static int save_values_compare(const void *ptr1, const void *ptr2); + int save_values_sort(void); + int save_values_store(struct save_values *s_v); + static int isotope_compare(const void *ptr1, const void *ptr2); + static int species_list_compare_alk(const void *ptr1, const void *ptr2); + static int species_list_compare_master(const void *ptr1, const void *ptr2); + int species_list_sort(void); + struct Change_Surf *change_surf_alloc(int count); +public: + struct master *surface_get_psi_master(const char *name, int plane); + int system_duplicate(int i, int save_old); + int trxn_add(struct reaction *r_ptr, LDBLE coef, int combine); + int trxn_add(cxxChemRxn &r_ptr, LDBLE coef, int combine); + int trxn_add_phase(struct reaction *r_ptr, LDBLE coef, int combine); + int trxn_combine(void); + int trxn_copy(struct reaction *rxn_ptr); + LDBLE trxn_find_coef(const char *str, int start); + int trxn_print(void); + int trxn_reverse_k(void); + int trxn_sort(void); + int trxn_swap(const char *token); + struct unknown *unknown_alloc(void); + int unknown_delete(int i); + int unknown_free(struct unknown *unknown_ptr); + int entity_exists(const char *name, int n_user); + static int inverse_compare(const void *ptr1, const void *ptr2); + int inverse_free(struct inverse *inverse_ptr); + static int kinetics_compare_int(const void *ptr1, const void *ptr2); + int logk_init(struct logk *logk_ptr); + static int master_compare_string(const void *ptr1, const void *ptr2); + int master_free(struct master *master_ptr); + struct phase *phase_alloc(void); + static int phase_compare_string(const void *ptr1, const void *ptr2); + int phase_free(struct phase *phase_ptr); + int phase_init(struct phase *phase_ptr); + static int rate_compare(const void *ptr1, const void *ptr2); + static int rate_compare_string(const void *ptr1, const void *ptr2); + struct species *s_alloc(void); + int s_free(struct species *s_ptr); + int s_init(struct species *s_ptr); + static int ss_assemblage_compare_int(const void *ptr1, const void *ptr2); + static int solution_compare(const void *ptr1, const void *ptr2); + static int solution_compare_int(const void *ptr1, const void *ptr2); + static int species_list_compare(const void *ptr1, const void *ptr2); + static int surface_compare_int(const void *ptr1, const void *ptr2); + static int rxn_token_temp_compare(const void *ptr1, const void *ptr2); + int trxn_multiply(LDBLE coef); + + struct elt_list * cxxNameDouble2elt_list(const cxxNameDouble * nd); + struct name_coef * cxxNameDouble2name_coef(const cxxNameDouble * nd); + struct master_activity * cxxNameDouble2master_activity(const cxxNameDouble * nd); + struct master * cxxNameDouble2surface_master(const cxxNameDouble * totals); + + void Use2cxxStorageBin(cxxStorageBin & sb); + void phreeqc2cxxStorageBin(cxxStorageBin & sb); + void phreeqc2cxxStorageBin(cxxStorageBin & sb, int n); + void cxxStorageBin2phreeqc(cxxStorageBin & sb, int n); + void cxxStorageBin2phreeqc(cxxStorageBin & sb); + + /* tally.cpp */ + void add_all_components_tally(void); + int build_tally_table(void); + int calc_dummy_kinetic_reaction_tally(cxxKinetics *kinetics_ptr); + int diff_tally_table(void); + int extend_tally_table(void); + int free_tally_table(void); + int fill_tally_table(int *n_user, int index_conservative, int n_buffer); + int get_tally_table_rows_columns(int *rows, int *columns); + int get_tally_table_column_heading(int column, int *type, char *string); + int get_tally_table_row_heading(int column, char *string); + int store_tally_table(LDBLE * array, int row_dim, int col_dim, + LDBLE fill_factor); + int zero_tally_table(void); + int elt_list_to_tally_table(struct tally_buffer *buffer_ptr); + int master_to_tally_table(struct tally_buffer *buffer_ptr); + int get_all_components(void); + int print_tally_table(void); + int set_reaction_moles(int n_user, LDBLE moles); + int set_reaction_temperature(int n_user, LDBLE tc); + int set_kinetics_time(int n_user, LDBLE step); + + // tidy.cpp ------------------------------- + int add_other_logk(LDBLE * source_k, int count_add_logk, + struct name_coef *add_logk); + int add_logks(struct logk *logk_ptr, int repeats); + LDBLE halve(LDBLE f(LDBLE x, void *), LDBLE x0, LDBLE x1, LDBLE tol); + int replace_solids_gases(void); + int ss_prep(LDBLE t, cxxSS *ss_ptr, int print); + int select_log_k_expression(LDBLE * source_k, LDBLE * target_k); + int slnq(int n, LDBLE * a, LDBLE * delta, int ncols, int print); +public: + int tidy_punch(void); + int tidy_model(void); + int check_species_input(void); + LDBLE coef_in_master(struct master *master_ptr); + int phase_rxn_to_trxn(struct phase *phase_ptr, + struct reaction *rxn_ptr); + int reset_last_model(void); + int rewrite_eqn_to_primary(void); + int rewrite_eqn_to_secondary(void); + int species_rxn_to_trxn(struct species *s_ptr); + int tidy_logk(void); + int tidy_exchange(void); + int tidy_min_exchange(void); + int tidy_kin_exchange(void); + int tidy_gas_phase(void); + int tidy_inverse(void); + int tidy_isotopes(void); + int tidy_isotope_ratios(void); + int tidy_isotope_alphas(void); + int tidy_kin_surface(void); + int tidy_master_isotope(void); + int tidy_min_surface(void); + int tidy_phases(void); + int tidy_pp_assemblage(void); + int tidy_solutions(void); + int tidy_ss_assemblage(void); + int tidy_species(void); + int tidy_surface(void); + int scan(LDBLE f(LDBLE x, void *), LDBLE * xx0, LDBLE * xx1); + static LDBLE f_spinodal(LDBLE x, void *); + int solve_misc(LDBLE * xxc1, LDBLE * xxc2, LDBLE tol); + int ss_calc_a0_a1(cxxSS *ss_ptr); + + // transport.cpp ------------------------------- + int transport(void); + void print_punch(int i, boolean active); + int set_initial_moles(int i); + cxxSurface sum_surface_comp(cxxSurface *source1, LDBLE f1, + cxxSurface *source2, std::string charge_name, LDBLE f2, + LDBLE new_Dw); + int reformat_surf(const char *comp_name, LDBLE fraction, const char *new_comp_name, + LDBLE new_Dw, int cell); + LDBLE viscosity(void); + LDBLE calc_vm_Cl(void); + int multi_D(LDBLE DDt, int mobile_cell, int stagnant); + int find_J(int icell, int jcell, LDBLE mixf, LDBLE DDt, int stagnant); + int fill_spec(int cell_no); + void define_ct_structures(void); + int fill_m_s(struct J_ij *J_ij, int J_ij_count_spec); + static int sort_species_name(const void *ptr1, const void *ptr2); + int disp_surf(LDBLE stagkin_time); + int diff_stag_surf(int mobile_cell); + int check_surfaces(cxxSurface *surface_ptr1, cxxSurface *surface_ptr2); + cxxSurface mobile_surface_copy(cxxSurface *surface_old_ptr, + int n_user_new, + bool move_old); + void transport_cleanup(void); + int init_mix(void); + int init_heat_mix(int nmix); + int heat_mix(int heat_nmix); + int mix_stag(int i, LDBLE stagkin_time, int punch, + LDBLE step_fraction_kin); + + // utilities.cpp ------------------------------- +public: + int add_elt_list(struct elt_list *elt_list_ptr, LDBLE coef); + int add_elt_list_multi_surf(struct elt_list *elt_list_ptr, LDBLE coef, struct element *surf_elt_ptr); + int add_elt_list(const cxxNameDouble & nd, LDBLE coef); +protected: + int backspace_screen(int spaces); + LDBLE calc_alk(struct reaction *rxn_ptr); +public: + LDBLE calc_rho_0(LDBLE tc, LDBLE pa); + LDBLE calc_dielectrics(LDBLE tc, LDBLE pa); + int compute_gfw(const char *string, LDBLE * gfw); +#if defined PHREEQ98 + int copy_title(char *token_ptr, char **ptr, int *length); +#endif + int copy_token(char *token_ptr, char **ptr, int *length); + int copy_token(std::string &token, char **ptr); + int dup_print(const char *ptr, int emphasis); + int equal(LDBLE a, LDBLE b, LDBLE eps); +public: + void *free_check_null(void *ptr); +protected: + void free_hash_strings(HashTable * Table); + int get_token(char **eqnaddr, char *string, LDBLE * z, int *l); + int hcreate_multi(unsigned Count, HashTable ** HashTable_ptr); + void hdestroy_multi(HashTable * HashTable_ptr); + ENTRY *hsearch_multi(HashTable * Table, ENTRY item, ACTION action); + int islegit(const char c); +public: + void malloc_error(void); +protected: + int parse_couple(char *token); + int print_centered(const char *string); +public: + static int replace(const char *str1, const char *str2, char *str); + static bool replace(const char *str1, const char *str2, std::string & str); + static int strcmp_nocase(const char *str1, const char *str2); + static int strcmp_nocase_arg1(const char *str1, const char *str2); +protected: + void space(void **ptr, int i, int *max, int struct_size); + void squeeze_white(char *s_l); + int status(int count, const char *str, bool kinetics = false); + void str_tolower(char *str); + void str_toupper(char *str); +public: +#if !defined(NDEBUG) && defined(WIN32_MEMORY_DEBUG) + char *_string_duplicate(const char *token, const char *szFileName, int nLine); +#else + char *string_duplicate(const char *token); +#endif + const char *string_hsave(const char *str); + void strings_map_clear(); +#ifdef HASH + void strings_hash_clear(); +#endif +protected: + char *string_pad(const char *str, int i); + int string_trim(char *str); + int string_trim_right(char *str); + int string_trim_left(char *str); + static LDBLE under(LDBLE xval); + void zero_double(LDBLE * target, int n); + int get_input_errors(void); +#ifdef PHREEQ98 + void AddToCEntry(char *a, int l, int i); + void ApplicationProcessMessages(void); + int copy_title(char *token_ptr, char **ptr, int *length); + extern int clean_up_null(void); +#endif + int isamong(char c, const char *s_l); + Address Hash_multi(HashTable * Table, const char *Key); + void ExpandTable_multi(HashTable * Table); +public: + int main_method(int argc, char *argv[]); + void set_phast(int); + int next_user_number(Keywords::KEYWORDS key); + size_t list_components(std::list &list_c); + PHRQ_io * Get_phrq_io(void) {return this->phrq_io;} + void Set_run_cells_one_step(const bool tf) {this->run_cells_one_step = tf;} + + + std::map & Get_Rxn_solution_map() {return this->Rxn_solution_map;} + std::map & Get_Rxn_exchange_map() {return this->Rxn_exchange_map;} + std::map & Get_Rxn_gas_phase_map() {return this->Rxn_gas_phase_map;} + std::map & Get_Rxn_kinetics_map() {return this->Rxn_kinetics_map;} + std::map & Get_Rxn_pp_assemblage_map() {return this->Rxn_pp_assemblage_map;} + std::map & Get_Rxn_ss_assemblage_map() {return this->Rxn_ss_assemblage_map;} + std::map & Get_Rxn_surface_map() {return this->Rxn_surface_map;} + std::map & Get_Rxn_temperature_map() {return this->Rxn_temperature_map;} + std::map & Get_Rxn_pressure_map() {return this->Rxn_pressure_map;} + + +protected: + void init(void); + + // + //Data members + // +protected: + PHRQ_io *phrq_io; + PHRQ_io ioInstance; + int same_model; + + LDBLE current_tc; + LDBLE current_pa; + LDBLE current_mu; + bool mu_terms_in_logk; + + /* ---------------------------------------------------------------------- + * STRUCTURES + * ---------------------------------------------------------------------- */ + + struct model last_model; + //struct punch punch; + bool high_precision; + + /* ---------------------------------------------------------------------- + * Temperatures + * ---------------------------------------------------------------------- */ + + std::map Rxn_temperature_map; + + /* ---------------------------------------------------------------------- + * Pressures + * ---------------------------------------------------------------------- */ + std::map Rxn_pressure_map; + + /* ---------------------------------------------------------------------- + * Surface + * --------------------------------------------------------------------- */ + + int g_iterations; + LDBLE G_TOL; + std::map Rxn_surface_map; + std::map charge_group_map; + int change_surf_count; + struct Change_Surf *change_surf; + + /* ---------------------------------------------------------------------- + * Exchange + * ---------------------------------------------------------------------- */ + std::map Rxn_exchange_map; + + /* ---------------------------------------------------------------------- + * Kinetics + * ---------------------------------------------------------------------- */ + std::map Rxn_kinetics_map; + bool use_kinetics_limiter; + + /*---------------------------------------------------------------------- + * Save + *---------------------------------------------------------------------- */ + int count_save_values; + struct save_values *save_values; + struct save save; + + /*---------------------------------------------------------------------- + * Use + *---------------------------------------------------------------------- */ + cxxUse use; + + /*---------------------------------------------------------------------- + * Copy + *---------------------------------------------------------------------- */ + struct copier copy_solution; + struct copier copy_pp_assemblage; + struct copier copy_exchange; + struct copier copy_surface; + struct copier copy_ss_assemblage; + struct copier copy_gas_phase; + struct copier copy_kinetics; + struct copier copy_mix; + struct copier copy_reaction; + struct copier copy_temperature; + struct copier copy_pressure; + + /*---------------------------------------------------------------------- + * Inverse + *---------------------------------------------------------------------- */ + + struct inverse *inverse; + int count_inverse; + + /*---------------------------------------------------------------------- + * Mix + *---------------------------------------------------------------------- */ + std::map Rxn_mix_map; + std::map Dispersion_mix_map; + std::map Rxn_solution_mix_map; + std::map Rxn_exchange_mix_map; + std::map Rxn_gas_phase_mix_map; + std::map Rxn_kinetics_mix_map; + std::map Rxn_pp_assemblage_mix_map; + std::map Rxn_ss_assemblage_mix_map; + std::map Rxn_surface_mix_map; + /* + * List new definitions + */ + std::set Rxn_new_exchange; + std::set Rxn_new_gas_phase; + std::set Rxn_new_kinetics; // not used + std::set Rxn_new_mix; // not used + std::set Rxn_new_pp_assemblage; + std::set Rxn_new_pressure; // not used + std::set Rxn_new_reaction; // not used + std::set Rxn_new_solution; + std::set Rxn_new_ss_assemblage; + std::set Rxn_new_surface; + std::set Rxn_new_temperature; // not used + /*---------------------------------------------------------------------- + * Irreversible reaction + *---------------------------------------------------------------------- */ + std::map Rxn_reaction_map; + + /*---------------------------------------------------------------------- + * Gas phase + *---------------------------------------------------------------------- */ + std::map Rxn_gas_phase_map; + + /*---------------------------------------------------------------------- + * Solid solution + *---------------------------------------------------------------------- */ + std::map Rxn_ss_assemblage_map; + + /*---------------------------------------------------------------------- + * Pure-phase assemblage + *---------------------------------------------------------------------- */ + std::map Rxn_pp_assemblage_map; + + /*---------------------------------------------------------------------- + * Species_list + *---------------------------------------------------------------------- */ + int count_species_list; + int max_species_list; + struct species_list *species_list; + + /*---------------------------------------------------------------------- + * Jacobian and Mass balance lists + *---------------------------------------------------------------------- */ + + int count_sum_jacob0; /* number of elements in sum_jacob0 */ + int max_sum_jacob0; /* calculated maximum number of elements in sum_jacob0 */ + struct list0 *sum_jacob0; /* array of pointers to targets and coefficients for array */ + + int count_sum_mb1; /* number of elements in sum_mb1 */ + int max_sum_mb1; /* calculated maximum number of elements in sum_mb1 */ + struct list1 *sum_mb1; /* array of pointers to sources and targets for mass + balance summations with coef = 1.0 */ + int count_sum_jacob1; /* number of elements in sum_jacob1 */ + int max_sum_jacob1; /* calculated maximum number of elements in sum_jacob1 */ + struct list1 *sum_jacob1; /* array of pointers to sources and targets for array + equations with coef = 1.0 */ + int count_sum_mb2; /* number of elements in sum_mb2 */ + int max_sum_mb2; /* calculated maximum number of elements in sum_mb2 */ + struct list2 *sum_mb2; /* array of coefficients and pointers to sources and + targets for mass balance summations with coef != 1.0 */ + int count_sum_jacob2; /* number of elements in sum_jacob2 */ + int max_sum_jacob2; /* calculated maximum number of elements in sum_jacob2 */ + struct list2 *sum_jacob2; /* array of coefficients and pointers to sources and + targets, coef != 1.0 */ + int count_sum_delta; /* number of elements in sum_delta */ + int max_sum_delta; /* calculated maximum number of elements in sum_delta */ + struct list2 *sum_delta; /* array of pointers to sources, targets and coefficients for + summing deltas for mass balance equations */ + /*---------------------------------------------------------------------- + * Solution + *---------------------------------------------------------------------- */ + std::map Rxn_solution_map; + std::vector unnumbered_solutions; + bool save_species; + + /*---------------------------------------------------------------------- + * Global solution + *---------------------------------------------------------------------- */ + char *title_x; + std::string last_title_x; + int new_x; + char *description_x; + LDBLE tc_x; + LDBLE tk_x; + LDBLE patm_x; + LDBLE last_patm_x; + LDBLE potV_x; + bool numerical_fixed_volume; + bool force_numerical_fixed_volume; + //bool switch_numerical; + LDBLE ph_x; + LDBLE solution_pe_x; + LDBLE mu_x; + LDBLE ah2o_x; + LDBLE density_x; + LDBLE total_h_x; + LDBLE total_o_x; + LDBLE cb_x; + LDBLE total_ions_x; + LDBLE mass_water_aq_x; + LDBLE mass_water_surfaces_x; + LDBLE mass_water_bulk_x; + char *units_x; + std::map < std::string, cxxChemRxn > pe_x; + std::map isotopes_x; + std::string default_pe_x; + cxxSurface::DIFFUSE_LAYER_TYPE dl_type_x; + LDBLE total_carbon; + LDBLE total_co2; + LDBLE total_alkalinity; + LDBLE gfw_water; + LDBLE step_x; + LDBLE kin_time_x; + + /*---------------------------------------------------------------------- + * Transport data + *---------------------------------------------------------------------- */ + int count_cells; + int cell_data_max_cells; + int count_shifts; + int ishift; + int bcon_first; + int bcon_last; + int correct_disp; + LDBLE tempr; + LDBLE timest; + int simul_tr; + LDBLE diffc; + LDBLE heat_diffc; + int cell; + LDBLE mcd_substeps; + struct stag_data *stag_data; + int print_modulus; + int punch_modulus; + int dump_in; + int dump_modulus; + int transport_warnings; + struct cell_data *cell_data; + int old_cells, max_cells, all_cells; + int multi_Dflag; /* signals calc'n of multicomponent diffusion */ + int interlayer_Dflag; /* multicomponent diffusion and diffusion through interlayer porosity */ + LDBLE default_Dw; /* default species diffusion coefficient in water at 25oC, m2/s */ + int correct_Dw; /* if true, Dw is adapted in calc_SC */ + LDBLE multi_Dpor; /* uniform porosity of free porewater in solid medium */ + LDBLE interlayer_Dpor; /* uniform porosity of interlayer space of montmorillonite in solid medium */ + LDBLE multi_Dpor_lim; /* limiting free porewater porosity where transport stops */ + LDBLE interlayer_Dpor_lim; /* limiting interlayer porosity where transport stops */ + LDBLE multi_Dn; /* exponent to calculate pore water diffusion coefficient, + Dp = Dw * (multi_Dpor)^multi_Dn */ + LDBLE interlayer_tortf; /* tortuosity_factor in interlayer porosity, + Dpil = Dw / interlayer_tortf */ + + int cell_no, mixrun; + /*---------------------------------------------------------------------- + * Advection data + *---------------------------------------------------------------------- */ + int count_ad_cells; + int count_ad_shifts; + int print_ad_modulus; + int punch_ad_modulus; + int *advection_punch, *advection_print; + LDBLE advection_kin_time; + LDBLE advection_kin_time_defined; + int advection_warnings; + + /*---------------------------------------------------------------------- + * Tidy data + *---------------------------------------------------------------------- */ + int new_model, new_exchange, new_pp_assemblage, new_surface, + new_reaction, new_temperature, new_mix, new_solution, new_gas_phase, + new_inverse, new_punch, new_ss_assemblage, new_kinetics, new_copy, + new_pitzer; + + /*---------------------------------------------------------------------- + * Elements + *---------------------------------------------------------------------- */ + + struct element **elements; + int count_elements; + int max_elements; + struct element *element_h_one; + + /*---------------------------------------------------------------------- + * Element List + *---------------------------------------------------------------------- */ + + struct elt_list *elt_list; /* structure array of working space while reading equations + names are in "strings", initially in input order */ + int count_elts; /* number of elements in elt_list = position of next */ + int max_elts; + /*---------------------------------------------------------------------- + * Reaction + *---------------------------------------------------------------------- */ + bool run_cells_one_step; + /*---------------------------------------------------------------------- + * Species + *---------------------------------------------------------------------- */ + + struct logk **logk; + int count_logk; + int max_logk; + + char *moles_per_kilogram_string; + char *pe_string; + + struct species **s; + int count_s; + int max_s; + std::vector< std::map < std::string, cxxSpeciesDL > > s_diff_layer; + + struct species **s_x; + int count_s_x; + int max_s_x; + + struct species *s_h2o; + struct species *s_hplus; + struct species *s_h3oplus; + struct species *s_eminus; + struct species *s_co3; + struct species *s_h2; + struct species *s_o2; + + /*---------------------------------------------------------------------- + * Phases + *---------------------------------------------------------------------- */ + struct phase **phases; + int count_phases; + int max_phases; + + /*---------------------------------------------------------------------- + * Master species + *---------------------------------------------------------------------- */ + struct master **master; /* structure array of master species */ + struct master **dbg_master; + int count_master; + int max_master; + + /*---------------------------------------------------------------------- + * Unknowns + *---------------------------------------------------------------------- */ + + struct unknown **x; + int count_unknowns; + int max_unknowns; + + struct unknown *ah2o_unknown; + struct unknown *alkalinity_unknown; + struct unknown *carbon_unknown; + struct unknown *charge_balance_unknown; + struct unknown *exchange_unknown; + struct unknown *mass_hydrogen_unknown; + struct unknown *mass_oxygen_unknown; + struct unknown *mb_unknown; + struct unknown *mu_unknown; + struct unknown *pe_unknown; + struct unknown *ph_unknown; + struct unknown *pure_phase_unknown; + struct unknown *solution_phase_boundary_unknown; + struct unknown *surface_unknown; + struct unknown *gas_unknown; + struct unknown *ss_unknown; + std::vector gas_unknowns; + + /*---------------------------------------------------------------------- + * Reaction work space + *---------------------------------------------------------------------- */ + struct reaction_temp trxn; /* structure array of working space while reading equations + species names are in "temp_strings" */ + int count_trxn; /* number of reactants in trxn = position of next */ + int max_trxn; + + struct unknown_list *mb_unknowns; + int count_mb_unknowns; + int max_mb_unknowns; + + /* ---------------------------------------------------------------------- + * Print + * ---------------------------------------------------------------------- */ + struct prints pr; + bool status_on; + clock_t status_interval; + clock_t status_timer; + std::string status_string; + int count_warnings; + + /* ---------------------------------------------------------------------- + * RATES + * ---------------------------------------------------------------------- */ + struct rate *rates; + int count_rates; + LDBLE rate_m, rate_m0, rate_time, rate_kin_time, rate_sim_time_start, + rate_sim_time_end, rate_sim_time, rate_moles, initial_total_time; + std::vector rate_p; + int count_rate_p; + + /* ---------------------------------------------------------------------- + * USER PRINT COMMANDS + * ---------------------------------------------------------------------- */ + struct rate *user_print; + //struct rate *user_punch; + //const char **user_punch_headings; + //int user_punch_count_headings; + int n_user_punch_index; + + int fpunchf_user_s_warning; + char fpunchf_user_buffer[80]; + +#if defined PHREEQ98 + struct rate *user_graph; + char **user_graph_headings; + int user_graph_count_headings; +#endif +#if defined MULTICHART + ChartHandler chart_handler; +public: + ChartHandler& Get_chart_handler(void) + { + return chart_handler; + } + const ChartHandler& Get_chart_handler(void)const + { + return chart_handler; + } +protected: +#endif + + /* ---------------------------------------------------------------------- + * GLOBAL DECLARATIONS + * ---------------------------------------------------------------------- */ + const char * error_string; + int simulation; + int state; + int reaction_step; + int transport_step; + int transport_start; + int advection_step; + int stop_program; + int incremental_reactions; + + double MIN_LM; + double LOG_ZERO_MOLALITY; + double MIN_TOTAL; + double MIN_TOTAL_SS; + double MIN_RELATED_SURFACE; + double MIN_RELATED_LOG_ACTIVITY; + + int count_strings; + int max_strings; + + LDBLE *array; + LDBLE *delta; + LDBLE *residual; + + int input_error; + + Keywords::KEYWORDS next_keyword; + int parse_error; + int paren_count; + int iterations; + int gamma_iterations; + int run_reactions_iterations; + + int max_line; + char *line; + char *line_save; + + LDBLE LOG_10; + + int debug_model; + int debug_prep; + int debug_set; + int debug_diffuse_layer; + int debug_inverse; + + LDBLE inv_tol_default; + int itmax; + int max_tries; + LDBLE ineq_tol; + LDBLE convergence_tolerance; + LDBLE step_size; + LDBLE pe_step_size; + LDBLE step_size_now; + LDBLE pe_step_size_now; + LDBLE pp_scale; + LDBLE pp_column_scale; + int diagonal_scale; /* 0 not used, 1 used */ + int mass_water_switch; + int delay_mass_water; + int equi_delay; + bool dampen_ah2o; + LDBLE censor; + int aqueous_only; + int negative_concentrations; + int calculating_deriv; + int numerical_deriv; + + int count_total_steps; + int phast; + LDBLE *llnl_temp, *llnl_adh, *llnl_bdh, *llnl_bdot, *llnl_co2_coefs; + int llnl_count_temp, llnl_count_adh, llnl_count_bdh, llnl_count_bdot, + llnl_count_co2_coefs; + + //char *selected_output_file_name; + std::map SelectedOutput_map; + SelectedOutput * current_selected_output; + + std::map UserPunch_map; + UserPunch * current_user_punch; + + char *dump_file_name; + int remove_unstable_phases; + std::string screen_string; +#ifdef PHREEQCI_GUI + struct spread_sheet g_spread_sheet; +#endif + int spread_length; + + /* ---------------------------------------------------------------------- */ + /* + * Hash definitions + */ + + std::map strings_map; +#ifdef HASH + std::hash_map strings_hash; +#endif + HashTable *elements_hash_table; + HashTable *species_hash_table; + HashTable *phases_hash_table; + HashTable *logk_hash_table; + HashTable *master_isotope_hash_table; + +#if defined(PHREEQCI_GUI) +#include "../../phreeqci_gui.h" +#endif /* defined(PHREEQCI_GUI) */ + /* ---------------------------------------------------------------------- + * ISOTOPES + * ---------------------------------------------------------------------- */ + //struct name_coef match_tokens[50]; + //int count_match_tokens; + int count_master_isotope; + struct master_isotope **master_isotope; + int max_master_isotope; + int initial_solution_isotopes; + int count_calculate_value; + struct calculate_value **calculate_value; + int max_calculate_value; + HashTable *calculate_value_hash_table; + int count_isotope_ratio; + struct isotope_ratio **isotope_ratio; + int max_isotope_ratio; + HashTable *isotope_ratio_hash_table; + int count_isotope_alpha; + struct isotope_alpha **isotope_alpha; + int max_isotope_alpha; + HashTable *isotope_alpha_hash_table; + int phreeqc_mpi_myself; + int first_read_input; + char *user_database; + + //int have_punch_name; + /* VP: Density Start */ + int print_density; + /* VP: Density End */ + + int print_viscosity; + LDBLE *zeros; + int zeros_max; + + LDBLE viscos, viscos_0, viscos_0_25; // viscosity of the solution, of pure water, of pure water at 25 C + LDBLE cell_pore_volume; + LDBLE cell_porosity; + LDBLE cell_volume; + LDBLE cell_saturation; + struct system_species *sys; + int count_sys, max_sys; + LDBLE sys_tot; + + LDBLE V_solutes, rho_0, rho_0_sat, kappa_0, p_sat/*, ah2o_x0*/; + LDBLE SC; // specific conductance mS/cm + LDBLE eps_r; // relative dielectric permittivity + LDBLE DH_A, DH_B, DH_Av; // Debye-Hueckel A, B and Av + LDBLE QBrn; // Born function d(ln(eps_r))/dP / eps_r * 41.84004, for supcrt calc'n of molal volume + LDBLE ZBrn; // Born function (-1/eps_r + 1) * 41.84004, for supcrt calc'n of molal volume + LDBLE dgdP; // dg / dP, pressure derivative of g-function, for supcrt calc'n of molal volume + + int need_temp_msg; + LDBLE solution_mass, solution_volume; + + /* phqalloc.cpp ------------------------------- */ + PHRQMemHeader *s_pTail; + + /* Basic */ + PBasic * basic_interpreter; + double (*basic_callback_ptr) (double x1, double x2, const char *str, void *cookie); + void *basic_callback_cookie; +#ifdef IPHREEQC_NO_FORTRAN_MODULE + double (*basic_fortran_callback_ptr) (double *x1, double *x2, char *str, size_t l); +#else + double (*basic_fortran_callback_ptr) (double *x1, double *x2, const char *str, int l); +#endif +#if defined(SWIG) || defined(SWIG_IPHREEQC) + class BasicCallback *basicCallback; + void SetCallback(BasicCallback *cb) { basicCallback = cb; } +#endif + + /* cl1.cpp ------------------------------- */ + LDBLE *x_arg, *res_arg, *scratch; + int x_arg_max, res_arg_max, scratch_max; +#ifdef SKIP + /* dw.cpp ------------------------------- */ + /* COMMON /QQQQ/ */ + LDBLE Q0, Q5; + LDBLE GASCON, TZ, AA; + LDBLE Z, DZ, Y; + LDBLE G1, G2, GF; + LDBLE B1, B2, B1T, B2T, B1TT, B2TT; +#endif + /* gases.cpp ------------------------------- */ + LDBLE a_aa_sum, b2, b_sum, R_TK; + + /* input.cpp ------------------------------- */ + int check_line_return; + int reading_db; + + /* integrate.cpp ------------------------------- */ + LDBLE midpoint_sv; + LDBLE z_global, xd_global, alpha_global; + + /* inverse.cpp ------------------------------- */ + int max_row_count, max_column_count; + int carbon; + const char **col_name, **row_name; + int count_rows, count_optimize; + int col_phases, col_redox, col_epsilon, col_ph, col_water, + col_isotopes, col_phase_isotopes; + int row_mb, row_fract, row_charge, row_carbon, row_isotopes, + row_epsilon, row_isotope_epsilon, row_water; + LDBLE *inv_zero, *array1, *inv_res, *inv_delta1, *delta2, *delta3, *inv_cu, + *delta_save; + LDBLE *min_delta, *max_delta; + int *inv_iu, *inv_is; + int klmd, nklmd, n2d, kode, iter; + LDBLE toler, error, max_pct, scaled_error; + struct master *master_alk; + int *row_back, *col_back; + unsigned long *good, *bad, *minimal; + int max_good, max_bad, max_minimal; + int count_good, count_bad, count_minimal, count_calls; + unsigned long soln_bits, phase_bits, current_bits, temp_bits; + FILE *netpath_file; + int count_inverse_models, count_pat_solutions; + int min_position[32], max_position[32], now[32]; + std::vector inverse_heading_names; + + /* kinetics.cpp ------------------------------- */ +public: + int count_pp, count_pg, count_ss; + void *cvode_kinetics_ptr; + int cvode_test; + int cvode_error; + int cvode_n_user; + int cvode_n_reactions; + realtype cvode_step_fraction; + realtype cvode_rate_sim_time; + realtype cvode_rate_sim_time_start; + realtype cvode_last_good_time; + realtype cvode_prev_good_time; + N_Vector cvode_last_good_y; + N_Vector cvode_prev_good_y; + M_Env kinetics_machEnv; + N_Vector kinetics_y, kinetics_abstol; + void *kinetics_cvode_mem; + cxxSSassemblage *cvode_ss_assemblage_save; + cxxPPassemblage *cvode_pp_assemblage_save; +protected: + LDBLE *m_original; + LDBLE *m_temp; + LDBLE *rk_moles; + int set_and_run_attempt; + LDBLE *x0_moles; + + /* model.cpp ------------------------------- */ + int gas_in; + LDBLE min_value; + LDBLE *normal, *ineq_array, *res, *cu, *zero, *delta1; + int *iu, *is, *back_eq; + int normal_max, ineq_array_max, res_max, cu_max, zero_max, + delta1_max, iu_max, is_max, back_eq_max; + + /* phrq_io_output.cpp ------------------------------- */ + int forward_output_to_log; + + /* phreeqc_files.cpp ------------------------------- */ + char *default_data_base; +#ifdef PHREEQ98 + int outputlinenr; + char *LogFileNameC; + char progress_str[512]; +#endif + + /* Pitzer */ + int pitzer_model, sit_model, pitzer_pe; + int full_pitzer, always_full_pitzer, ICON, IC; + LDBLE COSMOT; + LDBLE AW; + LDBLE VP, DW0; + struct pitz_param **pitz_params; + int count_pitz_param, max_pitz_param; + std::map< std::string, size_t > pitz_param_map; + struct theta_param **theta_params; + int count_theta_param, max_theta_param; + int use_etheta; + LDBLE OTEMP, OPRESS; + LDBLE A0; + struct pitz_param *aphi; + struct species **spec, **cations, **anions, **neutrals; + int count_cations, count_anions, count_neutrals; + int MAXCATIONS, FIRSTANION, MAXNEUTRAL; + struct pitz_param *mcb0, *mcb1, *mcc0; + int *IPRSNT; + LDBLE *M, *LGAMMA; + LDBLE BK[23], DK[23]; + +#ifdef PHREEQ98 + int connect_simulations, graph_initial_solutions; + int shifts_as_points; + int chart_type; + int ShowChart; + int RowOffset, ColumnOffset; +#endif + LDBLE dummy; + + /* print.cpp ------------------------------- */ +#ifdef PHREEQ98 + int colnr, rownr; + int graph_initial_solutions; + int prev_advection_step, prev_transport_step; /*, prev_reaction_step */ + /* int shifts_as_points; */ + int chart_type; + int AddSeries; + int FirstCallToUSER_GRAPH; +#endif + + /* read.cpp */ + char *prev_next_char; +#if defined PHREEQ98 + int shifts_as_points; +#endif + + /* read_class.cxx */ + dumper dump_info; + StorageBinList delete_info; + runner run_info; + char * sformatf_buffer; + size_t sformatf_buffer_size; + + /* readtr.cpp */ + std::string dump_file_name_cpp; + + /* sit.cpp ------------------------------- */ + struct pitz_param **sit_params; + int count_sit_param, max_sit_param; + std::map< std::string, size_t > sit_param_map; + LDBLE sit_A0; + int sit_count_cations, sit_count_anions, sit_count_neutrals; + int sit_MAXCATIONS, sit_FIRSTANION, sit_MAXNEUTRAL; + int *sit_IPRSNT; + LDBLE *sit_M, *sit_LGAMMA; + std::vector s_list, cation_list, neutral_list, anion_list, ion_list, param_list; + + /* tidy.cpp ------------------------------- */ + LDBLE a0, a1, kc, kb; + + /* tally.cpp ------------------------------- */ + struct tally_buffer *t_buffer; + int tally_count_component; + struct tally *tally_table; + int count_tally_table_columns; + int count_tally_table_rows; + + /* transport.cpp ------------------------------- */ + struct sol_D *sol_D; + struct sol_D *sol_D_dbg; + struct J_ij *J_ij, *J_ij_il; + int J_ij_count_spec; + + struct M_S *m_s; + int count_m_s; + LDBLE tot1_h, tot1_o, tot2_h, tot2_o; + LDBLE diffc_max, diffc_tr, J_ij_sum; + int transp_surf; + LDBLE *heat_mix_array; + LDBLE *temp1, *temp2; + int nmix, heat_nmix; + LDBLE heat_mix_f_imm, heat_mix_f_m; + int warn_MCD_X, warn_fixed_Surf; + LDBLE current_x, current_A, fix_current; // current: coulomb / s, Ampere, fixed current (Ampere) + +#ifdef PHREEQ98 + int AutoLoadOutputFile, CreateToC; + int ProcessMessages, ShowProgress, ShowProgressWindow, ShowChart; + int outputlinenr; + int stop_calculations; + char err_str98[80]; +#endif + /* utilities.cpp ------------------------------- */ + int spinner; + std::map gfw_map; + std::map rates_map; + + /* new after release of Version 3 */ + std::map > sum_species_map; + std::map > sum_species_map_db; + + friend class PBasic; + friend class ChartObject; + friend class IPhreeqc; + friend class TestIPhreeqc; + friend class TestSelectedOutput; + friend class IPhreeqcMMS; + friend class IPhreeqcPhast; + friend class PhreeqcRM; + + std::vector keycount; // used to mark keywords that have been read + +public: + static const struct const_iso iso_defaults[]; + static const int count_iso_defaults; +}; +#endif /* _INC_PHREEQC_H */ + +#ifndef _INC_ISFINITE_H +#define _INC_ISFINITE_H + /********************************* + isfinite handling + (Note: Should NOT be guarded) + **********************************/ + +#if defined (PHREEQ98) || defined (_MSC_VER) +# define HAVE_FINITE +# define finite _finite +#else /*defined (PHREEQ98) || defined (_MSC_VER)*/ +# if defined(DJGPP) +# define HAVE_FINITE +# endif +#endif /*defined (PHREEQ98) || defined (_MSC_VER)*/ + +#if defined(HAVE_ISFINITE) +# if __GNUC__ && (__cplusplus >= 201103L) +# define PHR_ISFINITE(x) std::isfinite(x) +# else +# define PHR_ISFINITE(x) isfinite(x) +# endif +#elif defined(HAVE_FINITE) +# define PHR_ISFINITE(x) finite(x) +#elif defined(HAVE_ISNAN) +# define PHR_ISFINITE(x) ( ((x) == 0.0) || ((!isnan(x)) && ((x) != (2.0 * (x)))) ) +#else +# define PHR_ISFINITE(x) ( ((x) == 0.0) || (((x) == (x)) && ((x) != (2.0 * (x)))) ) +#endif +#endif // _INC_ISFINITE_H + +#ifndef _INC_UTILITIES_NAMESPACE_H +#define _INC_UTILITIES_NAMESPACE_H +namespace Utilities +{ + LDBLE get_nan(void); + + // operations on maps of entities (Solution, Exchange, ...) + template < typename T > + void Rxn_dump_raw(const T & b, std::ostream & s_oss, unsigned int indent) + { + typename T::const_iterator it; + for (it = b.begin(); it != b.end(); ++it) + { + // Adding logic to dump only non-negative entities + if (it->second.Get_n_user() >= 0) + { + it->second.dump_raw(s_oss, indent); + } + } + return; + } + + template < typename T > + T * Rxn_find(std::map < int, T > &b, int i) + { + if (b.find(i) != b.end()) + { + return (&(b.find(i)->second)); + } + else + { + return (NULL); + } + } + + template < typename T > + int Rxn_next_user_number(std::map < int, T > &b) + { + int ret = 0; + if (b.size() != 0) + { + ret = b.rbegin()->first + 1; + } + return ret; + } + + template < typename T > + T * Rxn_copy(std::map < int, T > &b, int i, int j) + { + typename std::map < int, T >::iterator it; + it = b.find(i); + if (it != b.end()) + { + b[j] = it->second; + it = b.find(j); + it->second.Set_n_user(j); + it->second.Set_n_user_end(j); + return &(it->second); + } + else + { + return (NULL); + } + } + + template < typename T > + void Rxn_copies(std::map < int, T > &b, int n_user, int n_user_end) + { + if (n_user_end <= n_user) return; + typename std::map < int, T >::iterator it; + it = b.find(n_user); + if (it != b.end()) + { + for (int j = n_user + 1; j <= n_user_end; j++) + { + b[j] = it->second; + it = b.find(j); + it->second.Set_n_user(j); + it->second.Set_n_user_end(j); + } + } + } + template < typename T > + int Rxn_read_raw(std::map < int, T > &m, std::set < int > &s, Phreeqc * phreeqc_cookie) + { + typename std::map < int, T >::iterator it; + assert(!phreeqc_cookie->reading_database()); + + T entity(phreeqc_cookie->Get_phrq_io()); + + CParser parser(phreeqc_cookie->Get_phrq_io()); + entity.read_raw(parser); + + // Store + if (entity.Get_base_error_count() == 0) + { + m[entity.Get_n_user()] = entity; + } + + // Make copies if necessary + Utilities::Rxn_copies(m, entity.Get_n_user(), entity.Get_n_user_end()); + for (int i = entity.Get_n_user(); i <= entity.Get_n_user_end(); i++) + { + s.insert(i); + } + return phreeqc_cookie->cleanup_after_parser(parser); + } + +#ifdef SKIP + template < typename T > + int Rxn_read_modify(std::map < int, T > &m, std::set < int > &s, Phreeqc * phreeqc_cookie) + { + typename std::map < int, T >::iterator it; + + CParser parser(phreeqc_cookie->Get_phrq_io()); + + std::string key_name; + std::string::iterator b = parser.line().begin(); + std::string::iterator e = parser.line().end(); + CParser::copy_token(key_name, b, e); + + cxxNumKeyword nk; + nk.read_number_description(parser); + T * entity_ptr = Utilities::Rxn_find(m, nk.Get_n_user()); + if (!entity_ptr) + { + std::ostringstream errstr; + errstr << "Could not find " << key_name << " " << nk.Get_n_user() << " to modify.\n"; + phreeqc_cookie->error_msg(errstr.str().c_str(), PHRQ_io::OT_STOP); + } + + entity_ptr->read_raw(parser, false); + entity_ptr->Set_n_user(nk.Get_n_user()); + entity_ptr->Set_n_user_end(nk.Get_n_user_end()); + entity_ptr->Set_description(nk.Get_description()); + s.insert(entity_ptr->Get_n_user()); + + return phreeqc_cookie->cleanup_after_parser(parser); + } +#endif + + template < typename T > + int Rxn_read_modify(std::map < int, T > &m, std::set < int > &s, Phreeqc * phreeqc_cookie) + { + typename std::map < int, T >::iterator it; + + CParser parser(phreeqc_cookie->Get_phrq_io()); + + std::string key_name; + std::string::iterator b = parser.line().begin(); + std::string::iterator e = parser.line().end(); + CParser::copy_token(key_name, b, e); + + cxxNumKeyword nk; + nk.read_number_description(parser); + T * entity_ptr = Utilities::Rxn_find(m, nk.Get_n_user()); + if (!entity_ptr) + { + std::ostringstream errstr; + errstr << "Could not find " << key_name << " " << nk.Get_n_user() << ", ignoring modify data.\n"; + phreeqc_cookie->warning_msg(errstr.str().c_str()); + //phreeqc_cookie->error_msg(errstr.str().c_str(), PHRQ_io::OT_STOP); + + // Don't throw, read data into dummy entity, then ignore + T entity; + entity_ptr = &entity; + entity_ptr->read_raw(parser, false); + return phreeqc_cookie->cleanup_after_parser(parser); + } + + entity_ptr->read_raw(parser, false); + entity_ptr->Set_n_user(nk.Get_n_user()); + entity_ptr->Set_n_user_end(nk.Get_n_user_end()); + entity_ptr->Set_description(nk.Get_description()); + s.insert(entity_ptr->Get_n_user()); + + return phreeqc_cookie->cleanup_after_parser(parser); + } + + template < typename T > + void Rxn_mix(std::map &mix_map, std::map < int, T > &entity_map, Phreeqc * phreeqc_cookie) + { + std::map::iterator mix_it; + for (mix_it = mix_map.begin(); mix_it != mix_map.end(); mix_it++) + { + T entity(entity_map, mix_it->second, mix_it->second.Get_n_user(), phreeqc_cookie->Get_phrq_io()); + entity_map[mix_it->second.Get_n_user()] = entity; + Utilities::Rxn_copies(entity_map, mix_it->second.Get_n_user(), mix_it->second.Get_n_user_end()); + } + mix_map.clear(); + } + +} // namespace Utilities + + +#if defined(PHREEQCI_GUI) +void PhreeqcIWait(Phreeqc *phreeqc); +#endif + +#if !defined(NDEBUG) && defined(WIN32_MEMORY_DEBUG) +#define string_duplicate(s) _string_duplicate(s, __FILE__, __LINE__) +#endif +#if defined(_DEBUG) + char * _string_duplicate(const char *token, const char *szFileName, int nLine); +#endif + +#endif //_INC_UTILITIES_NAMESPACE_H diff --git a/phreeqcpp/PhreeqcKeywords/Keywords.cpp b/phreeqcpp/PhreeqcKeywords/Keywords.cpp new file mode 100644 index 00000000..c17be459 --- /dev/null +++ b/phreeqcpp/PhreeqcKeywords/Keywords.cpp @@ -0,0 +1,225 @@ +#include "Keywords.h" + + +Keywords::Keywords(void) +{ +} + + +Keywords::~Keywords(void) +{ +} + +Keywords::KEYWORDS Keywords::Keyword_search(std::string key) +{ + std::map::const_iterator it; + it = phreeqc_keywords.find(key); + if (it != Keywords::phreeqc_keywords.end()) + { + return it->second; + } + return Keywords::KEY_NONE; +} + +const std::string & Keywords::Keyword_name_search(Keywords::KEYWORDS key) +{ + std::map::const_iterator it; + it = phreeqc_keyword_names.find(key); + if (it != Keywords::phreeqc_keyword_names.end()) + { + return it->second; + } + it = phreeqc_keyword_names.find(KEY_NONE); + return it->second; +} + +const std::map::value_type temp_keywords[] = { +std::map::value_type("eof", Keywords::KEY_END), +std::map::value_type("end", Keywords::KEY_END), +std::map::value_type("solution_species", Keywords::KEY_SOLUTION_SPECIES), +std::map::value_type("solution_master_species", Keywords::KEY_SOLUTION_MASTER_SPECIES), +std::map::value_type("solution", Keywords::KEY_SOLUTION), +std::map::value_type("phases", Keywords::KEY_PHASES), +std::map::value_type("pure_phases", Keywords::KEY_EQUILIBRIUM_PHASES), +std::map::value_type("reaction", Keywords::KEY_REACTION), +std::map::value_type("mix", Keywords::KEY_MIX), +std::map::value_type("use", Keywords::KEY_USE), +std::map::value_type("save", Keywords::KEY_SAVE), +std::map::value_type("exchange_species", Keywords::KEY_EXCHANGE_SPECIES), +std::map::value_type("exchange_master_species", Keywords::KEY_EXCHANGE_MASTER_SPECIES), +std::map::value_type("exchange", Keywords::KEY_EXCHANGE), +std::map::value_type("surface_species", Keywords::KEY_SURFACE_SPECIES), +std::map::value_type("surface_master_species", Keywords::KEY_SURFACE_MASTER_SPECIES), +std::map::value_type("surface", Keywords::KEY_SURFACE), +std::map::value_type("reaction_temperature", Keywords::KEY_REACTION_TEMPERATURE), +std::map::value_type("inverse_modeling", Keywords::KEY_INVERSE_MODELING), +std::map::value_type("gas_phase", Keywords::KEY_GAS_PHASE), +std::map::value_type("transport", Keywords::KEY_TRANSPORT), +std::map::value_type("debug", Keywords::KEY_KNOBS), +std::map::value_type("selected_output", Keywords::KEY_SELECTED_OUTPUT), +std::map::value_type("select_output", Keywords::KEY_SELECTED_OUTPUT), +std::map::value_type("knobs", Keywords::KEY_KNOBS), +std::map::value_type("print", Keywords::KEY_PRINT), +std::map::value_type("equilibrium_phases", Keywords::KEY_EQUILIBRIUM_PHASES), +std::map::value_type("equilibria", Keywords::KEY_EQUILIBRIUM_PHASES), +std::map::value_type("equilibrium", Keywords::KEY_EQUILIBRIUM_PHASES), +std::map::value_type("pure", Keywords::KEY_EQUILIBRIUM_PHASES), +std::map::value_type("title", Keywords::KEY_TITLE), +std::map::value_type("comment", Keywords::KEY_TITLE), +std::map::value_type("advection", Keywords::KEY_ADVECTION), +std::map::value_type("kinetics", Keywords::KEY_KINETICS), +std::map::value_type("incremental_reactions", Keywords::KEY_INCREMENTAL_REACTIONS), +std::map::value_type("incremental", Keywords::KEY_INCREMENTAL_REACTIONS), +std::map::value_type("rates", Keywords::KEY_RATES), +std::map::value_type("solution_s", Keywords::KEY_SOLUTION_SPREAD), +std::map::value_type("user_print", Keywords::KEY_USER_PRINT), +std::map::value_type("user_punch", Keywords::KEY_USER_PUNCH), +std::map::value_type("solid_solutions", Keywords::KEY_SOLID_SOLUTIONS), +std::map::value_type("solid_solution", Keywords::KEY_SOLID_SOLUTIONS), +std::map::value_type("solution_spread", Keywords::KEY_SOLUTION_SPREAD), +std::map::value_type("spread_solution", Keywords::KEY_SOLUTION_SPREAD), +std::map::value_type("selected_out", Keywords::KEY_SELECTED_OUTPUT), +std::map::value_type("select_out", Keywords::KEY_SELECTED_OUTPUT), +std::map::value_type("user_graph", Keywords::KEY_USER_GRAPH), +std::map::value_type("llnl_aqueous_model_parameters",Keywords::KEY_LLNL_AQUEOUS_MODEL_PARAMETERS), +std::map::value_type("llnl_aqueous_model", Keywords::KEY_LLNL_AQUEOUS_MODEL_PARAMETERS), +std::map::value_type("database", Keywords::KEY_DATABASE), +std::map::value_type("named_analytical_expression", Keywords::KEY_NAMED_EXPRESSIONS), +std::map::value_type("named_analytical_expressions", Keywords::KEY_NAMED_EXPRESSIONS), +std::map::value_type("named_expressions", Keywords::KEY_NAMED_EXPRESSIONS), +std::map::value_type("named_log_k", Keywords::KEY_NAMED_EXPRESSIONS), +std::map::value_type("isotopes", Keywords::KEY_ISOTOPES), +std::map::value_type("calculate_values", Keywords::KEY_CALCULATE_VALUES), +std::map::value_type("isotope_ratios", Keywords::KEY_ISOTOPE_RATIOS), +std::map::value_type("isotope_alphas", Keywords::KEY_ISOTOPE_ALPHAS), +std::map::value_type("copy", Keywords::KEY_COPY), +std::map::value_type("pitzer", Keywords::KEY_PITZER), +std::map::value_type("sit", Keywords::KEY_SIT), +std::map::value_type("equilibrium_phase", Keywords::KEY_EQUILIBRIUM_PHASES), +std::map::value_type("solution_raw", Keywords::KEY_SOLUTION_RAW), +std::map::value_type("exchange_raw", Keywords::KEY_EXCHANGE_RAW), +std::map::value_type("surface_raw", Keywords::KEY_SURFACE_RAW), +std::map::value_type("equilibrium_phases_raw", Keywords::KEY_EQUILIBRIUM_PHASES_RAW), +std::map::value_type("kinetics_raw", Keywords::KEY_KINETICS_RAW), +std::map::value_type("solid_solutions_raw", Keywords::KEY_SOLID_SOLUTIONS_RAW), +std::map::value_type("gas_phase_raw", Keywords::KEY_GAS_PHASE_RAW), +std::map::value_type("reaction_raw", Keywords::KEY_REACTION_RAW), +std::map::value_type("mix_raw", Keywords::KEY_MIX_RAW), +std::map::value_type("reaction_temperature_raw", Keywords::KEY_REACTION_TEMPERATURE_RAW), +std::map::value_type("dump", Keywords::KEY_DUMP), +std::map::value_type("solution_modify", Keywords::KEY_SOLUTION_MODIFY), +std::map::value_type("equilibrium_phases_modify", Keywords::KEY_EQUILIBRIUM_PHASES_MODIFY), +std::map::value_type("exchange_modify", Keywords::KEY_EXCHANGE_MODIFY), +std::map::value_type("surface_modify", Keywords::KEY_SURFACE_MODIFY), +std::map::value_type("solid_solutions_modify", Keywords::KEY_SOLID_SOLUTIONS_MODIFY), +std::map::value_type("gas_phase_modify", Keywords::KEY_GAS_PHASE_MODIFY), +std::map::value_type("kinetics_modify", Keywords::KEY_KINETICS_MODIFY), +std::map::value_type("delete", Keywords::KEY_DELETE), +std::map::value_type("run_cells", Keywords::KEY_RUN_CELLS), +std::map::value_type("reaction_modify", Keywords::KEY_REACTION_MODIFY), +std::map::value_type("reaction_temperature_modify", Keywords::KEY_REACTION_TEMPERATURE_MODIFY), +std::map::value_type("solid_solution_modify", Keywords::KEY_SOLID_SOLUTIONS_MODIFY), +std::map::value_type("reaction_pressure", Keywords::KEY_REACTION_PRESSURE), +std::map::value_type("reaction_pressures", Keywords::KEY_REACTION_PRESSURE), +std::map::value_type("reaction_pressure_raw", Keywords::KEY_REACTION_PRESSURE_RAW), +std::map::value_type("reaction_pressure_modify", Keywords::KEY_REACTION_PRESSURE_MODIFY), +std::map::value_type("solution_mix", Keywords::KEY_SOLUTION_MIX), +std::map::value_type("mix_solution", Keywords::KEY_SOLUTION_MIX), +std::map::value_type("exchange_mix", Keywords::KEY_EXCHANGE_MIX), +std::map::value_type("mix_exchange", Keywords::KEY_EXCHANGE_MIX), +std::map::value_type("gas_phase_mix", Keywords::KEY_GAS_PHASE_MIX), +std::map::value_type("mix_gas_phase", Keywords::KEY_GAS_PHASE_MIX), +std::map::value_type("kinetics_mix", Keywords::KEY_KINETICS_MIX), +std::map::value_type("mix_kinetics", Keywords::KEY_KINETICS_MIX), +std::map::value_type("equilibrium_phases_mix", Keywords::KEY_PPASSEMBLAGE_MIX), +std::map::value_type("mix_equilibrium_phases", Keywords::KEY_PPASSEMBLAGE_MIX), +std::map::value_type("equilibrium_phase_mix", Keywords::KEY_PPASSEMBLAGE_MIX), +std::map::value_type("mix_equilibrium_phase", Keywords::KEY_PPASSEMBLAGE_MIX), +std::map::value_type("solid_solutions_mix", Keywords::KEY_SSASSEMBLAGE_MIX), +std::map::value_type("mix_solid_solutions", Keywords::KEY_SSASSEMBLAGE_MIX), +std::map::value_type("solid_solution_mix", Keywords::KEY_SSASSEMBLAGE_MIX), +std::map::value_type("mix_solid_solution", Keywords::KEY_SSASSEMBLAGE_MIX), +std::map::value_type("surface_mix", Keywords::KEY_SURFACE_MIX), +std::map::value_type("mix_surface", Keywords::KEY_SURFACE_MIX) +}; +const std::map Keywords::phreeqc_keywords(temp_keywords, temp_keywords + sizeof temp_keywords / sizeof temp_keywords[0]); + +const std::map::value_type temp_keyword_names[] = { +std::map::value_type(Keywords::KEY_NONE, "UNKNOWN"), +std::map::value_type(Keywords::KEY_END, "END"), +std::map::value_type(Keywords::KEY_SOLUTION_SPECIES, "SOLUTION_SPECIES"), +std::map::value_type(Keywords::KEY_SOLUTION_MASTER_SPECIES, "SOLUTION_MASTER_SPECIES"), +std::map::value_type(Keywords::KEY_SOLUTION, "SOLUTION"), +std::map::value_type(Keywords::KEY_PHASES, "PHASES"), +std::map::value_type(Keywords::KEY_REACTION, "REACTION"), +std::map::value_type(Keywords::KEY_MIX, "MIX"), +std::map::value_type(Keywords::KEY_USE, "USE"), +std::map::value_type(Keywords::KEY_SAVE, "SAVE"), +std::map::value_type(Keywords::KEY_EXCHANGE_SPECIES, "EXCHANGE_SPECIES"), +std::map::value_type(Keywords::KEY_EXCHANGE_MASTER_SPECIES, "EXCHANGE_MASTER_SPECIES"), +std::map::value_type(Keywords::KEY_EXCHANGE, "EXCHANGE"), +std::map::value_type(Keywords::KEY_SURFACE_SPECIES, "SURFACE_SPECIES"), +std::map::value_type(Keywords::KEY_SURFACE_MASTER_SPECIES, "SURFACE_MASTER_SPECIES"), +std::map::value_type(Keywords::KEY_SURFACE, "SURFACE"), +std::map::value_type(Keywords::KEY_REACTION_TEMPERATURE, "REACTION_TEMPERATURE"), +std::map::value_type(Keywords::KEY_INVERSE_MODELING, "INVERSE_MODELING"), +std::map::value_type(Keywords::KEY_GAS_PHASE, "GAS_PHASE"), +std::map::value_type(Keywords::KEY_TRANSPORT, "TRANSPORT"), +std::map::value_type(Keywords::KEY_SELECTED_OUTPUT, "SELECTED_OUTPUT"), +std::map::value_type(Keywords::KEY_KNOBS, "KNOBS"), +std::map::value_type(Keywords::KEY_PRINT, "PRINT"), +std::map::value_type(Keywords::KEY_EQUILIBRIUM_PHASES, "EQUILIBRIUM_PHASES"), +std::map::value_type(Keywords::KEY_TITLE, "TITLE"), +std::map::value_type(Keywords::KEY_ADVECTION, "ADVECTION"), +std::map::value_type(Keywords::KEY_KINETICS, "KINETICS"), +std::map::value_type(Keywords::KEY_INCREMENTAL_REACTIONS, "INCREMENTAL_REACTIONS"), +std::map::value_type(Keywords::KEY_RATES, "RATES"), +std::map::value_type(Keywords::KEY_USER_PRINT, "USER_PRINT"), +std::map::value_type(Keywords::KEY_USER_PUNCH, "USER_PUNCH"), +std::map::value_type(Keywords::KEY_SOLID_SOLUTIONS, "SOLID_SOLUTIONS"), +std::map::value_type(Keywords::KEY_SOLUTION_SPREAD, "SOLUTION_SPREAD"), +std::map::value_type(Keywords::KEY_USER_GRAPH, "USER_GRAPH"), +std::map::value_type(Keywords::KEY_LLNL_AQUEOUS_MODEL_PARAMETERS,"LLNL_AQUEOUS_MODEL_PARAMETERS"), +std::map::value_type(Keywords::KEY_DATABASE, "DATABASE"), +std::map::value_type(Keywords::KEY_NAMED_EXPRESSIONS, "NAMED_EXPRESSIONS"), +std::map::value_type(Keywords::KEY_ISOTOPES, "ISOTOPES"), +std::map::value_type(Keywords::KEY_CALCULATE_VALUES, "CALCULATE_VALUES"), +std::map::value_type(Keywords::KEY_ISOTOPE_RATIOS, "ISOTOPE_RATIOS"), +std::map::value_type(Keywords::KEY_ISOTOPE_ALPHAS, "ISOTOPE_ALPHAS"), +std::map::value_type(Keywords::KEY_COPY, "COPY"), +std::map::value_type(Keywords::KEY_PITZER, "PITZER"), +std::map::value_type(Keywords::KEY_SIT, "SIT"), +std::map::value_type(Keywords::KEY_SOLUTION_RAW, "SOLUTION_RAW"), +std::map::value_type(Keywords::KEY_EXCHANGE_RAW, "EXCHANGE_RAW"), +std::map::value_type(Keywords::KEY_SURFACE_RAW, "SURFACE_RAW"), +std::map::value_type(Keywords::KEY_EQUILIBRIUM_PHASES_RAW, "EQUILIBRIUM_PHASES_RAW"), +std::map::value_type(Keywords::KEY_KINETICS_RAW, "KINETICS_RAW"), +std::map::value_type(Keywords::KEY_SOLID_SOLUTIONS_RAW, "SOLID_SOLUTIONS_RAW"), +std::map::value_type(Keywords::KEY_GAS_PHASE_RAW, "GAS_PHASE_RAW"), +std::map::value_type(Keywords::KEY_REACTION_RAW, "REACTION_RAW"), +std::map::value_type(Keywords::KEY_MIX_RAW, "MIX_RAW"), +std::map::value_type(Keywords::KEY_REACTION_TEMPERATURE_RAW, "REACTION_TEMPERATURE_RAW"), +std::map::value_type(Keywords::KEY_DUMP, "DUMP"), +std::map::value_type(Keywords::KEY_SOLUTION_MODIFY, "SOLUTION_MODIFY"), +std::map::value_type(Keywords::KEY_EQUILIBRIUM_PHASES_MODIFY, "EQUILIBRIUM_PHASES_MODIFY"), +std::map::value_type(Keywords::KEY_EXCHANGE_MODIFY, "EXCHANGE_MODIFY"), +std::map::value_type(Keywords::KEY_SURFACE_MODIFY, "SURFACE_MODIFY"), +std::map::value_type(Keywords::KEY_SOLID_SOLUTIONS_MODIFY, "SOLID_SOLUTIONS_MODIFY"), +std::map::value_type(Keywords::KEY_GAS_PHASE_MODIFY, "GAS_PHASE_MODIFY"), +std::map::value_type(Keywords::KEY_KINETICS_MODIFY, "KINETICS_MODIFY"), +std::map::value_type(Keywords::KEY_DELETE, "DELETE"), +std::map::value_type(Keywords::KEY_RUN_CELLS, "RUN_CELLS"), +std::map::value_type(Keywords::KEY_REACTION_MODIFY, "REACTION_MODIFY"), +std::map::value_type(Keywords::KEY_REACTION_TEMPERATURE_MODIFY, "REACTION_TEMPERATURE_MODIFY"), +std::map::value_type(Keywords::KEY_REACTION_PRESSURE, "REACTION_PRESSURE"), +std::map::value_type(Keywords::KEY_REACTION_PRESSURE_RAW, "REACTION_PRESSURE_RAW"), +std::map::value_type(Keywords::KEY_REACTION_PRESSURE_MODIFY, "REACTION_PRESSURE_MODIFY"), +std::map::value_type(Keywords::KEY_SOLUTION_MIX, "SOLUTION_MIX"), +std::map::value_type(Keywords::KEY_EXCHANGE_MIX, "EXCHANGE_MIX"), +std::map::value_type(Keywords::KEY_GAS_PHASE_MIX, "GAS_PHASE_MIX"), +std::map::value_type(Keywords::KEY_KINETICS_MIX, "KINETICS_MIX"), +std::map::value_type(Keywords::KEY_PPASSEMBLAGE_MIX, "EQUILIBRIUM_PHASES_MIX"), +std::map::value_type(Keywords::KEY_SSASSEMBLAGE_MIX, "SOLID_SOLUTIONS_MIX"), +std::map::value_type(Keywords::KEY_SURFACE_MIX, "SURFACE_MIX") +}; +const std::map Keywords::phreeqc_keyword_names(temp_keyword_names, temp_keyword_names + sizeof temp_keyword_names / sizeof temp_keyword_names[0]); diff --git a/phreeqcpp/PhreeqcKeywords/Keywords.h b/phreeqcpp/PhreeqcKeywords/Keywords.h new file mode 100644 index 00000000..7c5e3740 --- /dev/null +++ b/phreeqcpp/PhreeqcKeywords/Keywords.h @@ -0,0 +1,99 @@ +#ifndef _INC_KEYWORDS_H +#define _INC_KEYWORDS_H +#include +#include +class Keywords +{ +public: + enum KEYWORDS + { + KEY_NONE, + KEY_END, + KEY_SOLUTION_SPECIES, + KEY_SOLUTION_MASTER_SPECIES, + KEY_SOLUTION, + KEY_PHASES, + KEY_REACTION, + KEY_MIX, + KEY_USE, + KEY_SAVE, + KEY_EXCHANGE_SPECIES, + KEY_EXCHANGE_MASTER_SPECIES, + KEY_EXCHANGE, + KEY_SURFACE_SPECIES, + KEY_SURFACE_MASTER_SPECIES, + KEY_SURFACE, + KEY_REACTION_TEMPERATURE, + KEY_INVERSE_MODELING, + KEY_GAS_PHASE, + KEY_TRANSPORT, + KEY_SELECTED_OUTPUT, + KEY_KNOBS, + KEY_PRINT, + KEY_EQUILIBRIUM_PHASES, + KEY_TITLE, + KEY_ADVECTION, + KEY_KINETICS, + KEY_INCREMENTAL_REACTIONS, + KEY_RATES, + KEY_USER_PRINT, + KEY_USER_PUNCH, + KEY_SOLID_SOLUTIONS, + KEY_SOLUTION_SPREAD, + KEY_USER_GRAPH, + KEY_LLNL_AQUEOUS_MODEL_PARAMETERS, + KEY_DATABASE, + KEY_NAMED_EXPRESSIONS, + KEY_ISOTOPES, + KEY_CALCULATE_VALUES, + KEY_ISOTOPE_RATIOS, + KEY_ISOTOPE_ALPHAS, + KEY_COPY, + KEY_PITZER, + KEY_SIT, + KEY_SOLUTION_RAW, + KEY_EXCHANGE_RAW, + KEY_SURFACE_RAW, + KEY_EQUILIBRIUM_PHASES_RAW, + KEY_KINETICS_RAW, + KEY_SOLID_SOLUTIONS_RAW, + KEY_GAS_PHASE_RAW, + KEY_REACTION_RAW, + KEY_MIX_RAW, + KEY_REACTION_TEMPERATURE_RAW, + KEY_DUMP, + KEY_SOLUTION_MODIFY, + KEY_EQUILIBRIUM_PHASES_MODIFY, + KEY_EXCHANGE_MODIFY, + KEY_SURFACE_MODIFY, + KEY_SOLID_SOLUTIONS_MODIFY, + KEY_GAS_PHASE_MODIFY, + KEY_KINETICS_MODIFY, + KEY_DELETE, + KEY_RUN_CELLS, + KEY_REACTION_MODIFY, + KEY_REACTION_TEMPERATURE_MODIFY, + KEY_REACTION_PRESSURE, + KEY_REACTION_PRESSURE_RAW, + KEY_REACTION_PRESSURE_MODIFY, + KEY_SOLUTION_MIX, + KEY_EXCHANGE_MIX, + KEY_GAS_PHASE_MIX, + KEY_KINETICS_MIX, + KEY_PPASSEMBLAGE_MIX, + KEY_SSASSEMBLAGE_MIX, + KEY_SURFACE_MIX, + KEY_COUNT_KEYWORDS // must be last in list + }; + + Keywords(void); + ~Keywords(void); + + static KEYWORDS Keyword_search(std::string key); + static const std::string & Keyword_name_search(KEYWORDS key); + + static const std::map phreeqc_keywords; + static const std::map phreeqc_keyword_names; +}; + +#endif // _INC_KEYWORDS_H \ No newline at end of file diff --git a/phreeqcpp/Pressure.cxx b/phreeqcpp/Pressure.cxx new file mode 100644 index 00000000..03bd30ca --- /dev/null +++ b/phreeqcpp/Pressure.cxx @@ -0,0 +1,443 @@ +// Pressure.cxx: implementation of the cxxPressure class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Parser.h" +#include "Phreeqc.h" +#include "Pressure.h" +#include "phqalloc.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxPressure::cxxPressure(PHRQ_io *io) + // + // default constructor for cxxPressure + // +: cxxNumKeyword(io) +{ + count = 0; + equalIncrements = false; +} + +cxxPressure::~cxxPressure() +{ +} +int +cxxPressure::read(CParser & parser) +{ +/* + * Reads pressure data for reaction steps + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + // Number and description set in read_reaction_pressure + + PHRQ_io::LINE_TYPE lt; + bool done = false; + for (;;) + { + std::istream::pos_type ptr; + std::istream::pos_type next_char = 0; + std::string token, str; + lt = parser.check_line(str, false, true, true, true); + + if (lt == PHRQ_io::LT_EMPTY || + lt == PHRQ_io::LT_KEYWORD || + lt == PHRQ_io::LT_EOF) + { + break; + } + if (lt == PHRQ_io::LT_OPTION) + { + this->error_msg("Expected numeric value for pressures.", PHRQ_io::OT_CONTINUE); + break; + } + + if (done) + { + this->error_msg("Unknown input following equal increment definition.", PHRQ_io::OT_CONTINUE); + continue; + } + + // LT_OK + + for (;;) + { + if (done) break; + // new token + std::string token; + CParser::TOKEN_TYPE k = parser.copy_token(token, next_char); + + // need new line + if (k == CParser::TT_EMPTY) + { + break; + } + + // read a pressure + if (k == CParser::TT_DIGIT) + { + std::istringstream iss(token); + LDBLE d; + if (!(iss >> d)) + { + this->error_msg("Expected numeric value for pressures.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->pressures.push_back(d); + } + continue; + } + + // non digit, must be "in" + if (k == CParser::TT_UPPER || k == CParser::TT_LOWER) + { + if (this->pressures.size() != 2) + { + this->error_msg("To define equal increments, exactly two pressures should be defined.", CONTINUE); + } + else + { + int i = parser.copy_token(token, next_char); + if (i == EMPTY) + { + error_msg("To define equal increments, define 'in n steps'.", CONTINUE); + } + else + { + std::istringstream iss(token); + if ((iss >> i) && i > 0) + { + this->equalIncrements = true; + this->count = i; + } + else + { + error_msg("Unknown input for pressure steps.", CONTINUE); + } + } + done = true; + } + if (k == CParser::TT_UNKNOWN) + { + error_msg("Unknown input for pressure steps.", CONTINUE); + } + } + } // tokens + } // lines + return lt; +} + +void +cxxPressure::dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + s_oss << indent0; + int n_user_local = (n_out != NULL) ? *n_out : this->n_user; + s_oss << "REACTION_PRESSURE_RAW " << n_user_local << " " << this->description << "\n"; + + s_oss << indent1; + s_oss << "-count " << this->count << "\n"; + + s_oss << indent1; + s_oss << "-equal_increments " << this->equalIncrements << "\n"; + + // Temperature element and attributes + + s_oss << indent1; + s_oss << "-pressures" << "\n"; + { + int i = 0; + s_oss << indent2; + for (std::vector < LDBLE >::const_iterator it = this->pressures.begin(); + it != this->pressures.end(); it++) + { + if (i++ == 5) + { + s_oss << "\n"; + s_oss << indent2; + i = 0; + } + s_oss << *it << " "; + } + s_oss << "\n"; + } +} + +void +cxxPressure::read_raw(CParser & parser, bool check) +{ + // clear steps for modify operation, if pressures are read + bool cleared_once = false; + LDBLE d; + CParser::TOKEN_TYPE k; + + std::istream::pos_type ptr; + std::istream::pos_type next_char = 0; + std::string token; + int opt_save; + bool useLastLine(false); + + // Number and description set in read_reaction_pressure_raw + this->read_number_description(parser); + + opt_save = CParser::OPT_ERROR; + bool equalIncrements_defined(false); + bool count_defined(false); + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + opt = parser.getOptionFromLastLine(vopts, next_char, true); + } + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input in REACTION_PRESSURE_RAW keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + useLastLine = false; + break; + + case 0: // pressures + if (!cleared_once) + { + this->pressures.clear(); + cleared_once = true; + } + while ((k = parser.copy_token(token, next_char)) == CParser::TT_DIGIT) + { + std::istringstream iss(token); + if (!(iss >> d)) + { + parser.incr_input_error(); + parser.error_msg("Expected numeric value for pressures.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->pressures.push_back(d); + } + } + opt_save = 0; + useLastLine = false; + break; + + case 1: // equal_increments + if (!(parser.get_iss() >> this->equalIncrements)) + { + this->equalIncrements = 0; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for equalIncrements.", PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + useLastLine = false; + equalIncrements_defined = true; + break; + + case 2: // count + if (!(parser.get_iss() >> this->count)) + { + this->count = 0; + parser.incr_input_error(); + parser.error_msg("Expected integer value for count.", PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + useLastLine = false; + count_defined = true; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + // members that must be defined + if (check) + { + if (equalIncrements_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Equal_increments not defined for REACTION_PRESSURE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (count_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Count_temps not defined for REACTION_PRESSURE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + } +} +#ifdef SKIP +void +cxxPressure::dump_xml(std::ostream & s_oss, unsigned int indent) const const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Temperature element and attributes + s_oss << indent0; + s_oss << " + pitzer_temperature_gammas << "\"" << "\n"; + + // components + s_oss << indent1; + s_oss << "::const_iterator it = + temperatureComps.begin(); it != temperatureComps.end(); ++it) + { + it->dump_xml(s_oss, indent + 2); + } + + return; +} +#endif +/* ---------------------------------------------------------------------- */ +LDBLE cxxPressure:: +Pressure_for_step(int step_number) +/* ---------------------------------------------------------------------- */ +{ +/* + * Determine pressure of reaction step + */ + LDBLE p_temp; + if (this->pressures.size() == 0) + { + p_temp = 1; + } + else if (this->equalIncrements) + { + if (this->pressures.size() != 2) + { + error_msg("Number of pressures not equal to 2 for equal increments.", 0); + } + if (step_number > this->count) + { + p_temp = this->pressures[1]; + } + else + { + LDBLE denom; + denom = (this->count <= 1) ? 1 : (LDBLE) (this->count - 1); + p_temp = this->pressures[0] + ( this->pressures[1] - this->pressures[0]) * + ((LDBLE) (step_number - 1)) / (denom); + } + } + else + { + if (step_number > (int) this->pressures.size()) + { + p_temp = this->pressures[this->pressures.size() - 1]; + } + else + { + p_temp = this->pressures[step_number - 1]; + } + + } + + return (p_temp); +} +int cxxPressure:: +Get_count(void) const +{ + if (equalIncrements) + { + return this->count; + } + return (int) this->pressures.size(); +} +void +cxxPressure::Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles) +{ + ints.push_back(this->n_user); + { + ints.push_back((int) this->pressures.size()); + for (size_t i = 0; i < this->pressures.size(); i++) + { + doubles.push_back(pressures[i]); + } + } + ints.push_back(this->count); + ints.push_back(this->equalIncrements ? 1 : 0); + +} + +void +cxxPressure::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +{ + this->n_user = ints[ii++]; + this->n_user_end = this->n_user; + this->description = " "; + + { + int count = ints[ii++]; + this->pressures.clear(); + for (int i = 0; i < count; i++) + { + this->pressures.push_back(doubles[dd++]); + } + } + this->count = ints[ii++]; + this->equalIncrements = (ints[ii++] != 0); +} + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("pressures"), //0 + std::vector< std::string >::value_type("equal_increments"), //1 + std::vector< std::string >::value_type("count") //2 +}; +const std::vector< std::string > cxxPressure::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); \ No newline at end of file diff --git a/phreeqcpp/Pressure.h b/phreeqcpp/Pressure.h new file mode 100644 index 00000000..377f2039 --- /dev/null +++ b/phreeqcpp/Pressure.h @@ -0,0 +1,43 @@ +#if !defined(PRESSURE_H_INCLUDED) +#define PRESSURE_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NumKeyword.h" +class Dictionary; + +class cxxPressure:public cxxNumKeyword +{ + + public: + cxxPressure(PHRQ_io *io=NULL); + ~cxxPressure(); + + //void dump_xml(std::ostream& os, unsigned int indent = 0)const; + + void dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out=NULL) const; + + int read(CParser & parser); + void read_raw(CParser & parser, bool check = false); + LDBLE Pressure_for_step(int step_number); + std::vector & Get_pressures(void) {return pressures;} + const std::vector & Get_pressures(void)const {return pressures;} + int Get_count(void) const; + void Set_count(int i) {count = i;} + bool Get_equalIncrements(void) const {return equalIncrements;} + void Set_equalIncrements(bool tf) {equalIncrements = tf;} + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + std::vector < LDBLE >pressures; + int count; + bool equalIncrements; + const static std::vector < std::string > vopts; +}; + +#endif // !defined(PRESSURE_H_INCLUDED) diff --git a/phreeqcpp/Reaction.cxx b/phreeqcpp/Reaction.cxx new file mode 100644 index 00000000..44252814 --- /dev/null +++ b/phreeqcpp/Reaction.cxx @@ -0,0 +1,312 @@ +// Reaction.cxx: implementation of the cxxReaction class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "Reaction.h" +#include "phqalloc.h" + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxReaction::cxxReaction(PHRQ_io *io) + // + // default constructor for cxxReaction + // +: cxxNumKeyword(io) +{ + this->Set_units("Mol"); + countSteps = 0; + equalIncrements = false; + reactantList.type = cxxNameDouble::ND_NAME_COEF; + elementList.type = cxxNameDouble::ND_ELT_MOLES; +} +cxxReaction::~cxxReaction() +{ +} + +#ifdef SKIP +void +cxxReaction::dump_xml(std::ostream & s_oss, unsigned int indent) const const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Reaction element and attributes + s_oss << indent0; + s_oss << " + pitzer_irrev_gammas << "\"" << "\n"; + + // components + s_oss << indent1; + s_oss << "::const_iterator it = + irrevComps.begin(); it != irrevComps.end(); ++it) + { + it->dump_xml(s_oss, indent + 2); + } + + return; +} +#endif + +void +cxxReaction::dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Reaction element and attributes + s_oss << indent0; + int n_user_local = (n_out != NULL) ? *n_out : this->n_user; + s_oss << "REACTION_RAW " << n_user_local << " " << this->description << "\n"; + + s_oss << indent1; + s_oss << "-reactant_list" << "\n"; + this->reactantList.dump_raw(s_oss, indent + 2); + + s_oss << indent1; + s_oss << "-steps" << "\n"; + { + int i = 0; + s_oss << indent2; + for (std::vector < LDBLE >::const_iterator it = this->steps.begin(); + it != this->steps.end(); it++) + { + if (i++ == 5) + { + s_oss << "\n"; + s_oss << indent2; + i = 0; + } + s_oss << *it << " "; + } + s_oss << "\n"; + } + + s_oss << indent1; + s_oss << "-count_steps " << this->countSteps << "\n"; + + s_oss << indent1; + s_oss << "-equal_increments " << this->equalIncrements << "\n"; + + s_oss << indent1; + s_oss << "-units " << this->units << "\n"; + + s_oss << indent1 << "# REACTION workspace variables #\n"; + s_oss << indent1; + s_oss << "-element_list" << "\n"; + this->elementList.dump_raw(s_oss, indent + 2); +} + +void +cxxReaction::read_raw(CParser & parser, const bool check) +{ + + int j; + LDBLE d; + CParser::TOKEN_TYPE k; + + // clear steps for modify operation, if steps are read + bool cleared_once = false; + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + bool useLastLine(false); + + // Read irrev number and description + this->read_number_description(parser); + + opt_save = CParser::OPT_ERROR; + bool units_defined(false); + bool equalIncrements_defined(false); + bool countSteps_defined(false); + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + opt = parser.getOptionFromLastLine(vopts, next_char, true); + } + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input in IRREV_COMP_RAW keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + useLastLine = false; + break; + + case 0: // units + j = parser.copy_token(token, next_char); + if (j == CParser::TT_EMPTY) + break; + this->Set_units(token.c_str()); + opt_save = CParser::OPT_DEFAULT; + useLastLine = false; + units_defined = true; + break; + + case 1: // reactant_list + if (this->reactantList.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser.error_msg("Expected reactant formula and coefficient.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 1; + useLastLine = false; + break; + + case 2: // element_list + if (this->elementList.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser.error_msg("Expected element formula and coefficient.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 2; + useLastLine = false; + break; + + case 3: // steps + if (!cleared_once) + { + this->steps.clear(); + cleared_once = true; + } + while ((k = + parser.copy_token(token, next_char)) == CParser::TT_DIGIT) + { + std::istringstream iss(token); + if (!(iss >> d)) + { + parser.incr_input_error(); + parser.error_msg("Expected numeric value for steps.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->steps.push_back(d); + } + } + opt_save = 3; + useLastLine = false; + break; + + case 4: // equal_increments + if (!(parser.get_iss() >> this->equalIncrements)) + { + this->equalIncrements = 0; + parser.incr_input_error(); + parser. + error_msg("Expected boolean value for equalIncrements.", + PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + useLastLine = false; + equalIncrements_defined = true; + break; + + case 5: // countSteps + if (!(parser.get_iss() >> this->countSteps)) + { + this->countSteps = 0; + parser.incr_input_error(); + parser.error_msg("Expected integer value for countSteps.", + PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + useLastLine = false; + countSteps_defined = true; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (check) + { + // members that must be defined + if (units_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Units not defined for REACTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (equalIncrements_defined == false) + { + parser.incr_input_error(); + parser. + error_msg("Equal_increments not defined for REACTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (countSteps_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Count_steps not defined for REACTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + } +} +int cxxReaction:: +Get_reaction_steps(void) const +{ + if (equalIncrements) + { + return this->countSteps; + } + return (int) this->steps.size(); +} +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("units"), //0 + std::vector< std::string >::value_type("reactant_list"), //1 + std::vector< std::string >::value_type("element_list"), //2 + std::vector< std::string >::value_type("steps"), //3 + std::vector< std::string >::value_type("equal_increments"), //4 + std::vector< std::string >::value_type("count_steps") //5 +}; +const std::vector< std::string > cxxReaction::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); \ No newline at end of file diff --git a/phreeqcpp/Reaction.h b/phreeqcpp/Reaction.h new file mode 100644 index 00000000..d03cef64 --- /dev/null +++ b/phreeqcpp/Reaction.h @@ -0,0 +1,57 @@ +#if !defined(REACTION_H_INCLUDED) +#define REACTION_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NumKeyword.h" +#include "NameDouble.h" + +class cxxReaction:public cxxNumKeyword +{ + + public: + cxxReaction(PHRQ_io *io = NULL); + ~cxxReaction(); + + //void dump_xml(std::ostream& os, unsigned int indent = 0)const; + + void dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out=NULL) const; + + void read_raw(CParser & parser, bool check=true); + const cxxNameDouble &Get_elementList(void) const {return this->elementList;} + void Set_elementList(cxxNameDouble nd) {this->elementList = nd;} + cxxNameDouble &Get_reactantList(void) {return this->reactantList;} + const cxxNameDouble &Get_reactantList(void)const {return this->reactantList;} + std::vector < LDBLE > &Get_steps(void) {return this->steps;} + const std::vector < LDBLE > &Get_steps(void)const {return this->steps;} + void Set_steps(std::vector &v) {steps = v;} + int Get_reaction_steps(void) const; + int Get_countSteps(void) const {return this->countSteps;} + void Set_countSteps(int i) {countSteps = i;} + bool Get_equalIncrements(void) const {return this->equalIncrements;} + void Set_equalIncrements(bool tf) {equalIncrements = tf;} + const std::string &Get_units(void) const {return this->units;} + + void Set_units(const char * s) + { + if (s != NULL) + this->units = std::string(s); + else + this->units.clear(); + } + +protected: + cxxNameDouble reactantList; + cxxNameDouble elementList; + std::vector < LDBLE >steps; + int countSteps; + bool equalIncrements; + std::string units; + const static std::vector < std::string > vopts; +}; + +#endif // !defined(REACTION_H_INCLUDED) diff --git a/phreeqcpp/ReadClass.cxx b/phreeqcpp/ReadClass.cxx new file mode 100644 index 00000000..a6350745 --- /dev/null +++ b/phreeqcpp/ReadClass.cxx @@ -0,0 +1,1140 @@ +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include +#include +#include + +#include "Utils.h" +#include "Phreeqc.h" +#include "Parser.h" +#include "Solution.h" +#include "Exchange.h" +#include "Surface.h" +#include "PPassemblage.h" +#include "cxxKinetics.h" +#include "SSassemblage.h" +#include "GasPhase.h" +#include "Reaction.h" +#include "cxxMix.h" +#include "Temperature.h" +#include "dumper.h" +#include "runner.h" +#include "cxxMix.h" +#include "Surface.h" +#include "phqalloc.h" + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_dump(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads DUMP data block + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int return_value; + /* + * Make parser + */ + std::istringstream iss_in; + return_value = streamify_to_next_keyword(iss_in); + CParser parser(iss_in, phrq_io); + assert(!reading_database()); + + //For testing, need to read line to get started + parser.set_echo_file(CParser::EO_NONE); + std::vector < std::string > vopts; + std::istream::pos_type next_char; + parser.get_option(vopts, next_char); + + if (pr.echo_input == FALSE) + { + parser.set_echo_file(CParser::EO_NONE); + } + else + { + parser.set_echo_file(CParser::EO_NOKEYWORDS); + } + + dump_info.Read(parser); + + // Need to output the next keyword + if (return_value == OPTION_KEYWORD) echo_msg(sformatf( "\t%s\n", line)); + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_delete(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads DELETE data block + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int return_value; + /* + * Make parser + */ + std::istringstream iss_in; + return_value = streamify_to_next_keyword(iss_in); + CParser parser(iss_in, phrq_io); + //assert(!reading_database()); + + //For testing, need to read line to get started + parser.set_echo_file(CParser::EO_NONE); + std::vector < std::string > vopts; + std::istream::pos_type next_char; + parser.get_option(vopts, next_char); + + if (pr.echo_input == FALSE) + { + parser.set_echo_file(CParser::EO_NONE); + } + else + { + parser.set_echo_file(CParser::EO_NOKEYWORDS); + } + + delete_info.Read(parser); + + + // Need to output the next keyword + if (return_value == OPTION_KEYWORD) echo_msg(sformatf( "\t%s\n", line)); + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_run_cells(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads DELETE data block + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int return_value; + /* + * Make parser + */ + std::istringstream iss_in; + return_value = streamify_to_next_keyword(iss_in); + CParser parser(iss_in, phrq_io); + assert(!reading_database()); + + //For testing, need to read line to get started + parser.set_echo_file(CParser::EO_NONE); + std::vector < std::string > vopts; + std::istream::pos_type next_char; + parser.get_option(vopts, next_char); + + if (pr.echo_input == FALSE) + { + parser.set_echo_file(CParser::EO_NONE); + } + else + { + parser.set_echo_file(CParser::EO_NOKEYWORDS); + } + + runner r(parser, phrq_io); + run_info = r; + + + // Need to output the next keyword + if (return_value == OPTION_KEYWORD) echo_msg(sformatf( "\t%s\n", line)); + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +streamify_to_next_keyword(std::istringstream & lines) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads to next keyword or eof + * + * Returns: + * OPTION_KEYWORD + * OPTION_EOF + * + */ + // Handle echo + int save_echo_input = pr.echo_input; + pr.echo_input = FALSE; + + std::string accumulate(line); + accumulate.append("\n"); + int j; + for (;;) + { + j = check_line("Streamify", FALSE, TRUE, TRUE, FALSE); + if (j == EOF) + { /* end of file */ + break; + } + else if (j == KEYWORD) + { /* keyword */ + break; + } + else + { + accumulate.append(line); + accumulate.append("\n"); + } + } + + lines.str(accumulate); + pr.echo_input = save_echo_input; + if (j == EOF) return (OPTION_EOF); + if (j == KEYWORD) return (OPTION_KEYWORD); + + + return (OPTION_ERROR); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +dump_entities(void) +/* ---------------------------------------------------------------------- */ +{ + if (!dump_info.Get_on() || pr.dump == FALSE) + { + return(OK); + } + dump_info.Set_on(false); + if (!dump_info.Get_bool_any()) + { + return(OK); + } + + if (this->phrq_io) + { + std::ios_base::openmode mode = std::ios_base::out; + if (dump_info.Get_append()) + { + mode = std::ios_base::app; + } + if (this->phrq_io->dump_open(dump_info.Get_file_name().c_str(), mode)) + { + dump_ostream(*this->phrq_io->Get_dump_ostream()); + this->phrq_io->dump_close(); + } + else + { + error_string = sformatf( "Unable to open dump file \"%s\"", dump_info.Get_file_name().c_str()); + error_msg(error_string, STOP); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +delete_entities(void) +/* ---------------------------------------------------------------------- */ +{ + if (!delete_info.Get_solution().Get_defined() && + !delete_info.Get_pp_assemblage().Get_defined() && + !delete_info.Get_exchange().Get_defined() && + !delete_info.Get_surface().Get_defined() && + !delete_info.Get_ss_assemblage().Get_defined() && + !delete_info.Get_gas_phase().Get_defined() && + !delete_info.Get_kinetics().Get_defined() && + !delete_info.Get_mix().Get_defined() && + !delete_info.Get_reaction().Get_defined() && + !delete_info.Get_temperature().Get_defined() && + !delete_info.Get_pressure().Get_defined()) + { + return(OK); + } + + // solutions + if (delete_info.Get_solution().Get_defined()) + { + if (delete_info.Get_solution().Get_numbers().size() == 0) + { + Rxn_solution_map.clear(); + } + else + { + std::set < int >::iterator it; + for (it = delete_info.Get_solution().Get_numbers().begin(); it != delete_info.Get_solution().Get_numbers().end(); it++) + { + Rxn_solution_map.erase(*it); + } + } + } + + // pp_assemblages + if (delete_info.Get_pp_assemblage().Get_defined()) + { + if (delete_info.Get_pp_assemblage().Get_numbers().size() == 0) + { + Rxn_pp_assemblage_map.clear(); + } + else + { + std::set < int >::iterator it; + for (it = delete_info.Get_pp_assemblage().Get_numbers().begin(); it != delete_info.Get_pp_assemblage().Get_numbers().end(); it++) + { + Rxn_pp_assemblage_map.erase(*it); + } + } + } + // exchangers + if (delete_info.Get_exchange().Get_defined()) + { + if (delete_info.Get_exchange().Get_numbers().size() == 0) + { + Rxn_exchange_map.clear(); + } + else + { + std::set < int >::iterator it; + for (it = delete_info.Get_exchange().Get_numbers().begin(); it != delete_info.Get_exchange().Get_numbers().end(); it++) + { + Rxn_exchange_map.erase(*it); + } + } + } + // surfaces + if (delete_info.Get_surface().Get_defined()) + { + if (delete_info.Get_surface().Get_numbers().size() == 0) + { + Rxn_surface_map.clear(); + } + else + { + std::set < int >::iterator it; + for (it = delete_info.Get_surface().Get_numbers().begin(); it != delete_info.Get_surface().Get_numbers().end(); it++) + { + Rxn_surface_map.erase(*it); + } + } + } + // ss_assemblages + if (delete_info.Get_ss_assemblage().Get_defined()) + { + if (delete_info.Get_ss_assemblage().Get_numbers().size() == 0) + { + Rxn_ss_assemblage_map.clear(); + } + else + { + std::set < int >::iterator it; + for (it = delete_info.Get_ss_assemblage().Get_numbers().begin(); it != delete_info.Get_ss_assemblage().Get_numbers().end(); it++) + { + Rxn_ss_assemblage_map.erase(*it); + } + } + } + // gas_phases + if (delete_info.Get_gas_phase().Get_defined()) + { + if (delete_info.Get_gas_phase().Get_numbers().size() == 0) + { + Rxn_gas_phase_map.clear(); + } + else + { + std::set < int >::iterator it; + for (it = delete_info.Get_gas_phase().Get_numbers().begin(); it != delete_info.Get_gas_phase().Get_numbers().end(); it++) + { + Rxn_gas_phase_map.erase(*it); + } + } + } + // kinetics + if (delete_info.Get_kinetics().Get_defined()) + { + if (delete_info.Get_kinetics().Get_numbers().size() == 0) + { + Rxn_kinetics_map.clear(); + } + else + { + std::set < int >::iterator it; + for (it = delete_info.Get_kinetics().Get_numbers().begin(); it != delete_info.Get_kinetics().Get_numbers().end(); it++) + { + Rxn_kinetics_map.erase(*it); + } + } + } + // mixes + if (delete_info.Get_mix().Get_defined()) + { + if (delete_info.Get_mix().Get_numbers().size() == 0) + { + Rxn_mix_map.clear(); + } + else + { + std::set < int >::iterator it; + for (it = delete_info.Get_mix().Get_numbers().begin(); it != delete_info.Get_mix().Get_numbers().end(); it++) + { + Rxn_mix_map.erase(*it); + } + } + } + // reactions + if (delete_info.Get_reaction().Get_defined()) + { + if (delete_info.Get_reaction().Get_numbers().size() == 0) + { + Rxn_reaction_map.clear(); + } + else + { + std::set < int >::iterator it; + for (it = delete_info.Get_reaction().Get_numbers().begin(); it != delete_info.Get_reaction().Get_numbers().end(); it++) + { + Rxn_reaction_map.erase(*it); + } + } + } + // temperatures + if (delete_info.Get_temperature().Get_defined()) + { + if (delete_info.Get_temperature().Get_numbers().size() == 0) + { + Rxn_temperature_map.clear(); + } + else + { + std::set < int >::iterator it; + for (it = delete_info.Get_temperature().Get_numbers().begin(); it != delete_info.Get_temperature().Get_numbers().end(); it++) + { + Rxn_temperature_map.erase(*it); + } + } + } + // pressures + if (delete_info.Get_pressure().Get_defined()) + { + if (delete_info.Get_pressure().Get_numbers().size() == 0) + { + Rxn_pressure_map.clear(); + } + else + { + std::set < int >::iterator it; + for (it = delete_info.Get_pressure().Get_numbers().begin(); it != delete_info.Get_pressure().Get_numbers().end(); it++) + { + Rxn_pressure_map.erase(*it); + } + } + } + // Turn off delete until next read + delete_info.SetAll(false); + return (OK); +} +#ifdef USE_OPTIMIZED_BUT_NOT_MUCH +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +run_as_cells(void) +/* ---------------------------------------------------------------------- */ +{ + struct save save_data; + LDBLE kin_time; + int count_steps, use_mix; + char token[2 * MAX_LENGTH]; + + state = REACTION; + if (run_info.Get_cells().Get_numbers().size() == 0 || + !(run_info.Get_cells().Get_defined())) return(OK); + + // running cells + run_info.Set_run_cells(true); + + dup_print("Beginning of run as cells.", TRUE); + LDBLE initial_total_time_save; + if (run_info.Get_start_time() != NA) + { + initial_total_time_save = run_info.Get_start_time(); + } + else + { + initial_total_time_save = initial_total_time; + } + + std::set < int >::iterator it = run_info.Get_cells().Get_numbers().begin(); + + for ( ; it != run_info.Get_cells().Get_numbers().end(); it++) + { + int i = *it; + if (i < 0) continue; + initial_total_time = initial_total_time_save; + cxxKinetics *kinetics_ptr = NULL; +/* + * Run reaction step + */ + /* + * Find maximum number of steps + */ + dup_print("Beginning of batch-reaction calculations.", TRUE); + count_steps = 1; + if (cxxReaction *rxn_ptr = Utilities::Rxn_find(Rxn_reaction_map, i)) + { + int count = rxn_ptr->Get_reaction_steps(); + if (count > count_steps) + count_steps = count; + } + if (cxxKinetics *rxn_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i)) + { + kinetics_ptr = rxn_ptr; + if (rxn_ptr->Get_reaction_steps() > count_steps) + count_steps = rxn_ptr->Get_reaction_steps(); + } + if (cxxTemperature *rxn_ptr = Utilities::Rxn_find(Rxn_temperature_map, i)) + { + int count = rxn_ptr->Get_countTemps(); + if (count > count_steps) + { + count_steps = count; + } + } + if (cxxPressure *rxn_ptr = Utilities::Rxn_find(Rxn_pressure_map, i)) + { + int count = rxn_ptr->Get_count(); + if (count > count_steps) + { + count_steps = count; + } + } + count_total_steps = count_steps; + if (count_steps > 1) + { + state = ADVECTION; + set_advection(i, TRUE, TRUE, i); + /* + * save data for saving solutions + */ + memcpy(&save_data, &save, sizeof(struct save)); + /* + *Copy everything to -2 + */ + copy_use(-2); + rate_sim_time_start = 0; + rate_sim_time = 0; + for (reaction_step = 1; reaction_step <= count_steps; reaction_step++) + { + sprintf(token, "Reaction step %d.", reaction_step); + if (reaction_step > 1 && incremental_reactions == FALSE) + { + copy_use(-2); + } + set_initial_moles(-2); + dup_print(token, FALSE); + /* + * Determine time step for kinetics + */ + kin_time = 0.0; + if (use.Get_kinetics_in() == TRUE) + { + // runner kin_time + // equivalent to kin_time in count_steps + if (run_info.Get_time_step() != NA) + { + if (incremental_reactions == FALSE) + { + /* not incremental reactions */ + kin_time = reaction_step * run_info.Get_time_step() / ((LDBLE) count_steps); + } + else + { + /* incremental reactions */ + kin_time = run_info.Get_time_step() / ((LDBLE) count_steps); + } + } + // runner kin_time not defined + else + { + cxxKinetics *kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, -2); + kin_time = kinetics_ptr->Current_step((incremental_reactions==TRUE), reaction_step); + } + } + if (incremental_reactions == FALSE || + (incremental_reactions == TRUE && reaction_step == 1)) + { + use_mix = TRUE; + } + else + { + use_mix = FALSE; + } + /* + * Run reaction step + */ + run_reactions(-2, kin_time, use_mix, 1.0); + if (incremental_reactions == TRUE) + { + rate_sim_time_start += kin_time; + rate_sim_time = rate_sim_time_start; + } + else + { + rate_sim_time = kin_time; + } + punch_all(); + print_all(); + /* saves back into -2 */ + if (reaction_step < count_steps) + { + saver(); + } + } + /* + * save end of reaction + */ + memcpy(&save, &save_data, sizeof(struct save)); + if (use.Get_kinetics_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_kinetics_map, -2, use.Get_n_kinetics_user()); + } + saver(); + } + else + // only 1 step, no worries about incremental reactions + { + state = TRANSPORT; + + rate_sim_time_start = 0; + rate_sim_time = 0; + reaction_step = 1; + + sprintf(token, "Reaction step %d.", reaction_step); + + dup_print(token, FALSE); + /* + * Determine time step for kinetics + */ + kin_time = 0.0; + if (kinetics_ptr) + { + // runner kin_time + // equivalent to kin_time in count_steps + if (run_info.Get_time_step() != NA) + { + kin_time = run_info.Get_time_step(); + } + // runner kin_time not defined + else + { + kin_time = kinetics_ptr->Get_steps()[0]; + } + } + /* + * Run reaction step + */ + use_mix = TRUE; + run_reactions(i, kin_time, use_mix, 1.0); + rate_sim_time = kin_time; + saver(); + } + } + initial_total_time += rate_sim_time; + run_info.Get_cells().Set_defined(false); + // not running cells + run_info.Set_run_cells(false); + return (OK); +} +#else +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +run_as_cells(void) +/* ---------------------------------------------------------------------- */ +{ + struct save save_data; + LDBLE kin_time; + int count_steps, use_mix; + char token[2 * MAX_LENGTH]; + + state = REACTION; + if (run_info.Get_cells().Get_numbers().size() == 0 || + !(run_info.Get_cells().Get_defined())) return(OK); + + // running cells + run_info.Set_run_cells(true); + + dup_print("Beginning of run as cells.", TRUE); + LDBLE initial_total_time_save; + if (run_info.Get_start_time() != NA) + { + initial_total_time_save = run_info.Get_start_time(); + } + else + { + initial_total_time_save = initial_total_time; + } + + std::set < int >::iterator it = run_info.Get_cells().Get_numbers().begin(); + + for ( ; it != run_info.Get_cells().Get_numbers().end(); it++) + { + int i = *it; + if (i < 0) continue; + if (Utilities::Rxn_find(Rxn_solution_map, i) == NULL + && Utilities::Rxn_find(Rxn_mix_map, i) == NULL) + continue; + initial_total_time = initial_total_time_save; + set_advection(i, TRUE, TRUE, i); +/* + * Run reaction step + */ + /* + * Find maximum number of steps + */ + dup_print("Beginning of batch-reaction calculations.", TRUE); + count_steps = 1; + if (!this->run_cells_one_step) + { + if (use.Get_reaction_in() == TRUE && use.Get_reaction_ptr() != NULL) + { + int count = use.Get_reaction_ptr()->Get_reaction_steps(); + if (count > count_steps) + count_steps = count; + } + if (use.Get_kinetics_in() == TRUE && use.Get_kinetics_ptr() != NULL) + { + if (use.Get_kinetics_ptr()->Get_reaction_steps() > count_steps) + count_steps = use.Get_kinetics_ptr()->Get_reaction_steps(); + } + if (use.Get_temperature_in() == TRUE && use.Get_temperature_ptr() != NULL) + { + int count = use.Get_temperature_ptr()->Get_countTemps(); + if (count > count_steps) + { + count_steps = count; + } + } + if (use.Get_pressure_in() == TRUE && use.Get_pressure_ptr() != NULL) + { + int count = use.Get_pressure_ptr()->Get_count(); + if (count > count_steps) + { + count_steps = count; + } + } + } + count_total_steps = count_steps; + /* + * save data for saving solutions + */ + memcpy(&save_data, &save, sizeof(struct save)); + /* + *Copy everything to -2 + */ + copy_use(-2); + rate_sim_time_start = 0; + rate_sim_time = 0; + for (reaction_step = 1; reaction_step <= count_steps; reaction_step++) + { + sprintf(token, "Reaction step %d.", reaction_step); + if (reaction_step > 1 && incremental_reactions == FALSE) + { + copy_use(-2); + } + set_initial_moles(-2); + dup_print(token, FALSE); + /* + * Determine time step for kinetics + */ + kin_time = 0.0; + if (use.Get_kinetics_in() == TRUE) + { + // runner kin_time + // equivalent to kin_time in count_steps + if (run_info.Get_time_step() != NA) + { + if (incremental_reactions == FALSE) + { + /* not incremental reactions */ + kin_time = reaction_step * run_info.Get_time_step() / ((LDBLE) count_steps); + } + else + { + /* incremental reactions */ + kin_time = run_info.Get_time_step() / ((LDBLE) count_steps); + } + } + // runner kin_time not defined + else + { + cxxKinetics *kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, -2); + kin_time = kinetics_ptr->Current_step((incremental_reactions==TRUE), reaction_step); + } + } + if (incremental_reactions == FALSE || + (incremental_reactions == TRUE && reaction_step == 1)) + { + use_mix = TRUE; + } + else + { + use_mix = FALSE; + } + /* + * Run reaction step + */ + run_reactions(-2, kin_time, use_mix, 1.0); + if (incremental_reactions == TRUE) + { + rate_sim_time_start += kin_time; + rate_sim_time = rate_sim_time_start; + } + else + { + rate_sim_time = kin_time; + } + if (state != ADVECTION) + { + punch_all(); + print_all(); + } + /* saves back into -2 */ + if (reaction_step < count_steps) + { + saver(); + } + } + /* + * save end of reaction + */ + memcpy(&save, &save_data, sizeof(struct save)); + if (use.Get_kinetics_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_kinetics_map, -2, use.Get_n_kinetics_user()); + } + saver(); + } + initial_total_time += rate_sim_time; + run_info.Get_cells().Set_defined(false); + // not running cells + run_info.Set_run_cells(false); + return (OK); +} +#endif +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +dump_ostream(std::ostream& os) +/* ---------------------------------------------------------------------- */ +{ + // solutions + if (dump_info.Get_bool_solution()) + { + if (dump_info.Get_solution().size() == 0) + { + Utilities::Rxn_dump_raw(Rxn_solution_map, os, 0); + } + else + { + std::set < int >::iterator it; + for (it = dump_info.Get_solution().begin(); it != dump_info.Get_solution().end(); it++) + { + cxxSolution *p = Utilities::Rxn_find(Rxn_solution_map, *it); + if (p != NULL && p->Get_n_user() >= 0) + { + p->dump_raw(os, 0); + } + } + } + } + + // pp_assemblages + if (dump_info.Get_bool_pp_assemblage()) + { + if (dump_info.Get_pp_assemblage().size() == 0) + { + Utilities::Rxn_dump_raw(Rxn_pp_assemblage_map, os, 0); + } + else + { + std::set < int >::iterator it; + for (it = dump_info.Get_pp_assemblage().begin(); it != dump_info.Get_pp_assemblage().end(); it++) + { + cxxPPassemblage *p = Utilities::Rxn_find(Rxn_pp_assemblage_map, *it); + + if (p != NULL && p->Get_n_user() >= 0) + { + p->dump_raw(os, 0); + } + } + } + } + // exchanges + if (dump_info.Get_bool_exchange()) + { + if (dump_info.Get_exchange().size() == 0) + { + Utilities::Rxn_dump_raw(Rxn_exchange_map, os, 0); + } + else + { + std::set < int >::iterator it; + for (it = dump_info.Get_exchange().begin(); it != dump_info.Get_exchange().end(); it++) + { + cxxExchange *p = Utilities::Rxn_find(Rxn_exchange_map, *it); + + if (p != NULL && p->Get_n_user() >= 0) + { + p->dump_raw(os, 0); + } + } + } + } + + // surfaces + if (dump_info.Get_bool_surface()) + { + if (dump_info.Get_surface().size() == 0) + { + Utilities::Rxn_dump_raw(Rxn_surface_map, os, 0); + } + else + { + std::set < int >::iterator it; + for (it = dump_info.Get_surface().begin(); it != dump_info.Get_surface().end(); it++) + { + cxxSurface *p = Utilities::Rxn_find(Rxn_surface_map, *it); + + if (p != NULL && p->Get_n_user() >= 0) + { + p->dump_raw(os, 0); + } + } + } + } + // ss_assemblages + if (dump_info.Get_bool_ss_assemblage()) + { + if (dump_info.Get_ss_assemblage().size() == 0) + { + Utilities::Rxn_dump_raw(Rxn_ss_assemblage_map, os, 0); + } + else + { + std::set < int >::iterator it; + for (it = dump_info.Get_ss_assemblage().begin(); it != dump_info.Get_ss_assemblage().end(); it++) + { + cxxSSassemblage *p = Utilities::Rxn_find(Rxn_ss_assemblage_map, *it); + + if (p != NULL && p->Get_n_user() >= 0) + { + p->dump_raw(os, 0); + } + } + } + } + // gas_phases + if (dump_info.Get_bool_gas_phase()) + { + if (dump_info.Get_gas_phase().size() == 0) + { + Utilities::Rxn_dump_raw(Rxn_gas_phase_map, os, 0); + } + else + { + std::set < int >::iterator it; + for (it = dump_info.Get_gas_phase().begin(); it != dump_info.Get_gas_phase().end(); it++) + { + cxxGasPhase *p = Utilities::Rxn_find(Rxn_gas_phase_map, *it); + + if (p != NULL && p->Get_n_user() >= 0) + { + p->dump_raw(os, 0); + } + } + } + } + + // kinetics + if (dump_info.Get_bool_kinetics()) + { + if (dump_info.Get_kinetics().size() == 0) + { + Utilities::Rxn_dump_raw(Rxn_kinetics_map, os, 0); + } + else + { + std::set < int >::iterator it; + for (it = dump_info.Get_kinetics().begin(); it != dump_info.Get_kinetics().end(); it++) + { + cxxKinetics *p = Utilities::Rxn_find(Rxn_kinetics_map, *it); + + if (p != NULL && p->Get_n_user() >= 0) + { + p->dump_raw(os, 0); + } + } + } + } + // mix + if (dump_info.Get_bool_mix()) + { + if (dump_info.Get_mix().size() == 0) + { + Utilities::Rxn_dump_raw(Rxn_mix_map, os, 0); + } + else + { + std::set < int >::iterator it; + for (it = dump_info.Get_mix().begin(); it != dump_info.Get_mix().end(); it++) + { + cxxMix *p = Utilities::Rxn_find(Rxn_mix_map, *it); + + if (p != NULL && p->Get_n_user() >= 0) + { + p->dump_raw(os, 0); + } + } + } + } + + // reaction + if (dump_info.Get_bool_reaction()) + { + if (dump_info.Get_reaction().size() == 0) + { + Utilities::Rxn_dump_raw(Rxn_reaction_map, os, 0); + } + else + { + std::set < int >::iterator it; + for (it = dump_info.Get_reaction().begin(); it != dump_info.Get_reaction().end(); it++) + { + cxxReaction *p = Utilities::Rxn_find(Rxn_reaction_map, *it); + + if (p != NULL && p->Get_n_user() >= 0) + { + p->dump_raw(os, 0); + } + } + } + } + + // temperature + if (dump_info.Get_bool_temperature()) + { + if (dump_info.Get_temperature().size() == 0) + { + Utilities::Rxn_dump_raw(Rxn_temperature_map, os, 0); + } + else + { + std::set < int >::iterator it; + for (it = dump_info.Get_temperature().begin(); it != dump_info.Get_temperature().end(); it++) + { + cxxTemperature *p = Utilities::Rxn_find(Rxn_temperature_map, *it); + + if (p != NULL && p->Get_n_user() >= 0) + { + p->dump_raw(os, 0); + } + } + } + } + // pressure + if (dump_info.Get_bool_pressure()) + { + if (dump_info.Get_pressure().size() == 0) + { + Utilities::Rxn_dump_raw(Rxn_pressure_map, os, 0); + } + else + { + std::set < int >::iterator it; + for (it = dump_info.Get_pressure().begin(); it != dump_info.Get_pressure().end(); it++) + { + cxxPressure *p = Utilities::Rxn_find(Rxn_pressure_map, *it); + + if (p != NULL && p->Get_n_user() >= 0) + { + p->dump_raw(os, 0); + } + } + } + } + // Turn off any reaction calculation + os << "USE mix none" << "\n"; + os << "USE reaction none" << "\n"; + os << "USE reaction_temperature none" << "\n"; + os << "USE reaction_pressure none" << "\n"; + + // Turn off dump until next read + dump_info.SetAll(false); +} +#if defined MULTICHART +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_user_graph_handler(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads USER_GRAPH_DATA_BLOCK data block + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int return_value; + + /* + * Make parser + */ + std::istringstream iss_in; + return_value = streamify_to_next_keyword(iss_in); + CParser parser(iss_in, phrq_io); + + //For testing, need to read line to get started + std::vector < std::string > vopts; + std::istream::pos_type next_char; + + if (pr.echo_input == FALSE) + { + parser.set_echo_file(CParser::EO_NONE); + } + else + { + parser.set_echo_file(CParser::EO_NOKEYWORDS); + } + + assert(!reading_database()); + + bool success = chart_handler.Read(this, parser); + + // Need to output the next keyword + if (return_value == OPTION_KEYWORD) echo_msg(sformatf( "\t%s\n", line)); + return (return_value); +} +#endif diff --git a/phreeqcpp/SS.cxx b/phreeqcpp/SS.cxx new file mode 100644 index 00000000..2ed2edc6 --- /dev/null +++ b/phreeqcpp/SS.cxx @@ -0,0 +1,632 @@ +// SSassemblageSS.cxx: implementation of the cxxSS class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Phreeqc.h" +#include "Utils.h" // define first +#include "SS.h" +#include "Dictionary.h" +#include "phqalloc.h" + + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxSS::cxxSS(PHRQ_io *io) +: +PHRQ_base(io) + // + // default constructor for cxxSS + // +{ + + total_moles = 0; + dn = 0; + a0 = 0; + a1 = 0; + ag0 = 0; + ag1 = 0; + ss_in = false; + miscibility = false; + spinodal = false; + tk = 298.15; + xb1 = 0; + xb2 = 0; + input_case = SS_PARM_NONE; + for (int i = 0; i < 4; i++) + { + p.push_back(0); + } +} +cxxSS::~cxxSS() +{ +} + +#ifdef SKIP +void +cxxSS::dump_xml(std::ostream & s_oss, unsigned int indent) const const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // S_S element and attributes + + s_oss << indent0 << "name=\"" << this->name << "\"" << "\n"; + s_oss << indent0 << "add_formula=\"" << this-> + add_formula << "\"" << "\n"; + s_oss << indent0 << "si=\"" << this->si << "\"" << "\n"; + s_oss << indent0 << "moles=\"" << this->moles << "\"" << "\n"; + s_oss << indent0 << "delta=\"" << this->delta << "\"" << "\n"; + s_oss << indent0 << "initial_moles=\"" << this-> + initial_moles << "\"" << "\n"; + s_oss << indent0 << "dissolve_only=\"" << this-> + dissolve_only << "\"" << "\n"; + +} +#endif +void +cxxSS::dump_raw(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + + s_oss << indent0 << "# SOLID_SOLUTION_MODIFY candidate identifiers #\n"; + for (size_t i = 0; i < this->ss_comps.size(); i++) + { + s_oss << indent0 << "-component " << this->ss_comps[i].Get_name() << "\n"; + this->ss_comps[i].dump_raw(s_oss, indent + 1); + } + + s_oss << indent0 << "# SOLID_SOLUTION_MODIFY candidate identifiers with new_def=true #\n"; + s_oss << indent0 << "-tk " << this->tk << "\n"; + s_oss << indent0 << "-input_case " << this->input_case << "\n"; + s_oss << indent0 << "-p " << + p[0] << "\t" << + p[1] << "\t" << + p[2] << "\t" << + p[3] << "\n"; + + s_oss << indent0 << "# solid solution workspace variables #\n"; + s_oss << indent0 << "-ag0 " << this->ag0 << "\n"; + s_oss << indent0 << "-ag1 " << this->ag1 << "\n"; + s_oss << indent0 << "-a0 " << this->a0 << "\n"; + s_oss << indent0 << "-a1 " << this->a1 << "\n"; + s_oss << indent0 << "-xb1 " << this->xb1 << "\n"; + s_oss << indent0 << "-xb2 " << this->xb2 << "\n"; + s_oss << indent0 << "-miscibility " << this->miscibility << "\n"; + s_oss << indent0 << "-spinodal " << this->spinodal << "\n"; + s_oss << indent0 << "-ss_in " << this->ss_in << "\n"; + s_oss << indent0 << "-total_moles " << this->total_moles << "\n"; + s_oss << indent0 << "-dn " << this->dn << "\n"; + s_oss << indent0 << "-totals " << "\n"; + this->totals.dump_raw(s_oss, indent + 1); +} + +void +cxxSS::read_raw(CParser & parser, bool check) +{ + std::string str; + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + opt_save = CParser::OPT_ERROR; + bool a0_defined(false); + bool a1_defined(false); + bool ag0_defined(false); + bool ag1_defined(false); + bool miscibility_defined(false); + bool xb1_defined(false); + bool xb2_defined(false); + bool useLastLine = false; + + for (size_t i = this->Get_p().size(); i < 4; i++) + { + this->p.push_back(0.0); + } + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + opt = parser.getOptionFromLastLine(vopts, next_char, false); + } + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + useLastLine = false; + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_KEYWORD; + // Allow return to Exchange for more processing + break; + + case 0: // name not used + parser.warning_msg("-ss_name not used, defined in -solid_solution"); + opt_save = CParser::OPT_DEFAULT; + break; + + case 1: // total_moles + if (!(parser.get_iss() >> this->total_moles)) + { + this->total_moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for total_moles.", PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + break; + + case 2: // a0 + if (!(parser.get_iss() >> this->a0)) + { + this->a0 = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for a0.", + PHRQ_io::OT_CONTINUE); + } + a0_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 3: // a1 + if (!(parser.get_iss() >> this->a1)) + { + this->a1 = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for a1.", + PHRQ_io::OT_CONTINUE); + } + a1_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 4: // components + case 12: // component + { + if (!(parser.get_iss() >> str)) + { + this->name.clear(); + parser.incr_input_error(); + parser.error_msg("Expected string value for component name.", + PHRQ_io::OT_CONTINUE); + } + else + { + cxxSScomp temp_comp(io); + temp_comp.Set_name(str); + cxxSScomp * comp_ptr = this->Find(str.c_str()); + if (comp_ptr) + { + temp_comp = *comp_ptr; + } + temp_comp.read_raw(parser, false); + if (comp_ptr) + { + for (size_t j = 0; j < this->ss_comps.size(); j++) + { + if (Utilities::strcmp_nocase(this->ss_comps[j].Get_name().c_str(), str.c_str()) == 0) + { + this->ss_comps[j] = temp_comp; + } + } + } + else + { + this->ss_comps.push_back(temp_comp); + } + useLastLine = true; + } + } + + opt_save = CParser::OPT_DEFAULT; + break; + + case 5: // miscibility + if (!(parser.get_iss() >> this->miscibility)) + { + this->miscibility = 0; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for miscibility.", + PHRQ_io::OT_CONTINUE); + } + miscibility_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 6: // spinodal + if (!(parser.get_iss() >> this->spinodal)) + { + this->spinodal = 0; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for spinodal.", PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + break; + + case 7: // tk + if (!(parser.get_iss() >> this->tk)) + { + this->tk = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for tk.", PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + break; + + case 8: // xb1 + if (!(parser.get_iss() >> this->xb1)) + { + this->xb1 = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for xb1.", + PHRQ_io::OT_CONTINUE); + } + xb1_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 9: // xb2 + if (!(parser.get_iss() >> this->xb2)) + { + this->xb2 = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for xb2.", + PHRQ_io::OT_CONTINUE); + } + xb2_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 10: // ag0 + if (!(parser.get_iss() >> this->ag0)) + { + this->ag0 = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for ag0.", + PHRQ_io::OT_CONTINUE); + } + ag0_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 11: // ag1 + if (!(parser.get_iss() >> this->ag1)) + { + this->ag1 = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for ag1.", + PHRQ_io::OT_CONTINUE); + } + ag1_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + case 13: // input_case + { + int i; + if (!(parser.get_iss() >> i)) + { + this->input_case = cxxSS::SS_PARM_NONE; + parser.incr_input_error(); + parser.error_msg("Expected integer value for parameter type.", PHRQ_io::OT_CONTINUE); + } + else + { + this->input_case = (cxxSS::SS_PARAMETER_TYPE) i; + } + } + opt_save = CParser::OPT_DEFAULT; + break; + case 14: // p + { + this->p.clear(); + this->p.assign(4, 0.0); + for (int i = 0; i < 4; i++) + { + if (!(parser.get_iss() >> this->p[i])) + parser.error_msg("Expected 4 parameters."); + } + } + break; + + case 15: // ss_in + { + if (!(parser.get_iss() >> this->ss_in)) + { + this->ss_in = false; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for ss_in.", + PHRQ_io::OT_CONTINUE); + } + } + break; + case 16: // totals + if (this->totals.read_raw(parser, next_char) != CParser::PARSER_OK) + { + parser.incr_input_error(); + parser.error_msg + ("Expected element name and molality for cxxSS totals.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 16; + break; + case 17: // dn + if (!(parser.get_iss() >> this->dn)) + { + this->dn = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for dn.", PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (check) + { + // members that must be defined + if (a0_defined == false) + { + parser.incr_input_error(); + parser.error_msg("A0 not defined for SSassemblageSS input.", + PHRQ_io::OT_CONTINUE); + } + if (a1_defined == false) + { + parser.incr_input_error(); + parser.error_msg("A1 not defined for SSassemblageSS input.", + PHRQ_io::OT_CONTINUE); + } + if (ag0_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Ag0 not defined for SSassemblageSS input.", + PHRQ_io::OT_CONTINUE); + } + if (ag1_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Ag1 not defined for SSassemblageSS input.", + PHRQ_io::OT_CONTINUE); + } + if (miscibility_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Miscibility not defined for SSassemblageSS input.", + PHRQ_io::OT_CONTINUE); + } + if (xb1_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Xb1 not defined for SSassemblageSS input.", + PHRQ_io::OT_CONTINUE); + } + if (xb2_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Xb2 not defined for SSassemblageSS input.", + PHRQ_io::OT_CONTINUE); + } + } +} + +void +cxxSS::totalize(Phreeqc * phreeqc_ptr) +{ + this->totals.clear(); + // component structures + for (size_t i = 0; i < this->ss_comps.size(); i++) + { + struct phase *phase_ptr; + int l; + phase_ptr = phreeqc_ptr-> phase_bsearch(ss_comps[i].Get_name().c_str(), &l, FALSE); + if (phase_ptr != NULL) + { + cxxNameDouble phase_formula(phase_ptr->next_elt); + this->totals.add_extensive(phase_formula, ss_comps[i].Get_moles()); + } + else + { + assert(false); + } + } + return; +} +void +cxxSS::add(const cxxSS & addee_in, LDBLE extensive) +{ + if (extensive == 0.0) + return; + if (addee_in.name.size() == 0) + return; + cxxSS addee = addee_in; + for (size_t j = 0; j < addee.Get_ss_comps().size(); j++) + { + size_t i; + for (i = 0; i < this->ss_comps.size(); i++) + { + //look for component in this + if (Utilities::strcmp_nocase(this->ss_comps[i].Get_name().c_str(), + addee.Get_ss_comps()[j].Get_name().c_str()) == 0) + { + LDBLE d = this->ss_comps[i].Get_initial_moles() + + addee.Get_ss_comps()[j].Get_initial_moles() * extensive; + this->ss_comps[i].Set_initial_moles(d); + d = this->ss_comps[i].Get_moles() + + addee.Get_ss_comps()[j].Get_moles() * extensive; + this->ss_comps[i].Set_moles(d); + d = this->ss_comps[i].Get_init_moles() + + addee.Get_ss_comps()[j].Get_init_moles() * extensive; + this->ss_comps[i].Set_init_moles(d); + d = this->ss_comps[i].Get_delta() + + addee.Get_ss_comps()[j].Get_delta() * extensive; + this->ss_comps[i].Set_delta(d); + break; + } + } + if (i == this->ss_comps.size()) + { + cxxSScomp comp = addee.Get_ss_comps()[j]; + comp.multiply(extensive); + this->Get_ss_comps().push_back(comp); + } + } +} +void +cxxSS::multiply(LDBLE extensive) +{ + size_t i; + for (i = 0; i < this->ss_comps.size(); i++) + { + LDBLE d = this->ss_comps[i].Get_initial_moles() * extensive; + this->ss_comps[i].Set_initial_moles(d); + d = this->ss_comps[i].Get_moles() * extensive; + this->ss_comps[i].Set_moles(d); + d = this->ss_comps[i].Get_init_moles() * extensive; + this->ss_comps[i].Set_init_moles(d); + d = this->ss_comps[i].Get_delta() * extensive; + this->ss_comps[i].Set_delta(d); + } +} +cxxSScomp * +cxxSS::Find(const char * comp_name) +{ + + for (size_t i = 0; i < this->ss_comps.size(); i++) + { + if (ss_comps[i].Get_name() == comp_name) + return &(ss_comps[i]); + } + return NULL; +} +void +cxxSS::Serialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles) +{ + ints.push_back(dictionary.Find(this->name)); + doubles.push_back(this->ag0); + doubles.push_back(this->ag1); + { + ints.push_back((int) ss_comps.size()); + for (size_t i = 0; i < ss_comps.size(); i++) + { + ss_comps[i].Serialize(dictionary, ints, doubles); + } + } + doubles.push_back(this->a0); + doubles.push_back(this->a1); + ints.push_back(this->miscibility ? 1 : 0); + ints.push_back(this->spinodal ? 1 : 0); + doubles.push_back(this->tk); + doubles.push_back(this->xb1); + doubles.push_back(this->xb2); + ints.push_back((int) this->input_case); + { + ints.push_back((int) p.size()); + for (size_t i = 0; i < p.size(); i++) + { + doubles.push_back(p[i]); + } + } + doubles.push_back(this->total_moles); + doubles.push_back(this->dn); + ints.push_back(this->ss_in ? 1 : 0); + this->totals.Serialize(dictionary, ints, doubles); + +} + +void +cxxSS::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +{ + this->name = dictionary.GetWords()[ints[ii++]]; + this->ag0 = doubles[dd++]; + this->ag1 = doubles[dd++]; + { + int count = ints[ii++]; + this->ss_comps.clear(); + for (int i = 0; i < count; i++) + { + cxxSScomp ssc; + ssc.Deserialize(dictionary, ints, doubles, ii, dd); + this->ss_comps.push_back(ssc); + } + } + this->a0 = doubles[dd++]; + this->a1 = doubles[dd++]; + this->miscibility = (ints[ii++] != 0); + this->spinodal = (ints[ii++] != 0); + this->tk = doubles[dd++]; + this->xb1 = doubles[dd++]; + this->xb2 = doubles[dd++]; + this->input_case = (SS_PARAMETER_TYPE) ints[ii++]; + { + int count = ints[ii++]; + this->p.clear(); + for (int i = 0; i < count; i++) + { + p.push_back(doubles[dd++]); + } + } + this->total_moles = doubles[dd++]; + this->dn = doubles[dd++]; + this->ss_in = (ints[ii++] != 0); + this->totals.Deserialize(dictionary, ints, doubles, ii, dd); + +} + + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("ss_name"), // 0 + std::vector< std::string >::value_type("total_moles"), // 1 + std::vector< std::string >::value_type("a0"), // 2 + std::vector< std::string >::value_type("a1"), // 3 + std::vector< std::string >::value_type("components"), // 4 + std::vector< std::string >::value_type("miscibility"), // 5 + std::vector< std::string >::value_type("spinodal"), // 6 + std::vector< std::string >::value_type("tk"), // 7 + std::vector< std::string >::value_type("xb1"), // 8 + std::vector< std::string >::value_type("xb2"), // 9 + std::vector< std::string >::value_type("ag0"), // 10 + std::vector< std::string >::value_type("ag1"), // 11 + std::vector< std::string >::value_type("component"), // 12 + std::vector< std::string >::value_type("input_case"), //13 + std::vector< std::string >::value_type("p"), //14 + std::vector< std::string >::value_type("ss_in"), //15 + std::vector< std::string >::value_type("totals"), //16 + std::vector< std::string >::value_type("dn") //17 +}; +const std::vector< std::string > cxxSS::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); \ No newline at end of file diff --git a/phreeqcpp/SS.h b/phreeqcpp/SS.h new file mode 100644 index 00000000..d5e5a30d --- /dev/null +++ b/phreeqcpp/SS.h @@ -0,0 +1,128 @@ +#if !defined(SSASSEMBLAGESS_H_INCLUDED) +#define SSASSEMBLAGESS_H_INCLUDED + +#include // assert +#include +#include // std::string +#include // std::list +#include // std::vector +#include "phrqtype.h" +#include "NameDouble.h" +#include "SScomp.h" + + +class cxxSS: public PHRQ_base +{ + + public: + cxxSS(PHRQ_io *io=NULL); + virtual ~cxxSS(); + + enum SS_PARAMETER_TYPE + { + SS_PARM_NONE = -1, + SS_PARM_A0_A1 = 0, + SS_PARM_GAMMAS = 1, + SS_PARM_DIST_COEF = 2, + SS_PARM_MISCIBILITY = 3, + SS_PARM_SPINODAL = 4, + SS_PARM_CRITICAL = 5, + SS_PARM_ALYOTROPIC = 6, + SS_PARM_DIM_GUGG = 7, + SS_PARM_WALDBAUM = 8, + SS_PARM_MARGULES = 9 + }; + + void dump_xml(std::ostream & os, unsigned int indent = 0) const; + + void dump_raw(std::ostream & s_oss, unsigned int indent) const; + + void read_raw(CParser & parser, bool check = true); + + const std::string &Get_name() const + { + return this->name; + } + void Set_name (const std::string & s) {this->name = s;} + void Set_name (const char * s) + { + if (s != NULL) + this->name = std::string(s); + else + this->name.clear(); + } + + void totalize(Phreeqc * phreeqc_ptr); + const cxxNameDouble & Get_totals() const + { + return (this->totals); + }; + + cxxSScomp * Find(const char * comp_name); + + void add(const cxxSS & comp, LDBLE extensive); + void multiply(LDBLE extensive); + + LDBLE Get_total_moles(void) const {return total_moles;} + void Set_total_moles(LDBLE t) {total_moles = t;} + + LDBLE Get_dn(void) const {return (this->dn);} + void Set_dn(LDBLE t) {this->dn = t;} + + LDBLE Get_a0() const {return (this->a0);} + void Set_a0(LDBLE t) {a0 = t;} + LDBLE Get_a1() const {return (this->a1);} + void Set_a1(LDBLE t) {a1 = t;} + LDBLE Get_ag0() const {return (this->ag0);} + void Set_ag0(LDBLE t) {ag0 = t;} + LDBLE Get_ag1() const {return (this->ag1);} + void Set_ag1(LDBLE t) {ag1 = t;} + bool Get_ss_in() const {return (this->ss_in);} + void Set_ss_in(bool t) {ss_in = t;} + bool Get_miscibility() const {return (this->miscibility);} + void Set_miscibility(bool t) {miscibility = t;} + bool Get_spinodal() const {return (this->spinodal);} + void Set_spinodal(bool t) {spinodal = t;} + LDBLE Get_xb1() const {return (this->xb1);} + void Set_xb1(LDBLE t) {xb1 = t;} + LDBLE Get_xb2() const {return (this->xb2);} + void Set_xb2(LDBLE t) {xb2 = t;} + std::vector & Get_ss_comps(void) {return ss_comps;} + const std::vector & Get_ss_comps(void)const {return ss_comps;} + void Set_ss_comps(const std::vector & comps) {ss_comps = comps;} + + LDBLE Get_tk(void)const {return this->tk;} + void Set_tk(LDBLE t) {this->tk = t;} + SS_PARAMETER_TYPE Get_input_case(void)const {return this->input_case;} + void Set_input_case(SS_PARAMETER_TYPE t) {this->input_case = t;} + std::vector & Get_p(void) {return this->p;} + const std::vector & Get_p(void)const {return this->p;} + void Set_p(const std::vector & t) {this->p = t;} + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + std::string name; + // candidates for SOLID_SOLUTION_MODIFY + LDBLE ag0, ag1; + std::vector ss_comps; + + // SOLID_SOLUTION keyword data + LDBLE a0, a1; + bool miscibility; + bool spinodal; + LDBLE tk, xb1, xb2; + SS_PARAMETER_TYPE input_case; + std::vector p; + + // workspace variable + LDBLE total_moles; + LDBLE dn; + bool ss_in; + cxxNameDouble totals; + const static std::vector < std::string > vopts; +public: + +}; + +#endif // !defined(SSASSEMBLAGESS_H_INCLUDED) diff --git a/phreeqcpp/SSassemblage.cxx b/phreeqcpp/SSassemblage.cxx new file mode 100644 index 00000000..471127bb --- /dev/null +++ b/phreeqcpp/SSassemblage.cxx @@ -0,0 +1,341 @@ +// SSassemblage.cxx: implementation of the cxxSSassemblage class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "SSassemblage.h" +#include "SS.h" +#include "cxxMix.h" +#include "phqalloc.h" +#include "Dictionary.h" + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxSSassemblage::cxxSSassemblage(PHRQ_io * io) + // + // default constructor for cxxSSassemblage + // +: cxxNumKeyword(io) +{ + new_def = false; +} + +cxxSSassemblage::cxxSSassemblage(const std::map < int, + cxxSSassemblage > &entities, cxxMix & mix, + int l_n_user, PHRQ_io * io): +cxxNumKeyword(io) +{ + this->n_user = this->n_user_end = l_n_user; +// +// Mix +// + const std::map < int, LDBLE >&mixcomps = mix.Get_mixComps(); + std::map < int, LDBLE >::const_iterator it; + for (it = mixcomps.begin(); it != mixcomps.end(); it++) + { + if (entities.find(it->first) != entities.end()) + { + const cxxSSassemblage *entity_ptr = + &(entities.find(it->first)->second); + this->add(*entity_ptr, it->second); + } + } + this->new_def = false; +} + +cxxSSassemblage::~cxxSSassemblage() +{ +} + +#ifdef SKIP +void +cxxSSassemblage::dump_xml(std::ostream & s_oss, unsigned int indent) const const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // SSassemblage element and attributes + s_oss << indent0; + s_oss << "eltList.dump_xml(s_oss, indent + 1); + + // SSs + s_oss << indent1; + s_oss << "::const_iterator it = + SSs.begin(); it != SSs.end(); ++it) + { + it->dump_xml(s_oss, indent + 2); + } +} +#endif +void +cxxSSassemblage::dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // SSassemblage element and attributes + s_oss << indent0; + int n_user_local = (n_out != NULL) ? *n_out : this->n_user; + s_oss << "SOLID_SOLUTIONS_RAW " << n_user_local << " " << this->description << "\n"; + + s_oss << indent1 << "# SOLID_SOLUTION_MODIFY candidate identifiers #\n"; + // SSs + for (std::map < std::string, cxxSS >::const_iterator it = + SSs.begin(); it != SSs.end(); ++it) + { + s_oss << indent1; + s_oss << "-solid_solution " << it->first << "\n"; + (*it).second.dump_raw(s_oss, indent + 2); + } + + s_oss << indent1 << "# SOLID_SOLUTION candidate identifiers with new_def=true #\n"; + s_oss << indent1; + s_oss << "-new_def " << (this->new_def ? 1 : 0) << "\n"; + + s_oss << indent1 << "# solid solution workspace variables #\n"; + s_oss << indent1; + s_oss << "-SSassemblage_totals " << "\n"; + this->totals.dump_raw(s_oss, indent + 2); +} +void +cxxSSassemblage::read_raw(CParser & parser, bool check) +{ + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + bool useLastLine(false); + cxxSS *ss_ptr = NULL; + + // Read SSassemblage number and description + this->read_number_description(parser); + this->Set_new_def(false); + + opt_save = CParser::OPT_ERROR; + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + opt = parser.getOptionFromLastLine(vopts, next_char, true); + } + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + useLastLine = false; + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser. + error_msg("Unknown input in SOLID_SOLUTIONS_RAW or SOLID_SOLUTIONS_MODIFY keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + break; + + case 0: // solid_solution + { + std::string str; + if (!(parser.get_iss() >> str)) + { + parser.incr_input_error(); + parser.error_msg("Expected string value for solid solution name.", + PHRQ_io::OT_CONTINUE); + } + cxxSS temp_ss(this->Get_io()); + temp_ss.Set_name(str); + ss_ptr = this->Find(str); + if (ss_ptr) + { + temp_ss = *ss_ptr; + } + temp_ss.read_raw(parser, false); + this->SSs[str] = temp_ss; + } + useLastLine = true; + break; + case 1: // totals + if (this->totals.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected element name and molality for SSassemblage totals.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 1; + break; + case 2: // new_def + { + int i; + if (!(parser.get_iss() >> i)) + { + parser.incr_input_error(); + parser.error_msg("Expected 0/1 for new_def.", PHRQ_io::OT_CONTINUE); + } + else + { + this->new_def = (i == 0) ? false : true; + } + } + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } +} + +void +cxxSSassemblage::totalize(Phreeqc * phreeqc_ptr) +{ + this->totals.clear(); + // component structures + for (std::map < std::string, cxxSS >::iterator it = + SSs.begin(); it != SSs.end(); ++it) + { + (*it).second.totalize(phreeqc_ptr); + this->totals.add_extensive((*it).second.Get_totals(), 1.0); + } + return; +} +void +cxxSSassemblage::add(const cxxSSassemblage & addee, LDBLE extensive) + // + // Add to existing ssassemblage to "this" ssassemblage + // +{ + if (extensive == 0.0) + return; + + for (std::map < std::string, cxxSS >::const_iterator itadd = + addee.SSs.begin(); itadd != addee.SSs.end(); + ++itadd) + { + std::map < std::string, cxxSS >::iterator it = + this->SSs.find((*itadd).first); + if (it != this->SSs.end()) + { + (*it).second.add((*itadd).second, extensive); + } + else + { + cxxSS entity = (*itadd).second; + entity.multiply(extensive); + std::string str(entity.Get_name()); + this->SSs[str] = entity; + } + } +} + +std::vector cxxSSassemblage:: +Vectorize(void) +{ + std::vector ss_v; + std::map::iterator it; + for (it = this->SSs.begin(); it != this->SSs.end(); it++) + { + ss_v.push_back(&(it->second)); + } + return ss_v; +} +cxxSS * cxxSSassemblage:: +Find(const std::string &s) +{ + std::map::iterator it; + it = this->SSs.find(s); + if (it != this->SSs.end()) + return &(it->second); + return NULL; +} +void +cxxSSassemblage::Serialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles) +{ + /* int n_user; */ + ints.push_back(this->n_user); + { + ints.push_back((int) this->SSs.size()); + std::map < std::string, cxxSS >::iterator it; + for (it = this->SSs.begin(); it != this->SSs.end(); it++) + { + (*it).second.Serialize(dictionary, ints, doubles); + } + } + ints.push_back(this->new_def ? 1 : 0); + this->totals.Serialize(dictionary, ints, doubles); +} + +void +cxxSSassemblage::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +{ + + this->n_user = ints[ii++]; + this->n_user_end = this->n_user; + this->description = " "; + { + int count = ints[ii++]; + this->SSs.clear(); + for (int n = 0; n < count; n++) + { + cxxSS ssc; + ssc.Deserialize(dictionary, ints, doubles, ii, dd); + std::string str(ssc.Get_name()); + this->SSs[str] = ssc; + } + } + this->new_def = (ints[ii++] != 0); + this->totals.Deserialize(dictionary, ints, doubles, ii, dd); + +} + + + + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("solid_solution"), // 0 + std::vector< std::string >::value_type("ssassemblage_totals"), // 1 + std::vector< std::string >::value_type("new_def") // 2 +}; +const std::vector< std::string > cxxSSassemblage::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/SSassemblage.h b/phreeqcpp/SSassemblage.h new file mode 100644 index 00000000..d69a6962 --- /dev/null +++ b/phreeqcpp/SSassemblage.h @@ -0,0 +1,59 @@ +#if !defined(SSASSEMBLAGE_H_INCLUDED) +#define SSASSEMBLAGE_H_INCLUDED + + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NumKeyword.h" +#include "NameDouble.h" +#include "SS.h" + +class cxxSS; + +//#include "cxxMix.h" +class cxxMix; + +class cxxSSassemblage:public cxxNumKeyword +{ + +public: + cxxSSassemblage(PHRQ_io * io = NULL); + cxxSSassemblage(const std::map < int, cxxSSassemblage > &entity_map, + cxxMix & mx, int n_user, PHRQ_io * io = NULL); + ~cxxSSassemblage(); + + //void dump_xml(std::ostream& os, unsigned int indent = 0)const; + + void dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out=NULL) const; + + void read_raw(CParser & parser, bool check = true); + + void totalize(Phreeqc * phreeqc_ptr); + + const cxxNameDouble & Get_totals() const {return this->totals;} + std::map < std::string, cxxSS > & Get_SSs(void) {return SSs;} + const std::map < std::string, cxxSS > & Get_SSs(void)const {return SSs;} + void Set_SSs(std::map < std::string, cxxSS > & ss) {SSs = ss;} + bool Get_new_def(void) const {return new_def;} + void Set_new_def(bool tf) {new_def = tf;} + std::vector Vectorize(void); + void add(const cxxSSassemblage & addee, LDBLE extensive); + cxxSS *Find(const std::string &s); + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + // SOLID_SOLUTION_MODIFY candidate + std::map < std::string, cxxSS > SSs; + // SOLID_SOLUTION keyword data + bool new_def; + // internal variables + cxxNameDouble totals; + const static std::vector < std::string > vopts; +}; + +#endif // !defined(SSASSEMBLAGE_H_INCLUDED) diff --git a/phreeqcpp/SScomp.cxx b/phreeqcpp/SScomp.cxx new file mode 100644 index 00000000..a750a627 --- /dev/null +++ b/phreeqcpp/SScomp.cxx @@ -0,0 +1,332 @@ +// SScomp.cxx: implementation of the cxxSScomp class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "SScomp.h" +#include "phqalloc.h" +#include "Dictionary.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxSScomp::cxxSScomp(PHRQ_io *io) +: +PHRQ_base(io) + // + // default constructor for cxxSScomp + // +{ + name = ""; + initial_moles = 0; + moles = 0; + init_moles = 0; + delta = 0; + fraction_x = 0; + log10_lambda = 0; + log10_fraction_x = 0; + dn = 0; + dnc = 0; + dnb = 0; +} +#ifdef SKIP +cxxSScomp::cxxSScomp(struct pure_phase * pure_phase_ptr, PHRQ_io *io) +: +PHRQ_base(io) + // + // constructor for cxxSScomp from struct pure_phase + // +{ + this->Set_name(pure_phase_ptr->name); + this->Set_add_formula(pure_phase_ptr->add_formula); + si = pure_phase_ptr->si; + si_org = pure_phase_ptr->si_org; + moles = pure_phase_ptr->moles; + delta = pure_phase_ptr->delta; + initial_moles = pure_phase_ptr->initial_moles; + force_equality = (pure_phase_ptr->force_equality == TRUE); + dissolve_only = (pure_phase_ptr->dissolve_only == TRUE); + precipitate_only = (pure_phase_ptr->precipitate_only == TRUE); +} +#endif +cxxSScomp::~cxxSScomp() +{ +} + +void +cxxSScomp::dump_raw(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + s_oss << indent1 << "# SOLID_SOLUTION_MODIFY candidate identifiers #\n"; + s_oss << indent1 << "-moles " << this->moles << "\n"; + + s_oss << indent1 << "# Solid solution workspace variables #\n"; + s_oss << indent1 << "-initial_moles " << this->initial_moles << "\n"; + s_oss << indent1 << "-init_moles " << this->init_moles << "\n"; + s_oss << indent1 << "-delta " << this->delta << "\n"; + s_oss << indent1 << "-fraction_x " << this->fraction_x << "\n"; + s_oss << indent1 << "-log10_lambda " << this->log10_lambda << "\n"; + s_oss << indent1 << "-log10_fraction_x " << this->log10_fraction_x << "\n"; + s_oss << indent1 << "-dn " << this->dn << "\n"; + s_oss << indent1 << "-dnc " << this->dnc << "\n"; + s_oss << indent1 << "-dnb " << this->dnb << "\n"; +} + +void +cxxSScomp::read_raw(CParser & parser, bool check) +{ + std::string str; + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + opt_save = CParser::OPT_ERROR; + bool initial_moles_defined(false); + bool moles_defined(false); + + for (;;) + { + int opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_KEYWORD; + // Allow return to Exchange for more processing + break; + + case 0: // name + parser.error_msg("-Name ignored. Define with -component."); + break; + + case 1: // initial_moles + if (!(parser.get_iss() >> this->initial_moles)) + { + this->initial_moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for initial_moles.", + PHRQ_io::OT_CONTINUE); + } + initial_moles_defined = true; + break; + + case 2: // moles + if (!(parser.get_iss() >> this->moles)) + { + this->moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for moles.", + PHRQ_io::OT_CONTINUE); + } + moles_defined = true; + break; + + case 3: // init_moles + if (!(parser.get_iss() >> this->init_moles)) + { + this->init_moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for init_moles.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 4: // delta + if (!(parser.get_iss() >> this->delta)) + { + this->delta = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for delta.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 5: // fraction_x + if (!(parser.get_iss() >> this->fraction_x)) + { + this->fraction_x = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for fraction_x.", + PHRQ_io::OT_CONTINUE); + } + break; + + + case 6: // log10_lambda + if (!(parser.get_iss() >> this->log10_lambda)) + { + this->log10_lambda = 0; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for log10_lambda.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 7: // log10_fraction_x + if (!(parser.get_iss() >> this->log10_fraction_x)) + { + this->log10_fraction_x = 0; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for log10_fraction_x.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 8: // dn + if (!(parser.get_iss() >> this->dn)) + { + this->dn = 0; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for dn.", + PHRQ_io::OT_CONTINUE); + } + break; + case 9: // dnc + if (!(parser.get_iss() >> this->dnc)) + { + this->dnc = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for dnc.", + PHRQ_io::OT_CONTINUE); + } + break; + case 10: // dnb + if (!(parser.get_iss() >> this->dnb)) + { + this->dnb = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for dnb.", + PHRQ_io::OT_CONTINUE); + } + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + // members that must be defined + if (check) + { + if (moles_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Moles not defined for PPassemblageComp input.", + PHRQ_io::OT_CONTINUE); + } + if (initial_moles_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Initial_moles not defined for PPassemblageComp input.", + PHRQ_io::OT_CONTINUE); + } + } +} + +#ifdef SKIP +void +cxxSScomp::totalize(Phreeqc * phreeqc_ptr) +{ + this->totals.clear(); + // component structures + if (this->add_formula.size() != 0) + return; + struct phase *phase_ptr; + int l; + phase_ptr = phreeqc_ptr-> phase_bsearch(this->name.c_str(), &l, FALSE); + if (phase_ptr != NULL) + { + cxxNameDouble phase_formula(phase_ptr->next_elt); + this->totals.add_extensive(phase_formula, this->moles); + } + else + { + assert(false); + } + return; +} +#endif +void +cxxSScomp::multiply(LDBLE extensive) +{ + this->moles *= extensive; + this->delta *= extensive; + this->initial_moles *= extensive; +} +void +cxxSScomp::Serialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles) +{ + + ints.push_back(dictionary.Find(this->name)); + doubles.push_back(this->moles); + doubles.push_back(this->initial_moles); + doubles.push_back(this->init_moles); + doubles.push_back(this->delta); + doubles.push_back(this->fraction_x); + doubles.push_back(this->log10_lambda); + doubles.push_back(this->log10_fraction_x); + doubles.push_back(this->dn); + doubles.push_back(this->dnc); + doubles.push_back(this->dnb); + +} + +void +cxxSScomp::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +{ + + this->name = dictionary.GetWords()[ints[ii++]]; + this->moles = doubles[dd++]; + this->initial_moles = doubles[dd++]; + this->init_moles = doubles[dd++]; + this->delta = doubles[dd++]; + this->fraction_x = doubles[dd++]; + this->log10_lambda = doubles[dd++]; + this->log10_fraction_x = doubles[dd++]; + this->dn = doubles[dd++]; + this->dnc = doubles[dd++]; + this->dnb = doubles[dd++]; + +} + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("name"), // 0 + std::vector< std::string >::value_type("initial_moles"), // 1 + std::vector< std::string >::value_type("moles"), // 2 + std::vector< std::string >::value_type("init_moles"), // 3 + std::vector< std::string >::value_type("delta"), // 4 + std::vector< std::string >::value_type("fraction_x"), // 5 + std::vector< std::string >::value_type("log10_lambda"), // 6 + std::vector< std::string >::value_type("log10_fraction_x"), // 7 + std::vector< std::string >::value_type("dn"), // 8 + std::vector< std::string >::value_type("dnc"), // 9 + std::vector< std::string >::value_type("dnb") // 10 +}; +const std::vector< std::string > cxxSScomp::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); \ No newline at end of file diff --git a/phreeqcpp/SScomp.h b/phreeqcpp/SScomp.h new file mode 100644 index 00000000..1afa66e5 --- /dev/null +++ b/phreeqcpp/SScomp.h @@ -0,0 +1,66 @@ +#if !defined(SSCOMP_H_INCLUDED) +#define SSCOMP_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NameDouble.h" + +class cxxSScomp: public PHRQ_base +{ + + public: + cxxSScomp(PHRQ_io *io=NULL); + virtual ~cxxSScomp(); + + void dump_raw(std::ostream & s_oss, unsigned int indent) const; + void read_raw(CParser & parser, bool check = true); + + const std::string &Get_name() const {return this->name;} + void Set_name(std::string s) {this->name = s;} + + LDBLE Get_initial_moles() const {return this->initial_moles;} + void Set_initial_moles(LDBLE t) {this->initial_moles = t;} + LDBLE Get_moles() const {return this->moles;} + void Set_moles(LDBLE t) {this->moles = t;} + LDBLE Get_init_moles() const {return this->init_moles;} + void Set_init_moles(LDBLE t) {this->init_moles = t;} + LDBLE Get_delta() const {return this->delta;} + void Set_delta(LDBLE t) {this->delta = t;} + LDBLE Get_fraction_x() const {return this->fraction_x;} + void Set_fraction_x(LDBLE t) {this->fraction_x = t;} + LDBLE Get_log10_lambda() const {return this->log10_lambda;} + void Set_log10_lambda(LDBLE t) {this->log10_lambda = t;} + LDBLE Get_log10_fraction_x() const {return this->log10_fraction_x;} + void Set_log10_fraction_x(LDBLE t) {this->log10_fraction_x = t;} + LDBLE Get_dn() const {return this->dn;} + void Set_dn(LDBLE t) {this->dn = t;} + LDBLE Get_dnc() const {return this->dnc;} + void Set_dnc(LDBLE t) {this->dnc = t;} + LDBLE Get_dnb() const {return this->dnb;} + void Set_dnb(LDBLE t) {this->dnb = t;} + + void multiply(LDBLE extensive); + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + std::string name; + // SOLID_SOLUTION_MODIFY candidate identifier + LDBLE moles; + + // Solid solution workspace variables + LDBLE initial_moles; + LDBLE init_moles; + LDBLE delta; + LDBLE fraction_x; + LDBLE log10_lambda; + LDBLE log10_fraction_x; + LDBLE dn, dnc, dnb; + const static std::vector < std::string > vopts; +}; + +#endif // !defined(SSCOMP_H_INCLUDED) diff --git a/phreeqcpp/SelectedOutput.cpp b/phreeqcpp/SelectedOutput.cpp new file mode 100644 index 00000000..63937185 --- /dev/null +++ b/phreeqcpp/SelectedOutput.cpp @@ -0,0 +1,105 @@ +#include "SelectedOutput.h" + +SelectedOutput::SelectedOutput(int n, PHRQ_io *io) +: cxxNumKeyword(io) +{ + // file_name + this->Set_file_name(n); + + // punch_ostream + this->punch_ostream = NULL; + + // state vars + this->active = true; + this->new_def = false; + this->user_punch_new_def = false; + this->have_punch_name = false; + + // as-is vars + // + this->user_punch = true; + this->high_precision = false; + this->inverse = true; + + this->sim = true; + this->state = true; + this->soln = true; + this->dist = true; + this->time = true; + + this->step = true; + this->ph = true; + this->pe = true; + this->rxn = false; + this->temp = false; + + this->alk = false; + this->mu = false; + this->water = false; + this->charge_balance = false; + this->percent_error = false; + + // as-is set flags + // + this->set_user_punch = false; + this->set_high_precision = false; + this->set_inverse = false; + + this->set_sim = false; + this->set_state = false; + this->set_soln = false; + this->set_dist = false; + this->set_time = false; + + this->set_step = false; + this->set_ph = false; + this->set_pe = false; + this->set_rxn = false; + this->set_temp = false; + + this->set_alk = false; + this->set_mu = false; + this->set_water = false; + this->set_charge_balance = false; + this->set_percent_error = false; +} + +SelectedOutput::~SelectedOutput(void) +{ + if (this->punch_ostream != NULL) + { + delete this->punch_ostream; + } + this->punch_ostream = NULL; +} + +void +SelectedOutput::Reset(bool value) +{ + // as-is vars + Set_sim(value); + Set_state(value); + Set_soln(value); + Set_dist(value); + Set_time(value); + + Set_step(value); + Set_ph(value); + Set_pe(value); + Set_rxn(value); + Set_temp(value); + + Set_alk(value); + Set_mu(value); + Set_water(value); + Set_charge_balance(value); + Set_percent_error(value); +} + +void +SelectedOutput::Set_file_name(int n) +{ + std::ostringstream os; + os << "selected_output_" << n << ".sel"; + file_name = os.str(); +} diff --git a/phreeqcpp/SelectedOutput.h b/phreeqcpp/SelectedOutput.h new file mode 100644 index 00000000..39a8bac0 --- /dev/null +++ b/phreeqcpp/SelectedOutput.h @@ -0,0 +1,204 @@ +#if !defined(SELECTEDOUTPUT_H_INCLUDED) +#define SELECTEDOUTPUT_H_INCLUDED +#include // std::string +#include +#include +#include "NumKeyword.h" + +class SelectedOutput:public cxxNumKeyword +{ +public: + SelectedOutput(int n=1, PHRQ_io *io=NULL); + ~SelectedOutput(void); + + void Reset(bool tf); + + // vector getters + inline std::vector< std::pair< std::string, void * > > & Get_totals(void) {return this->totals;} + inline std::vector< std::pair< std::string, void * > > & Get_molalities(void) {return this->molalities;} + inline std::vector< std::pair< std::string, void * > > & Get_activities(void) {return this->activities;} + inline std::vector< std::pair< std::string, void * > > & Get_pure_phases(void) {return this->pure_phases;} + inline std::vector< std::pair< std::string, void * > > & Get_si(void) {return this->si;} + inline std::vector< std::pair< std::string, void * > > & Get_gases(void) {return this->gases;} + inline std::vector< std::pair< std::string, void * > > & Get_s_s(void) {return this->s_s;} + inline std::vector< std::pair< std::string, void * > > & Get_kinetics(void) {return this->kinetics;} + inline std::vector< std::pair< std::string, void * > > & Get_isotopes(void) {return this->isotopes;} + inline std::vector< std::pair< std::string, void * > > & Get_calculate_values(void) {return this->calculate_values;} + + // const vector getters + inline const std::vector< std::pair< std::string, void * > > & Get_totals(void)const {return this->totals;} + inline const std::vector< std::pair< std::string, void * > > & Get_molalities(void)const {return this->molalities;} + inline const std::vector< std::pair< std::string, void * > > & Get_activities(void)const {return this->activities;} + inline const std::vector< std::pair< std::string, void * > > & Get_pure_phases(void)const {return this->pure_phases;} + inline const std::vector< std::pair< std::string, void * > > & Get_si(void)const {return this->si;} + inline const std::vector< std::pair< std::string, void * > > & Get_gases(void)const {return this->gases;} + inline const std::vector< std::pair< std::string, void * > > & Get_s_s(void)const {return this->s_s;} + inline const std::vector< std::pair< std::string, void * > > & Get_kinetics(void)const {return this->kinetics;} + inline const std::vector< std::pair< std::string, void * > > & Get_isotopes(void)const {return this->isotopes;} + inline const std::vector< std::pair< std::string, void * > > & Get_calculate_values(void)const {return this->calculate_values;} + + // file_name getters/setters + void Set_file_name(int i); + inline void Set_file_name(std::string s) {this->file_name = s;} + inline std::string & Get_file_name(void) {return this->file_name;} + inline const std::string & Get_file_name(void)const {return this->file_name;} + + // punch_ostream getters/setters + inline std::ostream* Get_punch_ostream(void) {return this->punch_ostream;} + inline const std::ostream* Get_punch_ostream(void)const {return this->punch_ostream;} + inline void Set_punch_ostream(std::ostream * os) {this->punch_ostream = os;} + + // state var getters + inline bool Get_active(void)const {return this->active;} + inline bool Get_new_def(void)const {return this->new_def;} + inline bool Get_user_punch_new_def(void)const {return this->user_punch_new_def;} + inline bool Get_have_punch_name(void)const {return this->have_punch_name;} + + // state var setters + inline void Set_active(bool tf) {this->active = tf;} + inline void Set_new_def(bool tf) {this->new_def = tf;} + inline void Set_user_punch_new_def(bool tf) {this->user_punch_new_def = tf;} + inline void Set_have_punch_name(bool tf) {this->have_punch_name = tf;} + + // as_is getters + inline bool Get_user_punch(void)const {return this->user_punch;} + inline bool Get_high_precision(void)const {return this->high_precision;} + inline bool Get_inverse(void)const {return this->inverse;} + + inline bool Get_sim(void)const {return this->sim;} + inline bool Get_state(void)const {return this->state;} + inline bool Get_soln(void)const {return this->soln;} + inline bool Get_dist(void)const {return this->dist;} + inline bool Get_time(void)const {return this->time;} + + inline bool Get_step(void)const {return this->step;} + inline bool Get_ph(void)const {return this->ph;} + inline bool Get_pe(void)const {return this->pe;} + inline bool Get_rxn(void)const {return this->rxn;} + inline bool Get_temp(void)const {return this->temp;} + + inline bool Get_alk(void)const {return this->alk;} + inline bool Get_mu(void)const {return this->mu;} + inline bool Get_water(void)const {return this->water;} + inline bool Get_charge_balance(void)const {return this->charge_balance;} + inline bool Get_percent_error(void)const {return this->percent_error;} + + // as-is setters + inline void Set_user_punch(bool tf) {this->user_punch = tf; this->set_user_punch = true;} + inline void Set_high_precision(bool tf) {this->high_precision = tf; this->set_high_precision = true;} + inline void Set_inverse(bool tf) {this->inverse = tf; this->set_inverse = true;} + + inline void Set_sim(bool tf) {this->sim = tf; this->set_sim = true;} + inline void Set_state(bool tf) {this->state = tf; this->set_state = true;} + inline void Set_soln(bool tf) {this->soln = tf; this->set_soln = true;} + inline void Set_dist(bool tf) {this->dist = tf; this->set_dist = true;} + inline void Set_time(bool tf) {this->time = tf; this->set_time = true;} + + inline void Set_step(bool tf) {this->step = tf; this->set_step = true;} + inline void Set_ph(bool tf) {this->ph = tf; this->set_ph = true;} + inline void Set_pe(bool tf) {this->pe = tf; this->set_pe = true;} + inline void Set_rxn(bool tf) {this->rxn = tf; this->set_rxn = true;} + inline void Set_temp(bool tf) {this->temp = tf; this->set_temp = true;} + + inline void Set_alk(bool tf) {this->alk = tf; this->set_alk = true;} + inline void Set_mu(bool tf) {this->mu = tf; this->set_mu = true;} + inline void Set_water(bool tf) {this->water = tf; this->set_water = true;} + inline void Set_charge_balance(bool tf) {this->charge_balance = tf; this->set_charge_balance = true;} + inline void Set_percent_error(bool tf) {this->percent_error = tf; this->set_percent_error = true;} + + // set flag getters + inline bool was_set_user_punch()const {return this->set_user_punch;} + inline bool was_set_high_precision()const {return this->set_high_precision;} + inline bool was_set_inverse()const {return this->set_inverse;} + + inline bool was_set_sim()const {return this->set_sim;} + inline bool was_set_state()const {return this->set_state;} + inline bool was_set_soln()const {return this->set_soln;} + inline bool was_set_dist()const {return this->set_dist;} + inline bool was_set_time()const {return this->set_time;} + + inline bool was_set_step()const {return this->set_step;} + inline bool was_set_ph()const {return this->set_ph;} + inline bool was_set_pe()const {return this->set_pe;} + inline bool was_set_rxn()const {return this->set_rxn;} + inline bool was_set_temp()const {return this->set_temp;} + + inline bool was_set_alk()const {return this->set_alk;} + inline bool was_set_mu()const {return this->set_mu;} + inline bool was_set_water()const {return this->set_water;} + inline bool was_set_charge_balance()const {return this->set_charge_balance;} + inline bool was_set_percent_error()const {return this->set_percent_error;} + +protected: + + // vectors + std::vector< std::pair< std::string, void * > > totals; + std::vector< std::pair< std::string, void * > > molalities; + std::vector< std::pair< std::string, void * > > activities; + std::vector< std::pair< std::string, void * > > pure_phases; + std::vector< std::pair< std::string, void * > > si; + std::vector< std::pair< std::string, void * > > gases; + std::vector< std::pair< std::string, void * > > s_s; + std::vector< std::pair< std::string, void * > > kinetics; + std::vector< std::pair< std::string, void * > > isotopes; + std::vector< std::pair< std::string, void * > > calculate_values; + + // file_name + std::string file_name; + + // punch_ostream + std::ostream * punch_ostream; + + // state vars + bool active; + bool new_def; + bool user_punch_new_def; + bool have_punch_name; + + // as-is vars + bool user_punch; + bool high_precision; + bool inverse; + + bool sim; + bool state; + bool soln; + bool dist; + bool time; + + bool step; + bool ph; + bool pe; + bool rxn; + bool temp; + + bool alk; + bool mu; + bool water; + bool charge_balance; + bool percent_error; + + // as-is set flags + bool set_user_punch; + bool set_high_precision; + bool set_inverse; + + bool set_sim; + bool set_state; + bool set_soln; + bool set_dist; + bool set_time; + + bool set_step; + bool set_ph; + bool set_pe; + bool set_rxn; + bool set_temp; + + bool set_alk; + bool set_mu; + bool set_water; + bool set_charge_balance; + bool set_percent_error; +}; +#endif // !defined(SELECTEDOUTPUT_H_INCLUDED) diff --git a/phreeqcpp/Serializer.cxx b/phreeqcpp/Serializer.cxx new file mode 100644 index 00000000..4b67baeb --- /dev/null +++ b/phreeqcpp/Serializer.cxx @@ -0,0 +1,200 @@ +#include "Serializer.h" +#include "Phreeqc.h" +#include "Utils.h" +#include "Solution.h" +#include "Exchange.h" +#include "Temperature.h" +#include "GasPhase.h" +#include "cxxKinetics.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "Surface.h" +Serializer::Serializer(PHRQ_io *io) + : PHRQ_base(io) +{ +} +Serializer::~Serializer(void) +{ +} +bool Serializer::Serialize(Phreeqc &phreeqc_ref, int start, int end, bool include_t, bool include_p, PHRQ_io *io) +{ + for (int i = start; i <= end; i++) + { + cxxSolution *soln_ptr = Utilities::Rxn_find(phreeqc_ref.Get_Rxn_solution_map(), i); + if (soln_ptr) + { + ints.push_back((int) PT_SOLUTION); + soln_ptr->Serialize(this->dictionary, this->ints, this->doubles); + } + // Exchangers + { + cxxExchange *entity_ptr = Utilities::Rxn_find(phreeqc_ref.Get_Rxn_exchange_map(), i); + if (entity_ptr) + { + ints.push_back((int) PT_EXCHANGE); + entity_ptr->Serialize(this->dictionary, this->ints, this->doubles); + } + } + // GasPhases + { + cxxGasPhase *entity_ptr = Utilities::Rxn_find(phreeqc_ref.Get_Rxn_gas_phase_map(), i); + if (entity_ptr) + { + ints.push_back((int) PT_GASPHASE); + entity_ptr->Serialize(this->dictionary, this->ints, this->doubles); + } + } + // Kinetics + { + cxxKinetics *entity_ptr = Utilities::Rxn_find(phreeqc_ref.Get_Rxn_kinetics_map(), i); + if (entity_ptr) + { + ints.push_back((int) PT_KINETICS); + entity_ptr->Serialize(this->dictionary, this->ints, this->doubles); + } + } + // PPassemblages + { + cxxPPassemblage *entity_ptr = Utilities::Rxn_find(phreeqc_ref.Get_Rxn_pp_assemblage_map(), i); + if (entity_ptr) + { + ints.push_back((int) PT_PPASSEMBLAGE); + entity_ptr->Serialize(this->dictionary, this->ints, this->doubles); + } + } + // SSassemblages + { + cxxSSassemblage *entity_ptr = Utilities::Rxn_find(phreeqc_ref.Get_Rxn_ss_assemblage_map(), i); + if (entity_ptr) + { + ints.push_back((int) PT_SSASSEMBLAGE); + entity_ptr->Serialize(this->dictionary, this->ints, this->doubles); + } + } + // Surfaces + { + cxxSurface *entity_ptr = Utilities::Rxn_find(phreeqc_ref.Get_Rxn_surface_map(), i); + if (entity_ptr) + { + ints.push_back((int) PT_SURFACES); + entity_ptr->Serialize(this->dictionary, this->ints, this->doubles); + } + } + // Temperature + if (include_t) + { + cxxTemperature *entity_ptr = Utilities::Rxn_find(phreeqc_ref.Get_Rxn_temperature_map(), i); + if (entity_ptr) + { + ints.push_back((int) PT_TEMPERATURE); + entity_ptr->Serialize(this->dictionary, this->ints, this->doubles); + } + } + // Pressure + if (include_p) + { + cxxPressure *entity_ptr = Utilities::Rxn_find(phreeqc_ref.Get_Rxn_pressure_map(), i); + if (entity_ptr) + { + ints.push_back((int) PT_PRESSURE); + entity_ptr->Serialize(this->dictionary, this->ints, this->doubles); + } + } + } + return true; +} + +bool +Serializer::Deserialize(Phreeqc &phreeqc_ref, Dictionary &dictionary, std::vector &ints, std::vector &doubles) +{ + int ii = 0; + int dd = 0; + while (ii < (int) ints.size()) + { + PACK_TYPE type = (PACK_TYPE) ints[ii++]; + switch (type) + { + case PT_SOLUTION: + { + cxxSolution soln; + soln.Deserialize(dictionary, ints, doubles, ii, dd); + int n_user = soln.Get_n_user(); + //std::cerr << "unpacked solution " << n_user << std::endl; + phreeqc_ref.Get_Rxn_solution_map()[n_user] = soln; + } + break; + case PT_EXCHANGE: + { + cxxExchange entity; + entity.Deserialize(dictionary, ints, doubles, ii, dd); + int n_user = entity.Get_n_user(); + phreeqc_ref.Get_Rxn_exchange_map()[n_user] = entity; + } + break; + case PT_GASPHASE: + { + cxxGasPhase entity; + entity.Deserialize(dictionary, ints, doubles, ii, dd); + int n_user = entity.Get_n_user(); + phreeqc_ref.Get_Rxn_gas_phase_map()[n_user] = entity; + } + break; + case PT_KINETICS: + { + cxxKinetics entity; + entity.Deserialize(dictionary, ints, doubles, ii, dd); + int n_user = entity.Get_n_user(); + phreeqc_ref.Get_Rxn_kinetics_map()[n_user] = entity; + } + break; + case PT_PPASSEMBLAGE: + { + cxxPPassemblage entity; + entity.Deserialize(dictionary, ints, doubles, ii, dd); + int n_user = entity.Get_n_user(); + //std::cerr << "unpacked pp assemblage " << n_user << std::endl; + phreeqc_ref.Get_Rxn_pp_assemblage_map()[n_user] = entity; + } + break; + case PT_SSASSEMBLAGE: + { + cxxSSassemblage entity; + entity.Deserialize(dictionary, ints, doubles, ii, dd); + int n_user = entity.Get_n_user(); + phreeqc_ref.Get_Rxn_ss_assemblage_map()[n_user] = entity; + } + break; + case PT_SURFACES: + { + cxxSurface entity; + entity.Deserialize(dictionary, ints, doubles, ii, dd); + int n_user = entity.Get_n_user(); + phreeqc_ref.Get_Rxn_surface_map()[n_user] = entity; + } + break; + case PT_TEMPERATURE: + { + cxxTemperature entity; + entity.Deserialize(dictionary, ints, doubles, ii, dd); + int n_user = entity.Get_n_user(); + phreeqc_ref.Get_Rxn_temperature_map()[n_user] = entity; + } + break; + case PT_PRESSURE: + { + cxxPressure entity; + entity.Deserialize(dictionary, ints, doubles, ii, dd); + int n_user = entity.Get_n_user(); + phreeqc_ref.Get_Rxn_pressure_map()[n_user] = entity; + } + break; + default: +#if !defined(R_SO) + std::cerr << "Unknown pack type in deserialize " << type << std::endl; + exit(4); +#endif + break; + } + } + return true; +} diff --git a/phreeqcpp/Serializer.h b/phreeqcpp/Serializer.h new file mode 100644 index 00000000..80c285d8 --- /dev/null +++ b/phreeqcpp/Serializer.h @@ -0,0 +1,42 @@ +#if !defined(SERIALIZER_H_INCLUDED) +#define SERIALIZER_H_INCLUDED +#include +#include +#include +#include +#include "PHRQ_base.h" +#include "Dictionary.h" +class Phreeqc; +class Serializer : public PHRQ_base +{ +public: + Serializer(PHRQ_io *io = NULL); + ~Serializer(void); + + enum PACK_TYPE + { + PT_SOLUTION = 0, + PT_EXCHANGE = 1, + PT_GASPHASE = 2, + PT_KINETICS = 3, + PT_PPASSEMBLAGE = 4, + PT_SSASSEMBLAGE = 5, + PT_SURFACES = 6, + PT_TEMPERATURE = 7, + PT_PRESSURE = 8 + }; + bool Serialize(Phreeqc &phreeqc_ptr, int start, int end, bool include_t, bool include_p, PHRQ_io *io = NULL); + bool Deserialize(Phreeqc &phreeqc_ptr, Dictionary &dictionary, std::vector &ints, std::vector &doubles); + Dictionary &GetDictionary(void) {return this->dictionary;} + std::vector &GetInts(void) {return this->ints;} + std::vector &GetDoubles(void) {return this->doubles;} + //std::string &GetWordsString(void) {return this->words_string;} + + +protected: + std::vector ints; + std::vector doubles; + //std::string words_string; + Dictionary dictionary; +}; +#endif // !defined(SERIALIZER_H_INCLUDED) \ No newline at end of file diff --git a/phreeqcpp/Solution.cxx b/phreeqcpp/Solution.cxx new file mode 100644 index 00000000..3500128d --- /dev/null +++ b/phreeqcpp/Solution.cxx @@ -0,0 +1,1727 @@ +// Solution.cxx: implementation of the cxxSolution class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif + +#include +#include // assert +#include // std::sort +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "Solution.h" +#include "cxxMix.h" +#include "phqalloc.h" +#include "Dictionary.h" + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + + +cxxSolution::cxxSolution(PHRQ_io * io) + // + // default constructor for cxxSolution + // +: cxxNumKeyword(io) +{ + this->io = io; + this->new_def = false; + this->patm = 1.0; + this->potV = 0.0; + this->tc = 25.0; + this->ph = 7.0; + this->pe = 4.0; + this->mu = 1e-7; + this->ah2o = 1.0; + this->total_h = 111.1; + this->total_o = 55.55; + this->cb = 0.0; + this->density = 1.0; + this->mass_water = 1.0; + this->soln_vol = 1.0; + this->total_alkalinity = 0.0; + this->totals.type = cxxNameDouble::ND_ELT_MOLES; + this->master_activity.type = cxxNameDouble::ND_SPECIES_LA; + this->species_gamma.type = cxxNameDouble::ND_SPECIES_GAMMA; + this->initial_data = NULL; +} +cxxSolution::cxxSolution(const cxxSolution &old_sol) +: initial_data(NULL) +{ + *this = old_sol; +} +const cxxSolution & +cxxSolution::operator =(const cxxSolution &rhs) +{ + if (this != &rhs) + { + this->io = rhs.io; + this->n_user = rhs.n_user; + this->n_user_end = rhs.n_user_end; + this->description = rhs.description; + this->new_def = rhs.new_def; + this->patm = rhs.patm; + this->potV = rhs.potV; + this->tc = rhs.tc; + this->ph = rhs.ph; + this->pe = rhs.pe; + this->mu = rhs.mu; + this->ah2o = rhs.ah2o; + this->total_h = rhs.total_h; + this->total_o = rhs.total_o; + this->density = rhs.density; + this->cb = rhs.cb; + this->mass_water = rhs.mass_water; + this->soln_vol = rhs.soln_vol; + this->total_alkalinity = rhs.total_alkalinity; + this->totals = rhs.totals; + this->master_activity = rhs.master_activity; + this->species_gamma = rhs.species_gamma; + this->isotopes = rhs.isotopes; + this->species_map = rhs.species_map; + this->log_gamma_map = rhs.log_gamma_map; + if (this->initial_data) + delete initial_data; + if (rhs.initial_data != NULL) + this->initial_data = new cxxISolution(*rhs.initial_data); + else + this->initial_data = NULL; + } + return *this; +} +cxxSolution::cxxSolution(std::map < int, cxxSolution > &solutions, + cxxMix & mix, int l_n_user, PHRQ_io * io) +// +// constructor for cxxSolution from mixture of solutions +// +: cxxNumKeyword(io) +{ +// +// Zero out solution data +// + this->zero(); + this->n_user = this->n_user_end = l_n_user; + this->new_def = false; + this->ah2o = 0; +// +// Mix solutions +// + const std::map < int, LDBLE >&mixcomps = mix.Get_mixComps(); + std::map < int, LDBLE >::const_iterator it; + for (it = mixcomps.begin(); it != mixcomps.end(); it++) + { + std::map < int, cxxSolution >::const_iterator sol = + solutions.find(it->first); + if (sol == solutions.end()) + { + std::ostringstream msg; + msg << "Solution " << it->first << " not found in mix_cxxSolutions."; + error_msg(msg.str(), CONTINUE); + } + else + { + const cxxSolution *cxxsoln_ptr1 = &(sol->second); + this->add(*cxxsoln_ptr1, it->second); + } + } + +} +cxxSolution::~cxxSolution() +{ + delete this->initial_data; +} + +void +cxxSolution::dump_xml(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + + // Solution element and attributes + s_oss << indent0; + s_oss << "n_user << "\" " << "\n"; + + s_oss << indent1; + s_oss << "soln_description=\"" << this->description << "\"" << "\n"; + + s_oss << indent1; + s_oss << "soln_tc=\"" << this->tc << "\"" << "\n"; + + s_oss << indent1; + s_oss << "soln_ph=\"" << this->ph << "\"" << "\n"; + + s_oss << indent1; + s_oss << "soln_solution_pe=\"" << this->pe << "\"" << "\n"; + + s_oss << indent1; + s_oss << "soln_mu=\"" << this->mu << "\"" << "\n"; + + s_oss << indent1; + s_oss << "soln_ah2o=\"" << this->ah2o << "\"" << "\n"; + + s_oss << indent1; + s_oss << "soln_total_h=\"" << this->total_h << "\"" << "\n"; + + s_oss << indent1; + s_oss << "soln_total_o=\"" << this->total_o << "\"" << "\n"; + + s_oss << indent1; + s_oss << "soln_cb=\"" << this->cb << "\"" << "\n"; + + s_oss << indent1; + s_oss << "soln_mass_water=\"" << this->mass_water << "\"" << "\n"; + + s_oss << indent1; + s_oss << "soln_vol=\"" << this->soln_vol << "\"" << "\n"; + + s_oss << indent1; + s_oss << "soln_total_alkalinity=\"" << this-> + total_alkalinity << "\"" << "\n"; + + s_oss << indent1; + s_oss << "\">" << "\n"; + + // soln_total conc structures + this->totals.dump_xml(s_oss, indent + 1); + + // master_activity map + this->master_activity.dump_xml(s_oss, indent + 1); + + // species_gamma map + this->species_gamma.dump_xml(s_oss, indent + 1); + + // End of solution + s_oss << indent0; + s_oss << "" << "\n"; + + return; +} + +void +cxxSolution::dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Solution element and attributes + s_oss << indent0; + int n_user_local = (n_out != NULL) ? *n_out : this->n_user; + s_oss << "SOLUTION_RAW " << n_user_local << " " << this->description << "\n"; + + s_oss << indent1; + s_oss << "-temp " << this->tc << "\n"; + + s_oss << indent1; + s_oss << "-pressure " << this->patm << "\n"; + + s_oss << indent1; + s_oss << "-potential " << this->potV << "\n"; + + // new identifier + s_oss << indent1; + s_oss << "-total_h " << this->total_h << "\n"; + + // new identifier + s_oss << indent1; + s_oss << "-total_o " << this->total_o << "\n"; + + // new identifier + s_oss << indent1; + s_oss << "-cb " << this->cb << "\n"; + + // new identifier + s_oss << indent1; + s_oss << "-density " << this->density << "\n"; + + // soln_total conc structures + s_oss << indent1; + s_oss << "-totals" << "\n"; + this->totals.dump_raw(s_oss, indent + 2); + + // Isotopes + { + for (std::map < std::string, cxxSolutionIsotope >::const_iterator it = + this->isotopes.begin(); it != isotopes.end(); ++it) + { + s_oss << indent1 << "-Isotope" << "\n"; + it->second.dump_raw(s_oss, indent + 2); + } + } + + s_oss << indent1; + s_oss << "-pH " << this->ph << "\n"; + + s_oss << indent1; + s_oss << "-pe " << this->pe << "\n"; + + // new identifier + s_oss << indent1; + s_oss << "-mu " << this->mu << "\n"; + + // new identifier + s_oss << indent1; + s_oss << "-ah2o " << this->ah2o << "\n"; + + // new identifier + s_oss << indent1; + s_oss << "-mass_water " << this->mass_water << "\n"; + + // new identifier + s_oss << indent1; + s_oss << "-soln_vol " << this->soln_vol << "\n"; + + // new identifier + s_oss << indent1; + s_oss << "-total_alkalinity " << this->total_alkalinity << "\n"; + + // master_activity map + s_oss << indent1; + s_oss << "-activities" << "\n"; + this->master_activity.dump_raw(s_oss, indent + 2); + + // species_gamma map + s_oss << indent1; + s_oss << "-gammas" << "\n"; + this->species_gamma.dump_raw(s_oss, indent + 2); + + // species_map + if (species_map.size() > 0) + { + s_oss << indent1; + s_oss << "-species_map" << "\n"; + std::map::const_iterator it = this->species_map.begin(); + for ( ; it != species_map.end(); it++) + { + s_oss << indent2; + s_oss << it->first << " " << it->second << "\n"; + } + } + + // log_gamma_map + if (log_gamma_map.size() > 0) + { + s_oss << indent1; + s_oss << "-log_gamma_map" << "\n"; + std::map::const_iterator it = this->log_gamma_map.begin(); + for ( ; it != log_gamma_map.end(); it++) + { + s_oss << indent2; + s_oss << it->first << " " << it->second << "\n"; + } + } + return; +} + +#ifdef USE_REVISED_READ_RAW +void +cxxSolution::read_raw(CParser & parser, bool check) +{ + + // Used if it is modify + cxxNameDouble original_totals = this->totals; + cxxNameDouble original_activities(this->master_activity); + + this->master_activity.clear(); + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + // Read solution number and description + this->read_number_description(parser.line()); + this->Set_new_def(false); + + opt_save = CParser::OPT_ERROR; + bool tc_defined(false); + bool ph_defined(false); + bool pe_defined(false); + bool mu_defined(false); + bool ah2o_defined(false); + bool total_h_defined(false); + bool total_o_defined(false); + bool cb_defined(false); + bool mass_water_defined(false); + bool total_alkalinity_defined(false); + + for (;;) + { + int opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input in SOLUTION_RAW keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + continue; + + case 0: // totals + { + cxxNameDouble temp_totals; + if (temp_totals.read_raw(parser, next_char) != CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg("Expected element name and moles for totals.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->totals.merge_redox(temp_totals); + } + } + opt_save = 0; + break; + + case 1: // activities + if (this->master_activity.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected species name and log activity for activities.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 1; + break; + + case 2: // gammas + if (this->species_gamma.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected species name and activity coefficient for gammas.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 2; + break; + + case 3: // isotope + { + std::string name; + if (!(parser.get_iss() >> name)) + { + parser.incr_input_error(); + parser.error_msg("Expected character value for isotope name.", + PHRQ_io::OT_CONTINUE); + } + else + { + cxxSolutionIsotope iso(this->Get_io()); + iso.Set_isotope_name(name.c_str()); + iso.read_raw(parser, check); + this->isotopes[name] = iso; + } + } + opt_save = CParser::OPT_DEFAULT; + break; + + case 4: // temp + case 5: // tc_avoid_conflict_with_technetium + case 6: // temperature + if (!(parser.get_iss() >> this->tc)) + { + this->tc = 25.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for temperature.", + PHRQ_io::OT_CONTINUE); + } + tc_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 7: // ph + if (!(parser.get_iss() >> this->ph)) + { + this->ph = 7.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for pH.", + PHRQ_io::OT_CONTINUE); + } + ph_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 8: // pe + if (!(parser.get_iss() >> this->pe)) + { + this->pe = 4.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for pe.", + PHRQ_io::OT_CONTINUE); + } + pe_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 9: // mu + case 10: // ionic_strength + if (!(parser.get_iss() >> this->mu)) + { + this->mu = 1e-7; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for ionic strength.", + PHRQ_io::OT_CONTINUE); + } + mu_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 11: // ah2o + case 12: // activity_water + if (!(parser.get_iss() >> this->ah2o)) + { + this->ah2o = 1.0; + parser.incr_input_error(); + parser. + error_msg("Expected numeric value for activity of water.", + PHRQ_io::OT_CONTINUE); + } + ah2o_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 13: // total_h + if (!(parser.get_iss() >> this->total_h)) + { + this->total_h = 111.1; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for total hydrogen.", + PHRQ_io::OT_CONTINUE); + } + total_h_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 14: // total_o + if (!(parser.get_iss() >> this->total_o)) + { + this->total_o = 55.55; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for total oxygen.", + PHRQ_io::OT_CONTINUE); + } + total_o_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 15: // mass_water + case 16: // mass_h2o + if (!(parser.get_iss() >> this->mass_water)) + { + this->mass_water = 1.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for mass of water.", + PHRQ_io::OT_CONTINUE); + } + mass_water_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 17: // total_alkalinity + case 18: // total_alk + if (!(parser.get_iss() >> this->total_alkalinity)) + { + this->total_alkalinity = 0; + parser.incr_input_error(); + parser. + error_msg("Expected numeric value for total_alkalinity.", + PHRQ_io::OT_CONTINUE); + } + total_alkalinity_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 19: // cb + case 20: // charge_balance + if (!(parser.get_iss() >> this->cb)) + { + this->cb = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for charge balance.", + PHRQ_io::OT_CONTINUE); + } + cb_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + case 21: // density + if (!(parser.get_iss() >> this->density)) + { + this->density = 1.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for density.", + PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + break; + } + + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (check) + { + // all members must be defined + if (tc_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Temp not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (ph_defined == false) + { + parser.incr_input_error(); + parser.error_msg("pH not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (pe_defined == false) + { + parser.incr_input_error(); + parser.error_msg("pe not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (mu_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Ionic strength not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (ah2o_defined == false) + { + parser.incr_input_error(); + parser. + error_msg("Activity of water not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (total_h_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Total hydrogen not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (total_o_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Total oxygen not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (cb_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Charge balance not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (mass_water_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Temp not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (total_alkalinity_defined == false) + { + parser.incr_input_error(); + parser. + error_msg("Total alkalinity not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + } + + // Update activities + + if (original_activities.size() > 0) + { + cxxNameDouble new_activities = this->master_activity; + this->master_activity = original_activities; + this->Update_activities(original_totals); + cxxNameDouble::iterator it = new_activities.begin(); + for ( ; it != new_activities.end(); it++) + { + this->master_activity[it->first] = it->second; + } + } + return; +} +#endif +void +cxxSolution::read_raw(CParser & parser, bool check) +{ + + // Used if it is modify + cxxNameDouble simple_original_totals = this->totals.Simplify_redox(); + cxxNameDouble original_activities(this->master_activity); + + this->master_activity.clear(); + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + // Read solution number and description + this->read_number_description(parser.line()); + + opt_save = CParser::OPT_ERROR; + bool tc_defined(false); + bool ph_defined(false); + bool pe_defined(false); + bool mu_defined(false); + bool ah2o_defined(false); + bool total_h_defined(false); + bool total_o_defined(false); + bool cb_defined(false); + bool mass_water_defined(false); + bool total_alkalinity_defined(false); + + for (;;) + { + int opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input in SOLUTION_RAW keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + continue; + + case 0: // totals + { + cxxNameDouble temp_totals; + if (temp_totals.read_raw(parser, next_char) != CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg("Expected element name and moles for totals.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->totals.merge_redox(temp_totals); + } + } + opt_save = 0; + break; + + case 1: // activities + if (this->master_activity.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected species name and log activity for activities.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 1; + break; + + case 2: // gammas + if (this->species_gamma.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected species name and activity coefficient for gammas.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 2; + break; + + case 3: // isotope + { + std::string name; + if (!(parser.get_iss() >> name)) + { + parser.incr_input_error(); + parser.error_msg("Expected character value for isotope name.", + PHRQ_io::OT_CONTINUE); + } + else + { + cxxSolutionIsotope iso(this->Get_io()); + iso.Set_isotope_name(name.c_str()); + iso.read_raw(parser, check); + this->isotopes[name] = iso; + } + } + opt_save = CParser::OPT_DEFAULT; + break; + + case 4: // temp + case 5: // tc_avoid_conflict_with_technetium + case 6: // temperature + if (!(parser.get_iss() >> this->tc)) + { + this->tc = 25.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for temperature.", + PHRQ_io::OT_CONTINUE); + } + tc_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 7: // ph + if (!(parser.get_iss() >> this->ph)) + { + this->ph = 7.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for pH.", + PHRQ_io::OT_CONTINUE); + } + ph_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 8: // pe + if (!(parser.get_iss() >> this->pe)) + { + this->pe = 4.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for pe.", + PHRQ_io::OT_CONTINUE); + } + pe_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 9: // mu + case 10: // ionic_strength + if (!(parser.get_iss() >> this->mu)) + { + this->mu = 1e-7; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for ionic strength.", + PHRQ_io::OT_CONTINUE); + } + mu_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 11: // ah2o + case 12: // activity_water + if (!(parser.get_iss() >> this->ah2o)) + { + this->ah2o = 1.0; + parser.incr_input_error(); + parser. + error_msg("Expected numeric value for activity of water.", + PHRQ_io::OT_CONTINUE); + } + ah2o_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 13: // total_h + if (!(parser.get_iss() >> this->total_h)) + { + this->total_h = 111.1; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for total hydrogen.", + PHRQ_io::OT_CONTINUE); + } + total_h_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 14: // total_o + if (!(parser.get_iss() >> this->total_o)) + { + this->total_o = 55.55; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for total oxygen.", + PHRQ_io::OT_CONTINUE); + } + total_o_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 15: // mass_water + case 16: // mass_h2o + if (!(parser.get_iss() >> this->mass_water)) + { + this->mass_water = 1.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for mass of water.", + PHRQ_io::OT_CONTINUE); + } + mass_water_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 17: // total_alkalinity + case 18: // total_alk + if (!(parser.get_iss() >> this->total_alkalinity)) + { + this->total_alkalinity = 0; + parser.incr_input_error(); + parser. + error_msg("Expected numeric value for total_alkalinity.", + PHRQ_io::OT_CONTINUE); + } + total_alkalinity_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + + case 19: // cb + case 20: // charge_balance + if (!(parser.get_iss() >> this->cb)) + { + this->cb = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for charge balance.", + PHRQ_io::OT_CONTINUE); + } + cb_defined = true; + opt_save = CParser::OPT_DEFAULT; + break; + case 21: // density + if (!(parser.get_iss() >> this->density)) + { + this->density = 1.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for density.", + PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + break; + case 22: // pressure + if (!(parser.get_iss() >> this->patm)) + { + this->patm = 1.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for pressure.", + PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + break; + + case 23: // soln_vol + if (!(parser.get_iss() >> this->soln_vol)) + { + this->soln_vol = 1.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for solution volume.", + PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + break; + + case 24: // species_map + { + int s_num; + if (parser.peek_token() != CParser::TT_EMPTY) + { + if (!(parser.get_iss() >> s_num)) + { + parser.incr_input_error(); + parser.error_msg("Expected integer for species number.", + PHRQ_io::OT_CONTINUE); + } + else + { + double d; + if (!(parser.get_iss() >> d)) + { + parser.incr_input_error(); + parser.error_msg("Expected double for species concentration.", + PHRQ_io::OT_CONTINUE); + } + this->species_map[s_num] = d; + } + } + opt_save = 24; + } + break; + case 25: // log_gamma_map + { + int s_num; + if (parser.peek_token() != CParser::TT_EMPTY) + { + if (!(parser.get_iss() >> s_num)) + { + parser.incr_input_error(); + parser.error_msg("Expected integer for species number.", + PHRQ_io::OT_CONTINUE); + } + else + { + double d; + if (!(parser.get_iss() >> d)) + { + parser.incr_input_error(); + parser.error_msg("Expected double for species concentration.", + PHRQ_io::OT_CONTINUE); + } + this->log_gamma_map[s_num] = d; + } + } + opt_save = 25; + } + break; + + case 26: // potential + if (!(parser.get_iss() >> this->potV)) + { + this->potV = 0.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for potential (V).", + PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + break; + + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (check) + { + // all members must be defined + if (tc_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Temp not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (ph_defined == false) + { + parser.incr_input_error(); + parser.error_msg("pH not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (pe_defined == false) + { + parser.incr_input_error(); + parser.error_msg("pe not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (mu_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Ionic strength not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (ah2o_defined == false) + { + parser.incr_input_error(); + parser. + error_msg("Activity of water not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (total_h_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Total hydrogen not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (total_o_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Total oxygen not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (cb_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Charge balance not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (mass_water_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Temp not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (total_alkalinity_defined == false) + { + parser.incr_input_error(); + parser. + error_msg("Total alkalinity not defined for SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + } + + // Update activities + if (original_activities.size() > 0) + { + + cxxNameDouble simple_this_totals = this->totals.Simplify_redox(); + cxxNameDouble::iterator it = simple_original_totals.begin(); + for ( ; it != simple_original_totals.end(); it++) + { + cxxNameDouble::iterator jit = simple_this_totals.find(it->first); + if (jit != simple_this_totals.end()) + { + if (it->second > 0 && jit->second > 0.0) + { + LDBLE f = jit->second / it->second; + if (f != 1) + { + original_activities.Multiply_activities_redox(it->first, f); + } + } + } + } + original_activities.merge_redox(this->master_activity); + this->master_activity = original_activities; + } + + return; +} + +void +cxxSolution::Update(LDBLE h_tot, LDBLE o_tot, LDBLE charge, const cxxNameDouble &const_nd) +{ + // H, O, charge, totals, and activities of solution are updated + this->total_h = h_tot; + this->total_o = o_tot; + this->cb = charge; + this->mass_water = o_tot / 55.5; + + // Don`t bother to update activities? + this->Update(const_nd); + //this->totals = const_nd; + cxxNameDouble::iterator it; + for (it = this->totals.begin(); it != this->totals.end(); it++) + { + if (it->second < 1e-18) + { + it->second = 0.0; + } + } +} +void +cxxSolution::Update_activities(const cxxNameDouble &original_tot) +{ + // Totals and activities of solution are updated + // nd does not have H, O, charge + cxxNameDouble simple_original = original_tot.Simplify_redox(); + // totals after read + cxxNameDouble simple_new = this->totals.Simplify_redox(); + + // make factors + cxxNameDouble factors; + { + cxxNameDouble::iterator it = simple_new.begin(); + cxxNameDouble::iterator jit = simple_original.begin(); + while (it != simple_new.end() && jit != simple_original.end()) + { + int j = strcmp(it->first.c_str(), jit->first.c_str()); + if (j < 0) + { + it++; + } + else if (j == 0) + { + if (jit->second != it->second) + { + if (it->second > 0 && jit->second > 0) + { + factors[it->first] = log10(jit->second / it->second); + } + } + it++; + jit++; + } + else + { + jit++; + } + } + + + // simple_new now has factors for master activities + // Now add factors to activities + { + cxxNameDouble::iterator activity_it = this->master_activity.begin(); + cxxNameDouble::iterator factors_it = factors.begin(); + std::string activity_ename; + std::basic_string < char >::size_type indexCh; + while (activity_it != master_activity.end() && factors_it != factors.end()) + { + activity_ename = activity_it->first; + if (activity_ename.size() > 3) + { + indexCh = activity_ename.find("("); + if (indexCh != std::string::npos) + { + activity_ename = activity_ename.substr(0, indexCh); + } + } + int j = strcmp(factors_it->first.c_str(), activity_ename.c_str()); + if (j < 0) + { + factors_it++; + } + else if (j == 0) + { + activity_it->second += factors_it->second; + activity_it++; + } + else + { + activity_it++; + } + } + } + } +} +void +cxxSolution::Update(const cxxNameDouble &const_nd) +{ + // const_nd is a list of new totals, assumed to be inclusive of all elements + // Totals and activities of solution are updated + // nd does not have H, O, charge + cxxNameDouble simple_original = this->totals.Simplify_redox(); + cxxNameDouble simple_new = const_nd.Simplify_redox(); + + cxxNameDouble factors; + { + // make factors + cxxNameDouble::iterator it = simple_new.begin(); + cxxNameDouble::iterator jit = simple_original.begin(); + while (it != simple_new.end() && jit != simple_original.end()) + { + int j = strcmp(it->first.c_str(), jit->first.c_str()); + if (j < 0) + { + it++; + } + else if (j == 0) + { + if (jit->second != it->second) + { + if (it->second > 0 && jit->second > 0) + { + factors[it->first] = log10(it->second / jit->second); + } + } + it++; + jit++; + } + else + { + jit++; + } + } + // simple_new now has factors for master activities + // Now add log factors to log activities + { + cxxNameDouble::iterator activity_it = this->master_activity.begin(); + cxxNameDouble::iterator factors_it = factors.begin(); + std::string activity_ename; + std::basic_string < char >::size_type indexCh; + while (activity_it != master_activity.end() && factors_it != factors.end()) + { + activity_ename = activity_it->first; + if (factors_it->first[0] < activity_ename[0]) + { + factors_it++; + continue; + } + else if (factors_it->first[0] > activity_ename[0]) + { + activity_it++; + continue; + } + if (activity_ename.size() > 3) + { + indexCh = activity_ename.find("("); + if (indexCh != std::string::npos) + { + activity_ename = activity_ename.substr(0, indexCh); + } + } + int j = strcmp(factors_it->first.c_str(), activity_ename.c_str()); + if (j < 0) + { + factors_it++; + } + else if (j == 0) + { + activity_it->second += factors_it->second; + activity_it++; + } + else + { + activity_it++; + } + } + } + } + + // update totals + this->totals = simple_new; +} +#ifdef SKIP +void +cxxSolution::Update(const cxxNameDouble &const_nd) +{ + // const_nd is updated totals + cxxNameDouble simple_original_totals = this->totals.Simplify_redox(); + cxxNameDouble original_activities(this->master_activity); + + this->master_activity.clear(); + + // Update activities + if (original_activities.size() > 0) + { + cxxNameDouble nd = const_nd; + cxxNameDouble simple_this_totals = nd.Simplify_redox(); + cxxNameDouble::iterator it = simple_original_totals.begin(); + for ( ; it != simple_original_totals.end(); it++) + { + cxxNameDouble::iterator jit = simple_this_totals.find(it->first); + if (jit != simple_this_totals.end()) + { + if (it->second != 0) + { + LDBLE f = jit->second / it->second; + if (f != 1) + { + original_activities.Multiply_activities_redox(it->first, f); + } + } + } + } + original_activities.merge_redox(this->master_activity); + this->master_activity = original_activities; + } + + return; +} +#endif +void +cxxSolution::zero() +{ + this->tc = 0.0; + this->ph = 0.0; + this->pe = 0.0; + this->mu = 0.0; + this->ah2o = 0.0; + this->total_h = 0.0; + this->total_o = 0.0; + this->cb = 0.0; + this->density = 1.0; + this->mass_water = 0.0; + this->soln_vol = 0.0; + this->total_alkalinity = 0.0; + this->totals.type = cxxNameDouble::ND_ELT_MOLES; + this->master_activity.type = cxxNameDouble::ND_SPECIES_LA; + this->species_gamma.type = cxxNameDouble::ND_SPECIES_GAMMA; + this->patm = 1.0; + this->potV = 0.0; + this->initial_data = NULL; +} + +void +cxxSolution::add(const cxxSolution & addee, LDBLE extensive) + // + // Add existing solution to "this" solution + // +{ + if (extensive == 0.0) + return; + LDBLE ext1 = this->mass_water; + LDBLE ext2 = addee.mass_water * extensive; + LDBLE f1 = ext1 / (ext1 + ext2); + LDBLE f2 = ext2 / (ext1 + ext2); + this->tc = f1 * this->tc + f2 * addee.tc; + this->ph = f1 * this->ph + f2 * addee.ph; + this->pe = f1 * this->pe + f2 * addee.pe; + this->mu = f1 * this->mu + f2 * addee.mu; + this->ah2o = f1 * this->mu + f2 * addee.ah2o; + this->total_h += addee.total_h * extensive; + this->total_o += addee.total_o * extensive; + this->cb += addee.cb * extensive; + this->density = f1 * this->density + f2 * addee.density; + this->patm = f1 * this->patm + f2 * addee.patm; + this->potV = f1 * this->potV + f2 * addee.potV; + this->mass_water += addee.mass_water * extensive; + this->soln_vol += addee.soln_vol * extensive; + this->total_alkalinity += addee.total_alkalinity * extensive; + this->totals.add_extensive(addee.totals, extensive); + this->master_activity.add_log_activities(addee.master_activity, f1, f2); + this->species_gamma.add_intensive(addee.species_gamma, f1, f2); + this->Add_isotopes(addee.isotopes, f2, extensive); + { + // Add species + std::map::const_iterator it = addee.species_map.begin(); + for ( ; it != addee.species_map.end(); it++) + { + if (this->species_map.find(it->first) != this->species_map.end()) + { + this->species_map[it->first] = this->species_map[it->first] * f1 + it->second * f2; + } + else + { + this->species_map[it->first] = it->second; + } + } + // Add gammas + std::map::const_iterator git = addee.log_gamma_map.begin(); + for ( ; git != addee.log_gamma_map.end(); git++) + { + if (this->log_gamma_map.find(git->first) != this->log_gamma_map.end()) + { + this->log_gamma_map[git->first] = this->log_gamma_map[git->first] * f1 + git->second * f2; + } + else + { + this->log_gamma_map[git->first] = git->second; + } + } + } +} + +void +cxxSolution::multiply(LDBLE extensive) + // + // Multiply existing solution by extensive + // +{ + if (extensive == 0.0 || extensive == 1.0) + return; + this->total_h *= extensive; + this->total_o *= extensive; + this->cb *= extensive; + this->mass_water *= extensive; + this->soln_vol *= extensive; + this->total_alkalinity *= extensive; + this->totals.multiply(extensive); + this->Multiply_isotopes(extensive); +} + +LDBLE +cxxSolution::Get_total(const char *string) const +{ + cxxNameDouble::const_iterator it = this->totals.find(string); + if (it == this->totals.end()) + { + return (0.0); + } + else + { + return (it->second); + } +} +#ifdef SKIP +LDBLE +cxxSolution::Get_total_element(const char *string) const +{ + cxxNameDouble::const_iterator it; + LDBLE d = 0.0; + for (it = this->totals.begin(); it != this->totals.end(); ++it) + { + // C++ way to do it + std::string ename(string); + std::string current_ename(it->first); + std::basic_string < char >::size_type indexCh; + indexCh = current_ename.find("("); + if (indexCh != std::string::npos) + { + current_ename = current_ename.substr(0, indexCh); + } + if (current_ename == ename) + { + d += it->second; + } + } + return (d); +} +#endif + +void +cxxSolution::Set_total(char *string, LDBLE d) +{ + this->totals[string] = d; +} + +LDBLE +cxxSolution::Get_master_activity(char *string) const +{ + cxxNameDouble::const_iterator it = this->master_activity.find(string); + if (it == this->master_activity.end()) + { + return (0.0); + } + else + { + return (it->second); + } +} + +void +cxxSolution::Set_master_activity(char *string, LDBLE d) +{ + this->master_activity[string] = d; +} +void +cxxSolution::Add_isotopes(const std::map < std::string, cxxSolutionIsotope > & old, LDBLE intensive, LDBLE extensive) +{ + for (std::map < std::string, cxxSolutionIsotope >::const_iterator itold = old.begin(); itold != old.end(); ++itold) + { + std::map < std::string, cxxSolutionIsotope >::iterator it_this; + it_this = this->isotopes.find(itold->first); + if (it_this != this->isotopes.end()) + { + LDBLE t = it_this->second.Get_total(); + t += itold->second.Get_total() * extensive; + it_this->second.Set_total(t); + + t = it_this->second.Get_ratio(); + t += itold->second.Get_ratio() * intensive; + it_this->second.Set_ratio(t); + + t = it_this->second.Get_ratio_uncertainty(); + t += itold->second.Get_ratio_uncertainty() * intensive; + it_this->second.Set_ratio_uncertainty(t); + it_this->second.Set_ratio_uncertainty_defined(it_this->second.Get_ratio_uncertainty_defined() + || itold->second.Get_ratio_uncertainty_defined()); + } + else + { + cxxSolutionIsotope iso(itold->second); + iso.Set_total(itold->second.Get_total() * extensive); + this->Get_isotopes()[iso.Get_isotope_name()] = iso; + } + } +} +void +cxxSolution::Multiply_isotopes(LDBLE extensive) +{ + std::map < std::string, cxxSolutionIsotope>::iterator it; + for (it = this->isotopes.begin(); it != this->isotopes.end(); it++) + { + LDBLE total = it->second.Get_total(); + total *= extensive; + it->second.Set_total(total); + } +} + +/* ---------------------------------------------------------------------- */ +void +cxxSolution::Serialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles) +/* ---------------------------------------------------------------------- */ +{ +/* + * Make list of list of ints and doubles from solution structure + * This list is not the complete structure, but only enough + * for batch-reaction, advection, and transport calculations + */ + ints.push_back(this->n_user); + ints.push_back(this->new_def ? 1 : 0); + doubles.push_back(this->patm); + doubles.push_back(this->potV); + doubles.push_back(this->tc); + doubles.push_back(this->ph); + doubles.push_back(this->pe); + doubles.push_back(this->mu); + doubles.push_back(this->ah2o); + doubles.push_back(this->total_h); + doubles.push_back(this->total_o); + doubles.push_back(this->cb); + doubles.push_back(this->mass_water); + doubles.push_back(this->density); + doubles.push_back(this->soln_vol); + doubles.push_back(this->total_alkalinity); +/* + * struct conc *totals; +*/ + this->totals.Serialize(dictionary, ints, doubles); +/* + * struct master_activity *master_activity; + */ + this->master_activity.Serialize(dictionary, ints, doubles); +/* + * struct master_activity *species_gamma + */ + this->species_gamma.Serialize(dictionary, ints, doubles); +/* + * isotopes + */ + ints.push_back((int) isotopes.size()); + { + std::map < std::string, cxxSolutionIsotope >::iterator it; + for (it = isotopes.begin(); it != isotopes.end(); it++) + { + ints.push_back(dictionary.Find(it->first)); + it->second.Serialize(dictionary, ints, doubles); + } + } +/* + * species_map + */ + ints.push_back((int) species_map.size()); + { + std::map < int, double >::iterator it; + for (it = species_map.begin(); it != species_map.end(); it++) + { + ints.push_back(it->first); + doubles.push_back(it->second); + } + } +/* + * log_gamma_map + */ + ints.push_back((int) log_gamma_map.size()); + { + std::map < int, double >::iterator it; + for (it = log_gamma_map.begin(); it != log_gamma_map.end(); it++) + { + ints.push_back(it->first); + doubles.push_back(it->second); + } + } +} + +/* ---------------------------------------------------------------------- */ +void +cxxSolution::Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd) +/* ---------------------------------------------------------------------- */ +{ + this->n_user = ints[ii++]; + this->n_user_end = this->n_user; + this->description = " "; + + this->new_def = (ints[ii++] != 0); + this->patm = doubles[dd++]; + this->potV = doubles[dd++]; + this->tc = doubles[dd++]; + this->ph = doubles[dd++]; + this->pe = doubles[dd++]; + this->mu = doubles[dd++]; + this->ah2o = doubles[dd++]; + this->total_h = doubles[dd++]; + this->total_o = doubles[dd++]; + this->cb = doubles[dd++]; + this->mass_water = doubles[dd++]; + this->density = doubles[dd++]; + this->soln_vol = doubles[dd++]; + this->total_alkalinity = doubles[dd++]; +/* + * struct conc *totals; +*/ + this->totals.Deserialize(dictionary, ints, doubles, ii, dd); +/* + * struct master_activity *master_activity; + */ + this->master_activity.Deserialize(dictionary, ints, doubles, ii, dd); +/* + * struct master_activity *species_gamma; + */ + this->species_gamma.Deserialize(dictionary, ints, doubles, ii, dd); +/* + * isotopes + */ + { + isotopes.clear(); + int n = ints[ii++]; + for (int i = 0; i < n; i++) + { + std::string str = dictionary.GetWords()[ints[ii++]]; + cxxSolutionIsotope iso; + iso.Deserialize(dictionary, ints, doubles, ii, dd); + isotopes[str] = iso; + } + } +/* + * species_map + */ + { + species_map.clear(); + int n = ints[ii++]; + for (int i = 0; i < n; i++) + { + species_map[ints[ii++]] = doubles[dd++]; + } + } +/* + * log_gamma_map + */ + { + log_gamma_map.clear(); + int n = ints[ii++]; + for (int i = 0; i < n; i++) + { + log_gamma_map[ints[ii++]] = doubles[dd++]; + } + } +} + + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("totals"), // 0 + std::vector< std::string >::value_type("activities"), // 1 + std::vector< std::string >::value_type("gammas"), // 2 + std::vector< std::string >::value_type("isotopes"), // 3 + std::vector< std::string >::value_type("temp"), // 4 + std::vector< std::string >::value_type("tc_avoid_conflict_with_technetium"), // 5 + std::vector< std::string >::value_type("temperature"), // 6 + std::vector< std::string >::value_type("ph"), // 7 + std::vector< std::string >::value_type("pe"), // 8 + std::vector< std::string >::value_type("mu"), // 9 + std::vector< std::string >::value_type("ionic_strength"), // 10 + std::vector< std::string >::value_type("ah2o"), // 11 + std::vector< std::string >::value_type("activity_water"), // 12 + std::vector< std::string >::value_type("total_h"), // 13 + std::vector< std::string >::value_type("total_o"), // 14 + std::vector< std::string >::value_type("mass_water"), // 15 + std::vector< std::string >::value_type("mass_h2o"), // 16 + std::vector< std::string >::value_type("total_alkalinity"), // 17 + std::vector< std::string >::value_type("total_alk"), // 18 + std::vector< std::string >::value_type("cb"), // 19 + std::vector< std::string >::value_type("charge_balance"), // 20 + std::vector< std::string >::value_type("density"), // 21 + std::vector< std::string >::value_type("pressure"), // 22 + std::vector< std::string >::value_type("soln_vol"), // 23 + std::vector< std::string >::value_type("species_map"), // 24 + std::vector< std::string >::value_type("log_gamma_map"), // 25 + std::vector< std::string >::value_type("potential") // 26 +}; +const std::vector< std::string > cxxSolution::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); \ No newline at end of file diff --git a/phreeqcpp/Solution.h b/phreeqcpp/Solution.h new file mode 100644 index 00000000..1ab18654 --- /dev/null +++ b/phreeqcpp/Solution.h @@ -0,0 +1,146 @@ +#if !defined(SOLUTION_H_INCLUDED) +#define SOLUTION_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::vector +#include +#include "NumKeyword.h" +#include "SolutionIsotope.h" +#include "NameDouble.h" +#include "PHRQ_base.h" +#include "PHRQ_io.h" +#include "ISolution.h" +class cxxMix; + +class cxxSolution:public cxxNumKeyword +{ + + public: + cxxSolution(PHRQ_io *io=NULL); + cxxSolution(const cxxSolution &old_sol); + const cxxSolution & operator = (const cxxSolution &rhs); + cxxSolution(std::map < int, cxxSolution > &solution_map, + cxxMix & mx, int n_user, PHRQ_io *io=NULL); + virtual ~cxxSolution(); + + bool Get_new_def() const {return this->new_def;} + void Set_new_def(bool p) {this->new_def = p;} + LDBLE Get_patm() const {return this->patm;} + void Set_patm(LDBLE p) {this->patm = p;} + LDBLE Get_potV() const {return this->potV;} + void Set_potV(LDBLE p) {this->potV = p;} + LDBLE Get_tc() const {return this->tc;} + void Set_tc(LDBLE l_tc) {this->tc = l_tc;} + LDBLE Get_ph() const {return this->ph;} + void Set_ph(LDBLE pH) {this->ph = pH;} + LDBLE Get_pe() const {return this->pe;} + void Set_pe(LDBLE l_pe) {this->pe = l_pe;} + LDBLE Get_mu() const {return this->mu;} + void Set_mu(LDBLE l_mu) {this->mu = l_mu;} + LDBLE Get_ah2o() const {return this->ah2o;} + void Set_ah2o(LDBLE l_ah2o) {this->ah2o = l_ah2o;} + LDBLE Get_total_h() const {return this->total_h;} + void Set_total_h(LDBLE l_total_h) {this->total_h = l_total_h;} + LDBLE Get_total_o() const {return this->total_o;} + void Set_total_o(LDBLE l_total_o) {this->total_o = l_total_o;} + LDBLE Get_cb() const {return this->cb;} + void Set_cb(LDBLE l_cb) {this->cb = l_cb;} + LDBLE Get_density() const {return this->density;} + void Set_density(LDBLE l_density) {this->density = l_density;} + LDBLE Get_mass_water() const {return this->mass_water;} + void Set_mass_water(LDBLE l_mass_water) {this->mass_water = l_mass_water;} + LDBLE Get_total_alkalinity() const {return this->total_alkalinity;} + void Set_total_alkalinity(LDBLE l_total_alkalinity) {this->total_alkalinity = l_total_alkalinity;} + LDBLE Get_soln_vol() const {return this->soln_vol;} + void Set_soln_vol(LDBLE t) {this->soln_vol = t;} + cxxNameDouble & Get_totals(void) {return this->totals;} + const cxxNameDouble & Get_totals(void)const {return this->totals;} + void Set_totals(cxxNameDouble & nd) + { + this->totals = nd; + this->totals.type = cxxNameDouble::ND_ELT_MOLES; + } + cxxNameDouble & Get_master_activity(void) {return this->master_activity;} + cxxNameDouble & Get_species_gamma(void) {return this->species_gamma;} + std::map & Get_species_map(void) {return this->species_map;} + std::map & Get_log_gamma_map(void) {return this->log_gamma_map;} + std::map < std::string, cxxSolutionIsotope > & Get_isotopes(void) {return this->isotopes;} + const std::map < std::string, cxxSolutionIsotope > & Get_isotopes(void)const {return this->isotopes;} + void Set_isotopes(const std::map < std::string, cxxSolutionIsotope > &iso ) {this->isotopes = iso;} + cxxISolution *Get_initial_data() {return this->initial_data;} + const cxxISolution *Get_initial_data()const {return this->initial_data;} + void Set_initial_data(const cxxISolution * id) + { + if (this->initial_data != NULL) + delete this->initial_data; + this->initial_data = new cxxISolution(*id); + } + void Create_initial_data() + { + if (this->initial_data != NULL) + delete initial_data; + initial_data = new cxxISolution; + } + void Destroy_initial_data() + { + if (this->initial_data != NULL) + delete initial_data; + initial_data = NULL; + } + + void clear_totals() {this->totals.clear();} + void clear_master_activity() {this->master_activity.clear();} + + void zero(); + void add(const cxxSolution & addee, LDBLE extensive); + void Add_isotopes(const std::map < std::string, cxxSolutionIsotope > &old, LDBLE intensive, LDBLE extensive); + void multiply(LDBLE extensive); + void Multiply_isotopes(LDBLE extensive); + // not checked + void dump_xml(std::ostream & os, unsigned int indent = 0) const; + void dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out=NULL) const; + + LDBLE Get_master_activity(char *string) const; + void Set_master_activity(char *string, LDBLE value); + LDBLE Get_total(const char *string) const; + LDBLE Get_total_element(const char *string) const; + void Set_total(char *string, LDBLE value); + void read_raw(CParser & parser, bool check = true); + //void modify_activities(const cxxSolution & original); + //void Simplify_totals(); + void Update(const cxxNameDouble &nd); + void Update(LDBLE h_tot, LDBLE o_tot, LDBLE charge, const cxxNameDouble &nd); + void Update_activities(const cxxNameDouble &original_tot); + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + + protected: + bool new_def; + LDBLE patm; + LDBLE potV; + LDBLE tc; + LDBLE ph; + LDBLE pe; + LDBLE mu; + LDBLE ah2o; + LDBLE total_h; + LDBLE total_o; + LDBLE cb; + LDBLE mass_water; + LDBLE density; + LDBLE soln_vol; + LDBLE total_alkalinity; + cxxNameDouble totals; + cxxNameDouble master_activity; + cxxNameDouble species_gamma; + //cxxSolutionIsotopeList isotopes; + std::map < std::string, cxxSolutionIsotope > isotopes; + cxxISolution *initial_data; + const static std::vector < std::string > vopts; + std::map species_map; + std::map log_gamma_map; +}; + +#endif // !defined(SOLUTION_H_INCLUDED) diff --git a/phreeqcpp/SolutionIsotope.cxx b/phreeqcpp/SolutionIsotope.cxx new file mode 100644 index 00000000..d37c90c7 --- /dev/null +++ b/phreeqcpp/SolutionIsotope.cxx @@ -0,0 +1,329 @@ +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include +#include // std::ostrstream + +#include "Utils.h" +#include "Phreeqc.h" +#include "SolutionIsotope.h" +#include "phqalloc.h" +#include "Dictionary.h" + +cxxSolutionIsotope::cxxSolutionIsotope(PHRQ_io *io) +: +PHRQ_base(io), +isotope_number(0.0) +{ + isotope_number = 0; + elt_name.clear(); + isotope_name.clear(); + total = 0; + ratio = -9999.9; + ratio_uncertainty = 1; + ratio_uncertainty_defined = false; + x_ratio_uncertainty = 0; + coef = 0; +} +cxxSolutionIsotope::~cxxSolutionIsotope(void) +{ +} + +void +cxxSolutionIsotope::dump_xml(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + + std::string indent0(""), indent1(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + + s_oss << indent0; + s_oss << " + isotope_number << "\"" << "\n"; + + s_oss << indent1; + s_oss << "iso_elt_name=\"" << this->elt_name << "\"" << "\n"; + + s_oss << indent1; + s_oss << "iso_isotope_name=\"" << this->isotope_name << "\"" << "\n"; + + s_oss << indent1; + s_oss << "iso_total=\"" << this->total << "\"" << "\n"; + + s_oss << indent1; + s_oss << "iso_ratio=\"" << this->ratio << "\"" << "\n"; + +#ifdef NPP + if (!isnan(this->ratio_uncertainty)) +#else + if (this->ratio_uncertainty != NAN) +#endif + { + s_oss << indent1; + s_oss << "iso_ratio_uncertainty=\"" << this-> + ratio_uncertainty << "\"" << "\n"; + } + s_oss << indent0; + s_oss << "\">" << "\n"; +} + +void +cxxSolutionIsotope::dump_raw(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + + std::string indent0(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + std::string indent1 = indent0; + indent1.append(Utilities::INDENT); + + s_oss << indent0; + s_oss << indent0 << "-isotope_number " << this->isotope_number << "\n"; + s_oss << indent0 << "-elt_name " << this->elt_name << "\n"; + s_oss << indent0 << "-total " << this->total << "\n"; + s_oss << indent0 << "-ratio " << this->ratio << "\n"; + s_oss << indent0 << "-ratio_uncertainty_defined " << this->ratio_uncertainty_defined << "\n"; + if (this->ratio_uncertainty_defined) + { + s_oss << indent0 << "-ratio_uncertainty " << this->ratio_uncertainty << "\n"; + } + s_oss << indent0 << "-x_ratio_uncertainty " << this->x_ratio_uncertainty << "\n"; + s_oss << indent0 << "-coef " << this->coef << "\n"; +} +void cxxSolutionIsotope::read_raw(CParser & parser, bool check ) +{ + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + opt_save = CParser::OPT_ERROR; + bool isotope_number_defined(false); + bool elt_name_defined(false); + bool total_defined(false); + bool ratio_defined(false); + + for (;;) + { + int opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + + opt_save = CParser::OPT_DEFAULT; + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input in isotopes of SOLUTION_RAW keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + continue; + + case 0: // isotope_number + if (!(parser.get_iss() >> this->isotope_number)) + { + this->isotope_number = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for isotope_number.", + PHRQ_io::OT_CONTINUE); + } + isotope_number_defined = true; + break; + + case 1: // elt_name + if (!(parser.get_iss() >> this->elt_name)) + { + this->elt_name.clear(); + parser.incr_input_error(); + parser.error_msg("Expected character value for elt_name.", + PHRQ_io::OT_CONTINUE); + } + elt_name_defined = true; + break; + + case 2: // total + if (!(parser.get_iss() >> this->total)) + { + this->total = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for total.", + PHRQ_io::OT_CONTINUE); + } + total_defined = true; + break; + + case 3: // ratio + if (!(parser.get_iss() >> this->ratio)) + { + this->ratio = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for ratio.", + PHRQ_io::OT_CONTINUE); + } + total_defined = true; + break; + + case 4: // ratio_uncertainty_defined + if (!(parser.get_iss() >> this->ratio_uncertainty_defined)) + { + this->ratio_uncertainty_defined = 0; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for ratio_uncertainty_defined.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 5: // ratio_uncertainty + if (!(parser.get_iss() >> this->ratio_uncertainty)) + { + this->ratio_uncertainty = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for ratio_uncertainty.", + PHRQ_io::OT_CONTINUE); + } + ratio_uncertainty_defined = true; + break; + + case 6: // x_ratio_uncertainty + if (!(parser.get_iss() >> this->x_ratio_uncertainty)) + { + this->x_ratio_uncertainty = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for x_ratio_uncertainty.", + PHRQ_io::OT_CONTINUE); + } + break; + case 7: // coef + if (!(parser.get_iss() >> this->coef)) + { + this->coef = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for coef.", + PHRQ_io::OT_CONTINUE); + } + break; + } + + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (check) + { + // all members must be defined + if (isotope_number_defined == false) + { + parser.incr_input_error(); + parser.error_msg("isotope_number not defined for isotopes SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (elt_name_defined == false) + { + parser.incr_input_error(); + parser.error_msg("elt_name not defined for isotopes SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (total_defined == false) + { + parser.incr_input_error(); + parser.error_msg("total not defined for isotopes SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (ratio_defined == false) + { + parser.incr_input_error(); + parser.error_msg("ratio not defined for isotopes SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (ratio_uncertainty_defined == false) + { + parser.incr_input_error(); + parser. + error_msg("ratio_uncertainty not defined for isotopes SOLUTION_RAW input.", + PHRQ_io::OT_CONTINUE); + } + } + return; +} +bool +cxxSolutionIsotope::operator<(const cxxSolutionIsotope & isotope) const +{ + int i = Utilities::strcmp_nocase(this->elt_name.c_str(), isotope.elt_name.c_str()); + if (i != 0) + return (i < 0); + return (this->isotope_number < isotope.isotope_number); +} + +void +cxxSolutionIsotope::add(const cxxSolutionIsotope & isotope_ptr, + LDBLE intensive, LDBLE extensive) +{ + if ((this->isotope_number == isotope_ptr.isotope_number) && + (this->elt_name == isotope_ptr.elt_name) && + (this->isotope_name == isotope_ptr.isotope_name)) + { + this->total += isotope_ptr.total * extensive; + this->ratio += isotope_ptr.ratio * intensive; + this->ratio_uncertainty += isotope_ptr.ratio_uncertainty * intensive; + this->ratio_uncertainty_defined = (this->ratio_uncertainty_defined + || isotope_ptr. + ratio_uncertainty_defined); + } +} +void +cxxSolutionIsotope::multiply(LDBLE extensive) +{ + this->total *= extensive; +} +void +cxxSolutionIsotope::Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles) +{ + doubles.push_back(this->isotope_number); + ints.push_back(dictionary.Find(this->elt_name)); + ints.push_back(dictionary.Find(this->isotope_name)); + doubles.push_back(this->total); + doubles.push_back(this->ratio); + doubles.push_back(this->ratio_uncertainty); + ints.push_back(this->ratio_uncertainty_defined ? 1 : 0); + doubles.push_back(this->x_ratio_uncertainty); + doubles.push_back(this->coef); +} +void +cxxSolutionIsotope::Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd) +{ + this->isotope_number = doubles[dd++]; + this->elt_name = dictionary.GetWords()[ints[ii++]]; + this->isotope_name = dictionary.GetWords()[ints[ii++]]; + this->total = doubles[dd++]; + this->ratio = doubles[dd++]; + this->ratio_uncertainty = doubles[dd++]; + this->ratio_uncertainty_defined = (ints[ii++] != 0); + this->x_ratio_uncertainty = doubles[dd++]; + this->coef = doubles[dd++]; +} + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("isotope_number"), // 0 + std::vector< std::string >::value_type("elt_name"), // 1 + std::vector< std::string >::value_type("total"), // 2 + std::vector< std::string >::value_type("ratio"), // 3 + std::vector< std::string >::value_type("ratio_uncertainty_defined"), // 4 + std::vector< std::string >::value_type("ratio_uncertainty"), // 5 + std::vector< std::string >::value_type("x_ratio_uncertainty"), // 6 + std::vector< std::string >::value_type("coef") // 7 +}; +const std::vector< std::string > cxxSolutionIsotope::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/SolutionIsotope.h b/phreeqcpp/SolutionIsotope.h new file mode 100644 index 00000000..231fe0ab --- /dev/null +++ b/phreeqcpp/SolutionIsotope.h @@ -0,0 +1,85 @@ +#if !defined(SOLUTIONISOTOPE_H_INCLUDED) +#define SOLUTIONISOTOPE_H_INCLUDED + +#include // std::ostream +#include // std::string +#include // std::list +#include "Parser.h" +class Dictionary; + +class cxxSolutionIsotope: public PHRQ_base +{ + public: + cxxSolutionIsotope(PHRQ_io *io=NULL); + cxxSolutionIsotope(struct isotope *isotope_ptr, PHRQ_io *io=NULL); + virtual ~cxxSolutionIsotope(void); + + void dump_xml(std::ostream & os, unsigned int indent) const; + void dump_raw(std::ostream & os, unsigned int indent) const; + + //CParser::STATUS_TYPE read_raw(CParser & parser, std::istream::pos_type next_char); + void read_raw(CParser & parser, bool check); + + LDBLE Get_isotope_number() const + { + return this->isotope_number; + } + void Set_isotope_number(LDBLE d) + { + this->isotope_number = d; + } + const std::string &Get_elt_name() const {return this->elt_name;} + void Set_elt_name(const char *cstring) + { + if (cstring != NULL) + this->elt_name = std::string(cstring); + else + this->elt_name.clear(); + } + + const std::string &Get_isotope_name() const {return this->isotope_name;} + void Set_isotope_name(const char *cstring) + { + if (cstring != NULL) + this->isotope_name = std::string(cstring); + else + this->isotope_name.clear(); + } + + LDBLE Get_total() const {return this->total;} + void Set_total(LDBLE d) {this->total = d;} + + LDBLE Get_ratio() const {return this->ratio;} + void Set_ratio(LDBLE t) {this->ratio = t;} + + LDBLE Get_ratio_uncertainty() const {return this->ratio_uncertainty;} + void Set_ratio_uncertainty(LDBLE t) {this->ratio_uncertainty = t;} + + LDBLE Get_x_ratio_uncertainty() const {return this->x_ratio_uncertainty;} + void Set_x_ratio_uncertainty(LDBLE t) {this->x_ratio_uncertainty = t;} + + bool Get_ratio_uncertainty_defined() const {return this->ratio_uncertainty_defined;} + void Set_ratio_uncertainty_defined(bool tf) {this->ratio_uncertainty_defined = tf;} + LDBLE Get_coef() const {return this->coef;} + void Set_coef(LDBLE t) {this->coef = t;} + + bool operator<(const cxxSolutionIsotope & conc) const; + void add(const cxxSolutionIsotope & isotope_ptr, LDBLE intensive, + LDBLE extensive); + void multiply(LDBLE extensive); + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + + protected: + LDBLE isotope_number; + std::string elt_name; + std::string isotope_name; + LDBLE total; + LDBLE ratio; + LDBLE ratio_uncertainty; + bool ratio_uncertainty_defined; + LDBLE x_ratio_uncertainty; + LDBLE coef; /* coefficient of element in phase */ + const static std::vector < std::string > vopts; +}; +#endif // SOLUTIONISOTOPE_H_INCLUDED diff --git a/phreeqcpp/StorageBin.cxx b/phreeqcpp/StorageBin.cxx new file mode 100644 index 00000000..efe3ffb2 --- /dev/null +++ b/phreeqcpp/StorageBin.cxx @@ -0,0 +1,1566 @@ +// StorageBin.cxx: implementation of the cxxStorageBin class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include +#include // std::cout std::cerr +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "NameDouble.h" +#include "StorageBin.h" +#include "SSassemblage.h" +#include "Solution.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "cxxKinetics.h" +#include "PPassemblage.h" +#include "SS.h" +#include "SSassemblage.h" +#include "Surface.h" +#include "cxxMix.h" +#include "Reaction.h" +#include "Temperature.h" +#include "phqalloc.h" +#include "Use.h" + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// +cxxStorageBin::cxxStorageBin(PHRQ_io *io) +: +PHRQ_base(io) +{ + // default constructor for cxxStorageBin + this->system.Set_io(io); + this->system.Initialize(); +} + +cxxStorageBin::cxxStorageBin(cxxUse &use_ref, PHRQ_io *io) +: +PHRQ_base(io) +{ + this->system.Set_io(io); + this->system.Initialize(); + // Solution + if (use_ref.Get_solution_ptr() != NULL) + { + this->Set_Solution(use_ref.Get_solution_ptr()->Get_n_user(), use_ref.Get_solution_ptr()); + } + + // Exchange + if (use_ref.Get_exchange_ptr() != NULL) + { + this->Set_Exchange(use_ref.Get_exchange_ptr()->Get_n_user(), use_ref.Get_exchange_ptr()); + } + + // gas_phase + if (use_ref.Get_gas_phase_ptr() != NULL) + { + this->Set_GasPhase(use_ref.Get_gas_phase_ptr()->Get_n_user(), use_ref.Get_gas_phase_ptr()); + } + + // kinetics + if (use_ref.Get_kinetics_ptr() != NULL) + { + this->Set_Kinetics(use_ref.Get_kinetics_ptr()->Get_n_user(), use_ref.Get_kinetics_ptr()); + } + + // pp_assemblage + if (use_ref.Get_pp_assemblage_ptr() != NULL) + { + this->Set_PPassemblage(use_ref.Get_pp_assemblage_ptr()->Get_n_user(), use_ref.Get_pp_assemblage_ptr()); + } + + // ss_assemblage + if (use_ref.Get_ss_assemblage_ptr() != NULL) + { + this->Set_SSassemblage(use_ref.Get_ss_assemblage_ptr()->Get_n_user(), use_ref.Get_ss_assemblage_ptr()); + } + + // surface + if (use_ref.Get_surface_ptr() != NULL) + { + this->Set_Surface(use_ref.Get_surface_ptr()->Get_n_user(), use_ref.Get_surface_ptr()); + } + + // mix + if (use_ref.Get_mix_ptr() != NULL) + { + this->Set_Mix(use_ref.Get_mix_ptr()->Get_n_user(), use_ref.Get_mix_ptr()); + } + + // reaction + if (use_ref.Get_reaction_ptr() != NULL) + { + this->Set_Reaction(use_ref.Get_reaction_ptr()->Get_n_user(), use_ref.Get_reaction_ptr()); + } + + // reaction temperature + if (use_ref.Get_temperature_ptr() != NULL) + { + this->Set_Temperature(use_ref.Get_temperature_ptr()->Get_n_user(), use_ref.Get_temperature_ptr()); + } + + // reaction pressure + if (use_ref.Get_pressure_ptr() != NULL) + { + this->Set_Pressure(use_ref.Get_pressure_ptr()->Get_n_user(), use_ref.Get_pressure_ptr()); + } +} +cxxStorageBin::~cxxStorageBin() +{ +} +void +cxxStorageBin::Add(cxxStorageBin &src, int n) +{ + // Solution + if (src.Get_Solution(n) != NULL) + { + this->Set_Solution(n, src.Get_Solution(n)); + } + + // Exchange + if (src.Get_Exchange(n) != NULL) + { + this->Set_Exchange(n, src.Get_Exchange(n)); + } + + // gas_phase + if (src.Get_GasPhase(n) != NULL) + { + this->Set_GasPhase(n, src.Get_GasPhase(n)); + } + + // kinetics + if (src.Get_Kinetics(n) != NULL) + { + this->Set_Kinetics(n, src.Get_Kinetics(n)); + } + + // pp_assemblage + if (src.Get_PPassemblage(n) != NULL) + { + this->Set_PPassemblage(n, src.Get_PPassemblage(n)); + } + + // ss_assemblage + if (src.Get_SSassemblage(n) != NULL) + { + this->Set_SSassemblage(n, src.Get_SSassemblage(n)); + } + + // surface + if (src.Get_Surface(n) != NULL) + { + this->Set_Surface(n, src.Get_Surface(n)); + } + + // mix + if (src.Get_Mix(n) != NULL) + { + this->Set_Mix(n, src.Get_Mix(n)); + } + + // reaction + if (src.Get_Reaction(n) != NULL) + { + this->Set_Reaction(n, src.Get_Reaction(n)); + } + + // reaction temperature + if (src.Get_Temperature(n) != NULL) + { + this->Set_Temperature(n, src.Get_Temperature(n)); + } + + // reaction pressure + if (src.Get_Pressure(n) != NULL) + { + this->Set_Pressure(n, src.Get_Pressure(n)); + } +} +void +cxxStorageBin::Copy(int destination, int source) +{ + if (destination == source) + return; + this->Remove(destination); + // Solution + { + std::map < int, cxxSolution >::iterator it = this->Solutions.find(source); + if (it != this->Solutions.end()) + { + this->Set_Solution(destination, &(it->second)); + } + } + + // Exchange + { + std::map < int, cxxExchange >::iterator it = this->Exchangers.find(source); + if (it != this->Exchangers.end()) + { + this->Set_Exchange(destination, &(it->second)); + } + } + + // gas_phase + { + std::map < int, cxxGasPhase >::iterator it = this->GasPhases.find(source); + if (it != this->GasPhases.end()) + { + this->Set_GasPhase(destination, &(it->second)); + } + } + // kinetics + { + std::map < int, cxxKinetics >::iterator it = this->Kinetics.find(source); + if (it != this->Kinetics.end()) + { + this->Set_Kinetics(destination, &(it->second)); + } + } + // pp_assemblage + { + std::map < int, cxxPPassemblage >::iterator it = this->PPassemblages.find(source); + if (it != this->PPassemblages.end()) + { + this->Set_PPassemblage(destination, &(it->second)); + } + } + // ss_assemblage + { + std::map < int, cxxSSassemblage >::iterator it = this->SSassemblages.find(source); + if (it != this->SSassemblages.end()) + { + this->Set_SSassemblage(destination, &(it->second)); + } + } + // surface + { + std::map < int, cxxSurface >::iterator it = this->Surfaces.find(source); + if (it != this->Surfaces.end()) + { + this->Set_Surface(destination, &(it->second)); + } + } + // mix + { + std::map < int, cxxMix >::iterator it = this->Mixes.find(source); + if (it != this->Mixes.end()) + { + this->Set_Mix(destination, &(it->second)); + } + } + // reaction + { + std::map < int, cxxReaction >::iterator it = this->Reactions.find(source); + if (it != this->Reactions.end()) + { + this->Set_Reaction(destination, &(it->second)); + } + } + // reaction temperature + { + std::map < int, cxxTemperature >::iterator it = this->Temperatures.find(source); + if (it != this->Temperatures.end()) + { + this->Set_Temperature(destination, &(it->second)); + } + } + + // reaction pressure + { + this->Set_Pressure(destination, Utilities::Rxn_find(this->Pressures, source)); + } +} + +cxxSolution * +cxxStorageBin::Get_Solution(int n_user) +{ + if (this->Solutions.find(n_user) != this->Solutions.end()) + { + return (&(this->Solutions.find(n_user)->second)); + } + return (NULL); +} +void +cxxStorageBin::Set_Solution(int n_user, cxxSolution * entity) +{ + if (entity == NULL) + return; + Solutions[n_user] = *entity; + Solutions.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Set_Solution(int n_user, cxxSolution & entity) +{ + Solutions[n_user] = entity; + Solutions.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Remove_Solution(int n_user) +{ + Solutions.erase(n_user); +} + +cxxExchange * +cxxStorageBin::Get_Exchange(int n_user) +{ + if (this->Exchangers.find(n_user) != this->Exchangers.end()) + { + return (&(this->Exchangers.find(n_user)->second)); + } + return (NULL); +} +void +cxxStorageBin::Set_Exchange(int n_user, cxxExchange * entity) +{ + if (entity == NULL) + return; + Exchangers[n_user] = *entity; + Exchangers.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Set_Exchange(int n_user, cxxExchange & entity) +{ + Exchangers[n_user] = entity; + Exchangers.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Remove_Exchange(int n_user) +{ + Exchangers.erase(n_user); +} + +cxxPPassemblage * +cxxStorageBin::Get_PPassemblage(int n_user) +{ + if (this->PPassemblages.find(n_user) != this->PPassemblages.end()) + { + return (&(this->PPassemblages.find(n_user)->second)); + } + return (NULL); +} +void +cxxStorageBin::Set_PPassemblage(int n_user, cxxPPassemblage * entity) +{ + if (entity == NULL) + return; + PPassemblages[n_user] = *entity; + PPassemblages.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Set_PPassemblage(int n_user, cxxPPassemblage & entity) +{ + PPassemblages[n_user] = entity; + PPassemblages.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Remove_PPassemblage(int n_user) +{ + PPassemblages.erase(n_user); +} + +cxxGasPhase * +cxxStorageBin::Get_GasPhase(int n_user) +{ + if (this->GasPhases.find(n_user) != this->GasPhases.end()) + { + return (&(this->GasPhases.find(n_user)->second)); + } + return (NULL); +} +void +cxxStorageBin::Set_GasPhase(int n_user, cxxGasPhase * entity) +{ + if (entity == NULL) + return; + GasPhases[n_user] = *entity; + GasPhases.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Set_GasPhase(int n_user, cxxGasPhase & entity) +{ + GasPhases[n_user] = entity; + GasPhases.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Remove_GasPhase(int n_user) +{ + GasPhases.erase(n_user); +} + +cxxSSassemblage * +cxxStorageBin::Get_SSassemblage(int n_user) +{ + if (this->SSassemblages.find(n_user) != this->SSassemblages.end()) + { + return (&(this->SSassemblages.find(n_user)->second)); + } + return (NULL); +} +void +cxxStorageBin::Set_SSassemblage(int n_user, cxxSSassemblage * entity) +{ + if (entity == NULL) + return; + SSassemblages[n_user] = *entity; + SSassemblages.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Set_SSassemblage(int n_user, cxxSSassemblage & entity) +{ + SSassemblages[n_user] = entity; + SSassemblages.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Remove_SSassemblage(int n_user) +{ + SSassemblages.erase(n_user); +} + +cxxKinetics * +cxxStorageBin::Get_Kinetics(int n_user) +{ + if (this->Kinetics.find(n_user) != this->Kinetics.end()) + { + return (&(this->Kinetics.find(n_user)->second)); + } + return (NULL); +} +void +cxxStorageBin::Set_Kinetics(int n_user, cxxKinetics * entity) +{ + if (entity == NULL) + return; + Kinetics[n_user] = *entity; + Kinetics.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Set_Kinetics(int n_user, cxxKinetics & entity) +{ + Kinetics[n_user] = entity; + Kinetics.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Remove_Kinetics(int n_user) +{ + Kinetics.erase(n_user); +} + +cxxSurface * +cxxStorageBin::Get_Surface(int n_user) +{ + if (this->Surfaces.find(n_user) != this->Surfaces.end()) + { + return (&(this->Surfaces.find(n_user)->second)); + } + return (NULL); +} +void +cxxStorageBin::Set_Surface(int n_user, cxxSurface * entity) +{ + if (entity == NULL) + return; + Surfaces[n_user] = *entity; + Surfaces.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Set_Surface(int n_user, cxxSurface & entity) +{ + Surfaces[n_user] = entity; + Surfaces.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Remove_Surface(int n_user) +{ + Surfaces.erase(n_user); +} + +cxxMix * +cxxStorageBin::Get_Mix(int n_user) +{ + if (this->Mixes.find(n_user) != this->Mixes.end()) + { + return (&(this->Mixes.find(n_user)->second)); + } + return (NULL); +} +void +cxxStorageBin::Set_Mix(int n_user, cxxMix * entity) +{ + if (entity == NULL) + return; + Mixes[n_user] = *entity; + Mixes.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Set_Mix(int n_user, cxxMix & entity) +{ + Mixes[n_user] = entity; + Mixes.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Remove_Mix(int n_user) +{ + Mixes.erase(n_user); +} + +cxxReaction * +cxxStorageBin::Get_Reaction(int n_user) +{ + if (this->Reactions.find(n_user) != this->Reactions.end()) + { + return (&(this->Reactions.find(n_user)->second)); + } + return (NULL); +} +void +cxxStorageBin::Set_Reaction(int n_user, cxxReaction * entity) +{ + if (entity == NULL) + return; + Reactions[n_user] = *entity; + Reactions.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Set_Reaction(int n_user, cxxReaction & entity) +{ + Reactions[n_user] = entity; + Reactions.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Remove_Reaction(int n_user) +{ + Reactions.erase(n_user); +} + +cxxTemperature * +cxxStorageBin::Get_Temperature(int n_user) +{ + if (this->Temperatures.find(n_user) != this->Temperatures.end()) + { + return (&(this->Temperatures.find(n_user)->second)); + } + return (NULL); +} + +void +cxxStorageBin::Set_Temperature(int n_user, cxxTemperature * entity) +{ + if (entity == NULL) + return; + Temperatures[n_user] = *entity; + Temperatures.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Set_Temperature(int n_user, cxxTemperature & entity) +{ + Temperatures[n_user] = entity; + Temperatures.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Remove_Temperature(int n_user) +{ + Temperatures.erase(n_user); +} + +cxxPressure * +cxxStorageBin::Get_Pressure(int n_user) +{ + return Utilities::Rxn_find(this->Pressures, n_user); +} + +void +cxxStorageBin::Set_Pressure(int n_user, cxxPressure * entity) +{ + if (entity == NULL) + return; + Pressures[n_user] = *entity; + Pressures.find(n_user)->second.Set_n_user_both(n_user); +} +void +cxxStorageBin::Set_Pressure(int n_user, cxxPressure & entity) +{ + Pressures[n_user] = entity; + Pressures.find(n_user)->second.Set_n_user_both(n_user); +} +void + +cxxStorageBin::Remove_Pressure(int n_user) +{ + Pressures.erase(n_user); +} + +std::map < int, cxxSolution > & +cxxStorageBin::Get_Solutions() +{ + return this->Solutions; +} +std::map < int, cxxExchange > & +cxxStorageBin::Get_Exchangers() +{ + return this->Exchangers; +} +std::map < int, cxxGasPhase > & +cxxStorageBin::Get_GasPhases() +{ + return this->GasPhases; +} +std::map < int, cxxKinetics > & +cxxStorageBin::Get_Kinetics() +{ + return this->Kinetics; +} +std::map < int, cxxPPassemblage > & +cxxStorageBin::Get_PPassemblages() +{ + return this->PPassemblages; +} +std::map < int, cxxSSassemblage > & +cxxStorageBin::Get_SSassemblages() +{ + return this->SSassemblages; +} +std::map < int, cxxSurface > & +cxxStorageBin::Get_Surfaces() +{ + return this->Surfaces; +} +std::map < int, cxxMix > & +cxxStorageBin::Get_Mixes() +{ + return this->Mixes; +} +std::map < int, cxxReaction > & +cxxStorageBin::Get_Reactions() +{ + return this->Reactions; +} +std::map < int, cxxTemperature > & +cxxStorageBin::Get_Temperatures() +{ + return this->Temperatures; +} +std::map < int, cxxPressure > & +cxxStorageBin::Get_Pressures() +{ + return this->Pressures; +} +#ifdef SKIP +void +cxxStorageBin::dump_xml(std::ostream & s_oss, unsigned int indent) const const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // StorageBin element and attributes + s_oss << indent0; + s_oss << " + pitzer_mix_gammas << "\"" << "\n"; + + // components + s_oss << indent1; + s_oss << "::const_iterator it = + mixComps.begin(); it != mixComps.end(); ++it) + { + it->dump_xml(s_oss, indent + 2); + } + + return; +} +#endif + +void +cxxStorageBin::dump_raw(std::ostream & s_oss, unsigned int indent) const +{ + // Dump all data + s_oss.precision(DBL_DIG - 1); + + // Solutions + Utilities::Rxn_dump_raw(Solutions, s_oss, indent); + + // Exchange + Utilities::Rxn_dump_raw(Exchangers, s_oss, indent); + + // Gas Phases + Utilities::Rxn_dump_raw(GasPhases, s_oss, indent); + + // Kinetics + Utilities::Rxn_dump_raw(Kinetics, s_oss, indent); + + // PPassemblage + Utilities::Rxn_dump_raw(PPassemblages, s_oss, indent); + + // SSassemblage + Utilities::Rxn_dump_raw(SSassemblages, s_oss, indent); + + // Surface + Utilities::Rxn_dump_raw(Surfaces, s_oss, indent); + + // Mix + Utilities::Rxn_dump_raw(Mixes, s_oss, indent); + + // Reactions + Utilities::Rxn_dump_raw(Reactions, s_oss, indent); + + // Temperature + Utilities::Rxn_dump_raw(Temperatures, s_oss, indent); +} + +void +cxxStorageBin::dump_raw(std::ostream & s_oss, int n, unsigned int indent, int *n_out) +{ + // Dump one user number, optionally change number from n to n_out + int n_user_local = (n_out != NULL) ? *n_out : n; + s_oss.precision(DBL_DIG - 1); + + // Solutions + if (this->Get_Solution(n) != NULL) + { + this->Get_Solution(n)->dump_raw(s_oss, indent, &n_user_local); + } + + // Exchange + if (this->Get_Exchange(n) != NULL) + { + this->Get_Exchange(n)->dump_raw(s_oss, indent, &n_user_local); + } + + // Gas Phases + if (this->Get_GasPhase(n) != NULL) + { + this->Get_GasPhase(n)->dump_raw(s_oss, indent, &n_user_local); + } + + // Kinetics + if (this->Get_Kinetics(n) != NULL) + { + this->Get_Kinetics(n)->dump_raw(s_oss, indent, &n_user_local); + } + + // PPassemblage + if (this->Get_PPassemblage(n) != NULL) + { + this->Get_PPassemblage(n)->dump_raw(s_oss, indent, &n_user_local); + } + + // SSassemblage + if (this->Get_SSassemblage(n) != NULL) + { + this->Get_SSassemblage(n)->dump_raw(s_oss, indent, &n_user_local); + } + + // Surface + if (this->Get_Surface(n) != NULL) + { + this->Get_Surface(n)->dump_raw(s_oss, indent, &n_user_local); + } + + // Mix + if (this->Get_Mix(n) != NULL) + { + this->Get_Mix(n)->dump_raw(s_oss, indent, &n_user_local); + } + + // Reaction + if (this->Get_Reaction(n) != NULL) + { + this->Get_Reaction(n)->dump_raw(s_oss, indent, &n_user_local); + } + + // Temperature + if (this->Get_Temperature(n) != NULL) + { + this->Get_Temperature(n)->dump_raw(s_oss, indent, &n_user_local); + } +} + +void +cxxStorageBin::read_raw(CParser & parser) +{ + PHRQ_io::LINE_TYPE i; + while ((i = + parser.check_line("StorageBin read_raw", false, true, true, + true)) != PHRQ_io::LT_KEYWORD) + { + if (i == PHRQ_io::LT_EOF) + return; // PHRQ_io::LT_EOF; + } + + for (;;) + { + switch (parser.next_keyword()) + { + case Keywords::KEY_END: + case Keywords::KEY_NONE: + goto END_OF_SIMULATION_INPUT; + break; + case Keywords::KEY_SOLUTION_RAW: + { + cxxSolution entity(this->Get_io()); + entity.read_raw(parser); + Solutions[entity.Get_n_user()] = entity; + } + break; + case Keywords::KEY_EXCHANGE_RAW: + { + cxxExchange entity(this->Get_io()); + entity.read_raw(parser); + Exchangers[entity.Get_n_user()] = entity; + } + break; + case Keywords::KEY_GAS_PHASE_RAW: + { + cxxGasPhase entity(this->Get_io()); + entity.read_raw(parser); + GasPhases[entity.Get_n_user()] = entity; + } + break; + case Keywords::KEY_KINETICS_RAW: + { + cxxKinetics entity(this->Get_io()); + entity.read_raw(parser); + Kinetics[entity.Get_n_user()] = entity; + } + break; + + case Keywords::KEY_EQUILIBRIUM_PHASES_RAW: + { + cxxPPassemblage entity(this->Get_io()); + entity.read_raw(parser); + PPassemblages[entity.Get_n_user()] = entity; + } + break; + + case Keywords::KEY_SOLID_SOLUTIONS_RAW: + { + cxxSSassemblage entity; + entity.read_raw(parser); + SSassemblages[entity.Get_n_user()] = entity; + } + break; + + case Keywords::KEY_SURFACE_RAW: + { + cxxSurface entity(this->Get_io()); + entity.read_raw(parser); + Surfaces[entity.Get_n_user()] = entity; + } + break; + + case Keywords::KEY_REACTION_TEMPERATURE_RAW: + { + cxxTemperature entity(this->Get_io()); + entity.read_raw(parser); + Temperatures[entity.Get_n_user()] = entity; + } + break; + + case Keywords::KEY_REACTION_RAW: + { + cxxReaction entity; + entity.read_raw(parser, true); + Reactions[entity.Get_n_user()] = entity; + } + break; + case Keywords::KEY_MIX_RAW: + { + cxxMix entity; + entity.read_raw(parser); + Mixes[entity.Get_n_user()] = entity; + } + break; + default: + { + for (;;) + { + PHRQ_io::LINE_TYPE lt; + lt = parser.check_line("read_raw", false, true, true, false); + if (lt == PHRQ_io::LT_KEYWORD) + break; + if (lt == PHRQ_io::LT_EOF) + goto END_OF_SIMULATION_INPUT; + } + } + break; + } + } + + END_OF_SIMULATION_INPUT: + return; //PHRQ_io::LT_OK; +} + +int +cxxStorageBin::read_raw_keyword(CParser & parser) +{ + PHRQ_io::LINE_TYPE i; + int entity_number = -999; + + switch (parser.next_keyword()) + { + case Keywords::KEY_NONE: + case Keywords::KEY_END: + while ((i = + parser.check_line("StorageBin read_raw_keyword", false, true, + true, true)) != PHRQ_io::LT_KEYWORD) + { + if (i == PHRQ_io::LT_EOF) + break; // PHRQ_io::LT_EOF; + } + break; + + case Keywords::KEY_SOLUTION_RAW: + { + cxxSolution entity(this->Get_io()); + entity.read_raw(parser); + Solutions[entity.Get_n_user()] = entity; + entity_number = entity.Get_n_user(); + } + break; + + case Keywords::KEY_EXCHANGE_RAW: + { + cxxExchange entity(this->Get_io()); + entity.read_raw(parser); + Exchangers[entity.Get_n_user()] = entity; + entity_number = entity.Get_n_user(); + } + break; + + case Keywords::KEY_GAS_PHASE_RAW: + { + cxxGasPhase entity(this->Get_io()); + entity.read_raw(parser); + GasPhases[entity.Get_n_user()] = entity; + entity_number = entity.Get_n_user(); + } + break; + + case Keywords::KEY_KINETICS_RAW: + { + cxxKinetics entity(this->Get_io()); + entity.read_raw(parser); + Kinetics[entity.Get_n_user()] = entity; + entity_number = entity.Get_n_user(); + } + break; + + case Keywords::KEY_EQUILIBRIUM_PHASES_RAW: + { + cxxPPassemblage entity(this->Get_io()); + entity.read_raw(parser); + PPassemblages[entity.Get_n_user()] = entity; + entity_number = entity.Get_n_user(); + } + break; + + case Keywords::KEY_SOLID_SOLUTIONS_RAW: + { + cxxSSassemblage entity; + entity.read_raw(parser); + SSassemblages[entity.Get_n_user()] = entity; + entity_number = entity.Get_n_user(); + } + break; + + case Keywords::KEY_SURFACE_RAW: + { + cxxSurface entity(this->Get_io()); + entity.read_raw(parser); + Surfaces[entity.Get_n_user()] = entity; + entity_number = entity.Get_n_user(); + } + break; + + case Keywords::KEY_REACTION_TEMPERATURE_RAW: + { + cxxTemperature entity(this->Get_io()); + entity.read_raw(parser); + Temperatures[entity.Get_n_user()] = entity; + entity_number = entity.Get_n_user(); + } + break; + + case Keywords::KEY_REACTION_RAW: + { + cxxReaction entity; + entity.read_raw(parser, true); + Reactions[entity.Get_n_user()] = entity; + entity_number = entity.Get_n_user(); + } + break; + + case Keywords::KEY_MIX_RAW: + { + cxxMix entity; + entity.read_raw(parser); + Mixes[entity.Get_n_user()] = entity; + entity_number = entity.Get_n_user(); + } + break; + + default: + break; + } + return (entity_number); //PHRQ_io::LT_OK; +} + +void +cxxStorageBin::Remove(int n) +{ + // Solution + this->Solutions.erase(n); + + // Exchanger + this->Exchangers.erase(n); + + // GasPhase + this->GasPhases.erase(n); + + // Kinetics + this->Kinetics.erase(n); + + // PPassemblage + this->PPassemblages.erase(n); + + // SSassemblage + this->SSassemblages.erase(n); + + // Surface + this->Surfaces.erase(n); + + // Mixes + this->Mixes.erase(n); + + // Reactions + this->Reactions.erase(n); + + // Temperature + this->Temperatures.erase(n); + + // Pressure + this->Pressures.erase(n); +} +void +cxxStorageBin::Clear(void) +{ + // Delete all data + + // Solutions + this->Solutions.clear(); + + // Exchange + this->Exchangers.clear(); + + // Gas Phases + this->GasPhases.clear(); + + // Kinetics + this->Kinetics.clear(); + + // PPassemblage + this->PPassemblages.clear(); + + // SSassemblage + this->SSassemblages.clear(); + + // Surface + this->Surfaces.clear(); + + // Mix + this->Mixes.clear(); + + // Reactions + this->Reactions.clear(); + + // Temperature + this->Temperatures.clear(); + + // Pressure + this->Pressures.clear(); +} +#ifdef SKIP +cxxSolution * +cxxStorageBin::mix_cxxSolutions(cxxMix & mixmap) +{ +/* + * mixes solutions based on cxxMix structure, returns new solution + * return solution must be freed by calling method + */ + LDBLE intensive, extensive; + cxxSolution *cxxsoln_ptr, *cxxsoln_ptr1; +/* + * Zero out global solution data + */ + cxxsoln_ptr = new cxxSolution(0.0); +/* + * Determine sum of mixing fractions + */ + extensive = 0.0; + + std::map < int, LDBLE >*mixcomps = mixmap.comps(); + + std::map < int, LDBLE >::const_iterator it; + for (it = mixcomps->begin(); it != mixcomps->end(); it++) + { + extensive += it->second; + } +/* + * Add solutions + */ + for (it = mixcomps->begin(); it != mixcomps->end(); it++) + { + cxxsoln_ptr1 = &((this->Solutions.find(it->first))->second); + if (cxxsoln_ptr1 == NULL) + { + error_string = sformatf( + "Solution %d not found in mix_cxxSolutions.", it->first); + error_msg(error_string, CONTINUE); + phreeqc_ptr-> input_error++; + return (NULL); + } + intensive = it->second / extensive; + cxxsoln_ptr->add(*cxxsoln_ptr1, intensive, it->second); + } + return (cxxsoln_ptr); +} +#endif + +#ifdef SKIP_OR_MOVE_TO_STRUCTURES +struct system * +cxxStorageBin::cxxStorageBin2system(Phreeqc * phreeqc_ptr, int n) + // + // make a system from storagebin + // +{ + struct system *system_ptr = + (struct system *) phreeqc_ptr-> PHRQ_malloc(sizeof(struct system)); + if (system_ptr == NULL) + phreeqc_ptr-> malloc_error(); + + // Solutions + + if (this->getSolution(n) != NULL) + { + //system_ptr->solution = (this->getSolution(n))->cxxSolution2solution(phreeqc_ptr); + system_ptr->solution = phreeqc_ptr-> cxxSolution2solution(this->getSolution(n)); + } + else + { + system_ptr->solution = NULL; + } + + // Exchangers + if (this->getExchange(n) != NULL) + { + //system_ptr->exchange = (this->getExchange(n))->cxxExchange2exchange(phreeqc_ptr); + system_ptr->exchange = phreeqc_ptr-> cxxExchange2exchange(this->getExchange(n)); + } + else + { + system_ptr->exchange = NULL; + } + + // GasPhases + if (this->getGasPhase(n) != NULL) + { + //system_ptr->gas_phase = (this->getGasPhase(n))->cxxGasPhase2gas_phase(phreeqc_ptr); + system_ptr->gas_phase = phreeqc_ptr-> cxxGasPhase2gas_phase(this->getGasPhase(n)); + } + else + { + system_ptr->gas_phase = NULL; + } + + // Kinetics + if (this->getKinetics(n) != NULL) + { + //system_ptr->kinetics = (this->getKinetics(n))->cxxKinetics2kinetics(phreeqc_ptr); + system_ptr->kinetics = phreeqc_ptr-> cxxKinetics2kinetics(this->getKinetics(n)); + + } + else + { + system_ptr->kinetics = NULL; + } + + // PPassemblages + if (this->getPPassemblage(n) != NULL) + { + //system_ptr->pp_assemblage = + // (this->getPPassemblage(n))->cxxPPassemblage2pp_assemblage(phreeqc_ptr); + system_ptr->pp_assemblage = + phreeqc_ptr-> cxxPPassemblage2pp_assemblage(this->getPPassemblage(n)); + } + else + { + system_ptr->pp_assemblage = NULL; + } + + // SSassemblages + if (this->getSSassemblage(n) != NULL) + { + //system_ptr->ss_assemblage = + // (this->getSSassemblage(n))->cxxSSassemblage2ss_assemblage(phreeqc_ptr); + system_ptr->ss_assemblage = + phreeqc_ptr-> cxxSSassemblage2ss_assemblage((this->getSSassemblage(n))); + } + else + { + system_ptr->ss_assemblage = NULL; + } + + // Surfaces + if (this->getSurface(n) != NULL) + { + //system_ptr->surface = (this->getSurface(n))->cxxSurface2surface(phreeqc_ptr); + system_ptr->surface = phreeqc_ptr-> cxxSurface2surface((this->getSurface(n))); + } + else + { + system_ptr->surface = NULL; + } + return system_ptr; +} +#endif + +#ifdef SKIP +cxxExchange * +cxxStorageBin::mix_cxxExchange(cxxMix & mixmap) +{ +/* + * mixes exchangers based on cxxMix structure, returns new exchanger + * return exchanger must be freed by calling method + */ + cxxExchange *new_exch_ptr, *old_exch_ptr; +/* + * Zero out global solution data + */ + new_exch_ptr = new cxxExchange(); + + std::map < int, LDBLE >::const_iterator it_mix; + std::map < int, LDBLE >*mixcomps = mixmap.comps(); + + // Pitzer_exchange_gammas + it_mix = mixcomps->begin(); + old_exch_ptr = &((this->Exchangers.find(it_mix->first))->second); + if (old_exch_ptr == NULL) + { + error_string = sformatf( "Exchange %d not found in mix_cxxExchange.", + it_mix->first); + error_msg(error_string, CONTINUE); + phreeqc_ptr-> input_error++; + return (NULL); + } + new_exch_ptr->set_pitzer_exchange_gammas(old_exch_ptr-> + get_pitzer_exchange_gammas()); +/* + * Make list of ExchComps + */ + std::vector < cxxExchComp > ec_vector; + std::vector < LDBLE >f_vector; + // + // make list of all exchange components and their mix fractions + // + for (it_mix = mixcomps->begin(); it_mix != mixcomps->end(); it_mix++) + { + old_exch_ptr = &((this->Exchangers.find(it_mix->first))->second); + if (old_exch_ptr == NULL) + { + error_string = sformatf( "Exchange %d not found in mix_cxxExchange.", + it_mix->first); + error_msg(error_string, CONTINUE); + phreeqc_ptr-> input_error++; + return (NULL); + } + // Add exchange components to vector ec_vector + std::list < cxxExchComp >::const_iterator it_ec; + std::list < cxxExchComp > &eclist = old_exch_ptr->get_exchComps(); + for (it_ec = eclist.begin(); it_ec != eclist.end(); it_ec++) + { + f_vector.push_back(it_mix->second); + //cxxExchComp ec = *it_ec; + //ec_vector.push_back(ec); + ec_vector.push_back(*it_ec); + } + } + // + // Process list to make mixture + // + char *current_formula = ec_vector.begin()->get_formula(); + while (current_formula != NULL) + { + + std::vector < cxxExchComp > ec_subvector; + std::vector < LDBLE >f_subvector; + std::vector < cxxExchComp >::iterator it_ec = ec_vector.begin(); + std::vector < LDBLE >::iterator it_f = f_vector.begin(); + current_formula = NULL; + for (; it_ec != ec_vector.end(); it_ec++) + { + if (*it_f != 0) + { + if (current_formula == NULL) + current_formula = it_ec->get_formula(); + if (it_ec->get_formula() == current_formula) + { + ec_subvector.push_back(*it_ec); + f_subvector.push_back(*it_f); + *it_f = 0; + //ec_vector.erase(it_ec); + //f_vector.erase(it_f); + } + } + it_f++; + } + // + // mix ec_subvector to make + // one exchange component + // + if (current_formula != NULL) + { + cxxExchComp new_comp(ec_subvector, f_subvector); + new_exch_ptr->get_exchComps().push_back(new_comp); + } + } + /* + std::ostringstream oss; + new_exch_ptr->dump_raw(oss, 0); + std::cerr << oss.str(); + */ + return (new_exch_ptr); +} +#endif + +cxxSystem & +cxxStorageBin::Get_System(void) +{ + return this->system; +} + +void +cxxStorageBin::Set_System(cxxUse *use_ptr) +{ + // Initialize + this->system.Initialize(); + // Solution + if (use_ptr->Get_solution_ptr() != NULL) + { + std::map < int, cxxSolution >::iterator it = + this->Solutions.find(use_ptr->Get_n_solution_user()); + if (it != this->Solutions.end()) + { + this->system.Set_Solution(&(it->second)); + } + } + // Exchange + if (use_ptr->Get_exchange_ptr() != NULL) + { + std::map < int, cxxExchange >::iterator it = + this->Exchangers.find(use_ptr->Get_n_exchange_user()); + if (it != this->Exchangers.end()) + { + this->system.Set_Exchange(&(it->second)); + } + } + // gas_phase + if (use_ptr->Get_gas_phase_ptr() != NULL) + { + std::map < int, cxxGasPhase >::iterator it = + this->GasPhases.find(use_ptr->Get_n_gas_phase_user()); + if (it != this->GasPhases.end()) + { + this->system.Set_GasPhase(&(it->second)); + } + } + // kinetics + if (use_ptr->Get_kinetics_ptr() != NULL) + { + std::map < int, cxxKinetics >::iterator it = + this->Kinetics.find(use_ptr->Get_n_kinetics_user()); + if (it != this->Kinetics.end()) + { + this->system.Set_Kinetics(&(it->second)); + } + } + // pp_assemblage + if (use_ptr->Get_pp_assemblage_ptr() != NULL) + { + std::map < int, cxxPPassemblage >::iterator it = + this->PPassemblages.find(use_ptr->Get_n_pp_assemblage_user()); + if (it != this->PPassemblages.end()) + { + this->system.Set_PPassemblage(&(it->second)); + } + } + // ss_assemblage + if (use_ptr->Get_ss_assemblage_ptr() != NULL) + { + std::map < int, cxxSSassemblage >::iterator it = + this->SSassemblages.find(use_ptr->Get_n_ss_assemblage_user()); + if (it != this->SSassemblages.end()) + { + this->system.Set_SSassemblage(&(it->second)); + } + } + // surface + if (use_ptr->Get_surface_ptr() != NULL) + { + std::map < int, cxxSurface >::iterator it = + this->Surfaces.find(use_ptr->Get_n_surface_user()); + if (it != this->Surfaces.end()) + { + this->system.Set_Surface(&(it->second)); + } + } + // mix + if (use_ptr->Get_mix_ptr() != NULL) + { + std::map < int, cxxMix >::iterator it = + this->Mixes.find(use_ptr->Get_n_mix_user()); + if (it != this->Mixes.end()) + { + this->system.Set_Mix(&(it->second)); + } + } + // reaction + if (use_ptr->Get_reaction_ptr() != NULL) + { + std::map < int, cxxReaction >::iterator it = + this->Reactions.find(use_ptr->Get_n_reaction_user()); + if (it != this->Reactions.end()) + { + this->system.Set_Reaction(&(it->second)); + } + } + // reaction temperature + if (use_ptr->Get_temperature_ptr() != NULL) + { + std::map < int, cxxTemperature >::iterator it = + this->Temperatures.find(use_ptr->Get_n_temperature_user()); + if (it != this->Temperatures.end()) + { + this->system.Set_Temperature(&(it->second)); + } + } + // reaction pressure + if (use_ptr->Get_pressure_ptr() != NULL) + { + cxxPressure * p = Utilities::Rxn_find(this->Pressures, use_ptr->Get_n_pressure_user()); + if (p != NULL) + { + this->system.Set_Pressure(p); + } + } +} +void +cxxStorageBin::Set_System(int i) +{ + // Initialize + this->system.Initialize(); + // Solution + { + std::map < int, cxxSolution >::iterator it = this->Solutions.find(i); + if (it != this->Solutions.end()) + { + this->system.Set_Solution(&(it->second)); + } + } + + // Exchange + { + std::map < int, cxxExchange >::iterator it = this->Exchangers.find(i); + if (it != this->Exchangers.end()) + { + this->system.Set_Exchange(&(it->second)); + } + } + + // gas_phase + { + std::map < int, cxxGasPhase >::iterator it = this->GasPhases.find(i); + if (it != this->GasPhases.end()) + { + this->system.Set_GasPhase(&(it->second)); + } + } + // kinetics + { + std::map < int, cxxKinetics >::iterator it = this->Kinetics.find(i); + if (it != this->Kinetics.end()) + { + this->system.Set_Kinetics(&(it->second)); + } + } + // pp_assemblage + { + std::map < int, cxxPPassemblage >::iterator it = this->PPassemblages.find(i); + if (it != this->PPassemblages.end()) + { + this->system.Set_PPassemblage(&(it->second)); + } + } + // ss_assemblage + { + std::map < int, cxxSSassemblage >::iterator it = this->SSassemblages.find(i); + if (it != this->SSassemblages.end()) + { + this->system.Set_SSassemblage(&(it->second)); + } + } + // surface + { + std::map < int, cxxSurface >::iterator it = this->Surfaces.find(i); + if (it != this->Surfaces.end()) + { + this->system.Set_Surface(&(it->second)); + } + } + // mix + { + std::map < int, cxxMix >::iterator it = this->Mixes.find(i); + if (it != this->Mixes.end()) + { + this->system.Set_Mix(&(it->second)); + } + } + // reaction + { + std::map < int, cxxReaction >::iterator it = this->Reactions.find(i); + if (it != this->Reactions.end()) + { + this->system.Set_Reaction(&(it->second)); + } + } + // reaction temperature + { + std::map < int, cxxTemperature >::iterator it = this->Temperatures.find(i); + if (it != this->Temperatures.end()) + { + this->system.Set_Temperature(&(it->second)); + } + } + + // reaction pressure + { + this->system.Set_Pressure(Utilities::Rxn_find(this->Pressures, i)); + } +} diff --git a/phreeqcpp/StorageBin.h b/phreeqcpp/StorageBin.h new file mode 100644 index 00000000..cf83d7f7 --- /dev/null +++ b/phreeqcpp/StorageBin.h @@ -0,0 +1,139 @@ +#if !defined(STORAGEBIN_H_INCLUDED) +#define STORAGEBIN_H_INCLUDED +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "System.h" +#include "PHRQ_io.h" +#include "PHRQ_base.h" +class cxxSolution; +class cxxExchange; +class cxxGasPhase; +class cxxKinetics; +class cxxPPassemblage; +class cxxSSassemblage; +class cxxSurface; +class cxxReaction; +class cxxTemperature; +class cxxUse; + +class cxxStorageBin: public PHRQ_base +{ + + public: + cxxStorageBin(PHRQ_io *io=NULL); + cxxStorageBin(cxxUse &use_ref, PHRQ_io *io=NULL); + virtual ~cxxStorageBin(); + + void Copy(int destination, int source); + void Remove(int n); + void Clear(void); + + cxxSolution *Get_Solution(int n_user); + void Set_Solution(int n_user, cxxSolution * entity); + void Set_Solution(int n_user, cxxSolution & entity); + void Remove_Solution(int n_user); + + cxxExchange *Get_Exchange(int n_user); + void Set_Exchange(int n_user, cxxExchange * entity); + void Set_Exchange(int n_user, cxxExchange & entity); + void Remove_Exchange(int n_user); + + cxxPPassemblage *Get_PPassemblage(int n_user); + void Set_PPassemblage(int n_user, cxxPPassemblage * entity); + void Set_PPassemblage(int n_user, cxxPPassemblage & entity); + void Remove_PPassemblage(int n_user); + + cxxGasPhase *Get_GasPhase(int n_user); + void Set_GasPhase(int n_user, cxxGasPhase * entity); + void Set_GasPhase(int n_user, cxxGasPhase & entity); + void Remove_GasPhase(int n_user); + + cxxSSassemblage *Get_SSassemblage(int n_user); + void Set_SSassemblage(int n_user, cxxSSassemblage * entity); + void Set_SSassemblage(int n_user, cxxSSassemblage & entity); + void Remove_SSassemblage(int n_user); + + cxxKinetics *Get_Kinetics(int n_user); + void Set_Kinetics(int n_user, cxxKinetics * entity); + void Set_Kinetics(int n_user, cxxKinetics & entity); + void Remove_Kinetics(int n_user); + + cxxSurface *Get_Surface(int n_user); + void Set_Surface(int n_user, cxxSurface * entity); + void Set_Surface(int n_user, cxxSurface & entity); + void Remove_Surface(int n_user); + + cxxMix *Get_Mix(int n_user); + void Set_Mix(int n_user, cxxMix * entity); + void Set_Mix(int n_user, cxxMix & entity); + void Remove_Mix(int n_user); + + cxxReaction *Get_Reaction(int n_user); + void Set_Reaction(int n_user, cxxReaction * entity); + void Set_Reaction(int n_user, cxxReaction & entity); + void Remove_Reaction(int n_user); + + cxxTemperature *Get_Temperature(int n_user); + void Set_Temperature(int n_user, cxxTemperature * entity); + void Set_Temperature(int n_user, cxxTemperature & entity); + void Remove_Temperature(int n_user); + + cxxPressure *Get_Pressure(int n_user); + void Set_Pressure(int n_user, cxxPressure * entity); + void Set_Pressure(int n_user, cxxPressure & entity); + void Remove_Pressure(int n_user); + + cxxSystem &Get_System(void); + void Set_System(cxxUse *use_ptr); + void Set_System(int i); + + void dump_raw(std::ostream & s_oss, unsigned int indent) const; + + void dump_raw(std::ostream & s_oss, int i, unsigned int indent, int *n_out=NULL); + + void read_raw(CParser & parser); + int read_raw_keyword(CParser & parser); + + void Add(cxxStorageBin &src, int n); + + //cxxSolution *mix_cxxSolutions(cxxMix &mixmap); + cxxExchange *mix_cxxExchange(cxxMix & mixmap); + + std::map < int, cxxSolution > &Get_Solutions(); + std::map < int, cxxExchange > &Get_Exchangers(); + std::map < int, cxxGasPhase > &Get_GasPhases(); + std::map < int, cxxKinetics > &Get_Kinetics(); + std::map < int, cxxPPassemblage > &Get_PPassemblages(); + std::map < int, cxxSSassemblage > &Get_SSassemblages(); + std::map < int, cxxSurface > &Get_Surfaces(); + std::map < int, cxxMix > &Get_Mixes(); + std::map < int, cxxReaction > &Get_Reactions(); + std::map < int, cxxTemperature > &Get_Temperatures(); + std::map < int, cxxPressure > &Get_Pressures(); + + cxxSystem & Get_system(void) {return system;}; + + protected: + // Tidied classes + std::map < int, cxxSolution > Solutions; + std::map < int, cxxExchange > Exchangers; + std::map < int, cxxGasPhase > GasPhases; + std::map < int, cxxKinetics > Kinetics; + std::map < int, cxxPPassemblage > PPassemblages; + std::map < int, cxxSSassemblage > SSassemblages; + std::map < int, cxxSurface > Surfaces; + + // Reaction classes + std::map < int, cxxMix > Mixes; + std::map < int, cxxReaction > Reactions; + std::map < int, cxxTemperature > Temperatures; + std::map < int, cxxPressure > Pressures; + cxxSystem system; + +}; + +#endif // !defined(STORAGEBIN_H_INCLUDED) diff --git a/phreeqcpp/StorageBinList.cpp b/phreeqcpp/StorageBinList.cpp new file mode 100644 index 00000000..71a89692 --- /dev/null +++ b/phreeqcpp/StorageBinList.cpp @@ -0,0 +1,350 @@ +#include // std::replace +#include "StorageBinList.h" +#include "Parser.h" + +StorageBinListItem::StorageBinListItem(void) +{ + this->defined = false; +} + +StorageBinListItem::~StorageBinListItem(void) +{ +} +StorageBinListItem::StorageBinListItem(CParser & parser) +{ + this->Clear(); + // Read list of numbers or number ranges + for (;;) + { + //read lines + PHRQ_io::LINE_TYPE l = parser.check_line("read StorageBinListLtem", false, true, true, true); + std::istream::pos_type next_char = 0; + if (l == PHRQ_io::LT_EOF) break; + for (;;) + { + std::string token; + CParser::TOKEN_TYPE j = parser.copy_token(token, next_char); + if (j == CParser::TT_DIGIT) + { + this->Augment(token); + } + else if (j == CParser::TT_EMPTY) + { + break; + } + } + } +} +void StorageBinListItem::Augment(std::string token) +{ + this->defined = true; + if (token.size() == 0) return; + + // split string accounting for possible negative numbers + size_t pos; + if ((pos = token.find("--")) != std::string::npos) + { + token.replace(pos,2," &"); + } + std::replace(token.begin() + 1, token.end(), '-', ' '); + std::replace(token.begin() + 1, token.end(), '&', '-'); + + // parse string into 1 or 2 numbers + std::istringstream iss(token); + std::set < int > temp_set; + int i; + if (iss >> i) + { + // add first + temp_set.insert(i); + if (iss >> i) + { + // add second if defined + temp_set.insert(i); + } + } + + // add single number or range to StorageBinListItem + if (temp_set.size() == 1) + { + this->numbers.insert(*(temp_set.begin())); + } + else if (temp_set.size() == 2) + { + int i1, i2; + std::set ::iterator it; + it = temp_set.begin(); + i1 = *it; + it++; + i2 = *it; + for (i = i1; i <= i2; i++) + { + this->numbers.insert(i); + } + } +} +void StorageBinListItem::Augment(int i) +{ + // Skip if all are defined + if (this->defined == true && this->numbers.size() == 0) return; + this->defined = true; + this->numbers.insert(i); +} +// +//Class definitions for StorageBinList +// +StorageBinList::StorageBinList(PHRQ_io *io) +: +PHRQ_base(io) +{ +} +StorageBinList::StorageBinList(CParser & parser, PHRQ_io *io) +: +PHRQ_base(io) +{ + this->Read(parser); +} +StorageBinList::~StorageBinList(void) +{ +} + +std::set StorageBinList::GetAllItems(void) +{ + // don't include this->cell + std::set items; + items.insert(&this->solution); + items.insert(&this->pp_assemblage); + items.insert(&this->exchange); + items.insert(&this->surface); + items.insert(&this->ss_assemblage); + items.insert(&this->gas_phase); + items.insert(&this->kinetics); + items.insert(&this->mix); + items.insert(&this->reaction); + items.insert(&this->temperature); + items.insert(&this->pressure); + return items; +} + +void StorageBinList::SetAll(bool tf) +{ + std::set all = this->GetAllItems(); + std::set::iterator it = all.begin(); + for (; it != all.end(); ++it) + { + (*it)->Clear(); + (*it)->Set_defined(tf); + } +} + +bool StorageBinList::Read(CParser & parser) +{ + bool return_value(true); + + std::istream::pos_type next_char; + std::string token; + int opt_save; + + opt_save = CParser::OPT_DEFAULT; + // reset cells + this->cell.Clear(); + this->cell.Set_defined(false); + for (;;) + { + int opt; + opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + else + { + opt_save = opt; + } + + // Select StorageBinListItem + StorageBinListItem *item = NULL; + switch (opt) + { + case 0: + item = &(this->Get_solution()); + break; + case 1: + case 2: + item = &(this->Get_pp_assemblage()); + break; + case 3: + item = &(this->Get_exchange()); + break; + case 4: + item = &(this->Get_surface()); + break; + case 5: + case 6: + case 7: + item = &(this->Get_ss_assemblage()); + break; + case 8: + item = &(this->Get_gas_phase()); + break; + case 9: + item = &(this->Get_kinetics()); + break; + case 10: + item = &(this->Get_mix()); + break; + case 11: + item = &(this->Get_reaction()); + break; + case 12: + case 16: + item = &(this->Get_temperature()); + break; + case 14: + case 15: + item = &(this->Get_cell()); + break; + case 17: + case 18: + item = &(this->Get_pressure()); + break; + default: + break; + } + + // Read dump entity list of numbers or number ranges for line, store in item + if ((opt >= 0 && opt <= 12) || (opt >= 14)) + { + for (;;) + { + CParser::TOKEN_TYPE j = parser.copy_token(token, next_char); + if (item) + { + if (j == CParser::TT_DIGIT) + { + item->Augment(token); + } + else if (j == CParser::TT_EMPTY) + { + item->Augment(token); + break; + } + else + { + parser.error_msg("Expected single number or range of numbers.", + PHRQ_io::OT_CONTINUE); + break; + } + } + else + { + parser.error_msg("Dump entity type not defined.", + PHRQ_io::OT_CONTINUE); + break; + } + } + } + + // Process other identifiers + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 16: + case 17: + case 18: + break; + case 13: //all + this->SetAll(true); + break; + case 14: + case 15: + //if (cell_list.Get_numbers().empty()) + //{ + // this->SetAll(true); + //} + //else + //{ + //this->TransferAll(cell_list); + //} + break; + default: + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input reading DELETE definition.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + return_value = false; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + + // Now check to see if cell_list defined + if (this->Get_cell().Get_defined()) + { + if (this->Get_cell().Get_numbers().empty()) + { + this->SetAll(true); + } + else + { + this->TransferAll(this->Get_cell()); + } + } + return(return_value); +} + +void StorageBinList::TransferAll(StorageBinListItem &source) +{ + std::set items = this->GetAllItems(); + std::set < int >::iterator it; + for (it = source.Get_numbers().begin(); it != source.Get_numbers().end(); it++) + { + std::set::iterator item = items.begin(); + for (; item != items.end(); ++item) + { + (*item)->Augment(*it); + } + } +} +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("solution"), // 0 + std::vector< std::string >::value_type("pp_assemblage"), // 1 + std::vector< std::string >::value_type("equilibrium_phases"), // 2 + std::vector< std::string >::value_type("exchange"), // 3 + std::vector< std::string >::value_type("surface"), // 4 + std::vector< std::string >::value_type("ss_assemblage"), // 5 + std::vector< std::string >::value_type("solid_solution"), // 6 + std::vector< std::string >::value_type("solid_solutions"), // 7 + std::vector< std::string >::value_type("gas_phase"), // 8 + std::vector< std::string >::value_type("kinetics"), // 9 + std::vector< std::string >::value_type("mix"), // 10 + std::vector< std::string >::value_type("reaction"), // 11 + std::vector< std::string >::value_type("temperature"), // 12 + std::vector< std::string >::value_type("all"), // 13 + std::vector< std::string >::value_type("cell"), // 14 + std::vector< std::string >::value_type("cells"), // 15 + std::vector< std::string >::value_type("reaction_temperature"), // 16 + std::vector< std::string >::value_type("pressure"), // 17 + std::vector< std::string >::value_type("reaction_pressure") // 18 +}; +const std::vector< std::string > StorageBinList::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/StorageBinList.h b/phreeqcpp/StorageBinList.h new file mode 100644 index 00000000..f55a6735 --- /dev/null +++ b/phreeqcpp/StorageBinList.h @@ -0,0 +1,81 @@ +#if !defined(STORAGEBINLIST_H_INCLUDED) +#define STORAGEBINLIST_H_INCLUDED +#include // std::set +#include // std::string +#include // std::list +#include // std::vector +#include "PHRQ_base.h" +class CParser; + +class StorageBinListItem +{ +public: + StorageBinListItem(void); + StorageBinListItem(CParser & parser); + ~StorageBinListItem(void); + void Set_defined(bool tf) { this->defined = tf; }; + bool Get_defined(void)const { return(this->defined); }; + void Augment(std::string token); + void Augment(int i); + std::set < int > &Get_numbers(void) { return(this->numbers); }; + const std::set < int > &Get_numbers(void)const { return(this->numbers); }; + void Clear(void) { this->numbers.clear(); }; +protected: + std::set < int > numbers; + bool defined; +}; +class StorageBinList: public PHRQ_base +{ +public: + StorageBinList(PHRQ_io *io=NULL); + StorageBinList(CParser & parser, PHRQ_io *io=NULL); + virtual ~StorageBinList(void); + bool Read(CParser & parser); + void SetAll(bool tf); + void TransferAll(StorageBinListItem &source); + std::set GetAllItems(void); + + StorageBinListItem & Get_solution(void) { return(this->solution); }; + StorageBinListItem & Get_pp_assemblage(void) { return(this->pp_assemblage); }; + StorageBinListItem & Get_exchange(void) { return(this->exchange); }; + StorageBinListItem & Get_surface(void) { return(this->surface); }; + StorageBinListItem & Get_ss_assemblage(void) { return(this->ss_assemblage); }; + StorageBinListItem & Get_gas_phase(void) { return(this->gas_phase); }; + StorageBinListItem & Get_kinetics(void) { return(this->kinetics); }; + StorageBinListItem & Get_mix(void) { return(this->mix); }; + StorageBinListItem & Get_reaction(void) { return(this->reaction); }; + StorageBinListItem & Get_temperature(void) { return(this->temperature); }; + StorageBinListItem & Get_pressure(void) { return(this->pressure); }; + StorageBinListItem & Get_cell(void) { return(this->cell); }; + + const StorageBinListItem & Get_solution(void)const { return(this->solution); }; + const StorageBinListItem & Get_pp_assemblage(void)const { return(this->pp_assemblage); }; + const StorageBinListItem & Get_exchange(void)const { return(this->exchange); }; + const StorageBinListItem & Get_surface(void)const { return(this->surface); }; + const StorageBinListItem & Get_ss_assemblage(void)const { return(this->ss_assemblage); }; + const StorageBinListItem & Get_gas_phase(void)const { return(this->gas_phase); }; + const StorageBinListItem & Get_kinetics(void)const { return(this->kinetics); }; + const StorageBinListItem & Get_mix(void)const { return(this->mix); }; + const StorageBinListItem & Get_reaction(void)const { return(this->reaction); }; + const StorageBinListItem & Get_temperature(void)const { return(this->temperature); }; + const StorageBinListItem & Get_pressure(void)const { return(this->pressure); }; + const StorageBinListItem & Get_cell(void)const { return(this->cell); }; +protected: + // update GetAllItems() if StorageBinListItem is added/removed + StorageBinListItem solution; + StorageBinListItem pp_assemblage; + StorageBinListItem exchange; + StorageBinListItem surface; + StorageBinListItem ss_assemblage; + StorageBinListItem gas_phase; + StorageBinListItem kinetics; + StorageBinListItem mix; + StorageBinListItem reaction; + StorageBinListItem temperature; + StorageBinListItem pressure; + const static std::vector < std::string > vopts; + StorageBinListItem cell; // not included in GetAllItems +}; + + +#endif // !defined(STORAGEBINLIST_H_INCLUDED) diff --git a/phreeqcpp/Surface.cxx b/phreeqcpp/Surface.cxx new file mode 100644 index 00000000..b2e7ef2f --- /dev/null +++ b/phreeqcpp/Surface.cxx @@ -0,0 +1,852 @@ +// Surface.cxx: implementation of the cxxSurface class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "Surface.h" +#include "cxxMix.h" +#include "phqalloc.h" + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxSurface::cxxSurface(PHRQ_io *io) + // + // default constructor for cxxSurface + // +: cxxNumKeyword(io) +{ + new_def = false; + type = DDL; + dl_type = NO_DL; + sites_units = SITES_ABSOLUTE; + only_counter_ions = false; + thickness = 1e-8; + debye_lengths = 0.0; + DDL_viscosity = 1.0; + DDL_limit = 0.8; + transport = false; + solution_equilibria = false; + n_solution = -999; +} +cxxSurface::cxxSurface(std::map < int, cxxSurface > &entities, + cxxMix & mix, int l_n_user, PHRQ_io *io): +cxxNumKeyword(io) +{ + this->n_user = this->n_user_end = l_n_user; + this->new_def = false; + type = DDL; + dl_type = NO_DL; + sites_units = SITES_ABSOLUTE; + only_counter_ions = false; + thickness = 1e-8; + debye_lengths = 0.0; + DDL_viscosity = 1.0; + DDL_limit = 0.8; + transport = false; + solution_equilibria = false; + n_solution = -999; +// +// Mix surfaces +// + const std::map < int, LDBLE >&mixcomps = mix.Get_mixComps(); + std::map < int, LDBLE >::const_iterator it; + for (it = mixcomps.begin(); it != mixcomps.end(); it++) + { + if (entities.find(it->first) != entities.end()) + { + this->add(entities.find(it->first)->second, it->second); + } + } +} + +cxxSurface::~cxxSurface() +{ +} + +bool +cxxSurface::Get_related_phases() const +{ + for (size_t i = 0; i != this->surface_comps.size(); i++) + { + if (this->surface_comps[i].Get_phase_name().size() == 0) + continue; + return (true); + } + return (false); +} + +bool +cxxSurface::Get_related_rate() const +{ + for (size_t i = 0; i != this->surface_comps.size(); i++) + { + if (this->surface_comps[i].Get_rate_name().size() == 0) + continue; + return (true); + } + return (false); +} +#ifdef SKIP +void +cxxSurface::dump_xml(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Surface element and attributes + s_oss << indent0; + s_oss << "type << "\"" << "\n"; + + s_oss << indent1; + s_oss << "dl_type=\"" << this->dl_type << "\"" << "\n"; + + s_oss << indent1; + s_oss << "sites_units=\"" << this->sites_units << "\"" << "\n"; + + s_oss << indent1; + s_oss << "only_counter_ions=\"" << this-> + only_counter_ions << "\"" << "\n"; + + s_oss << indent1; + s_oss << "thickness=\"" << this->thickness << "\"" << "\n"; + + s_oss << indent1; + s_oss << "debye_lengths=\"" << this->debye_lengths << "\"" << "\n"; + + s_oss << indent1; + s_oss << "DDL_viscosity=\"" << this->DDL_viscosity << "\"" << "\n"; + + s_oss << indent1; + s_oss << "DDL_limit=\"" << this->DDL_limit << "\"" << "\n"; + + s_oss << indent1; + s_oss << "transport=\"" << this->transport << "\"" << "\n"; + + // surface component structures + s_oss << indent1; + s_oss << "::const_iterator it = + this->surfaceComps.begin(); it != this->surfaceComps.end(); ++it) + { + (*it).second.dump_xml(s_oss, indent + 2); + } + } + // surface charge structures + s_oss << indent1; + s_oss << "::const_iterator it = + surface_charges.begin(); it != surface_charges.end(); ++it) + { + (*it).second.dump_xml(s_oss, indent + 2); + } + + return; +} +#endif +void +cxxSurface::dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Surface element and attributes + s_oss << indent0; + int n_user_local = (n_out != NULL) ? *n_out : this->n_user; + s_oss << "SURFACE_RAW " << n_user_local << " " << this->description << "\n"; + s_oss << indent1 << "# SURFACE_MODIFY candidate identifiers #\n"; + s_oss << indent1; + s_oss << "-type " << this->type << "\n"; + s_oss << indent1; + s_oss << "-dl_type " << this->dl_type << "\n"; + s_oss << indent1; + s_oss << "-only_counter_ions " << this->only_counter_ions << "\n"; + s_oss << indent1; + s_oss << "-thickness " << this->thickness << "\n"; + s_oss << indent1; + s_oss << "-debye_lengths " << this->debye_lengths << "\n"; + s_oss << indent1; + s_oss << "-DDL_viscosity " << this->DDL_viscosity << "\n"; + s_oss << indent1; + s_oss << "-DDL_limit " << this->DDL_limit << "\n"; + // surfaceComps + for (size_t i = 0; i != this->surface_comps.size(); i++) + { + const cxxSurfaceComp * comp_ptr = &(this->surface_comps[i]); + s_oss << indent1; + s_oss << "-component " << comp_ptr->Get_formula() << "\n"; + comp_ptr->dump_raw(s_oss, indent + 2); + } + // surface charge + for (size_t i = 0; i != this->surface_charges.size(); i++) + { + const cxxSurfaceCharge * charge_ptr = &(this->surface_charges[i]); + s_oss << indent1; + s_oss << "-charge_component " << charge_ptr->Get_name() << "\n"; + charge_ptr->dump_raw(s_oss, indent + 2); + } + + s_oss << indent1 << "# SURFACE_MODIFY candidates with new_def=true #\n"; + s_oss << indent1; + s_oss << "-new_def " << this->new_def << "\n"; + s_oss << indent1; + s_oss << "-sites_units " << this->sites_units << "\n"; + s_oss << indent1; + s_oss << "-solution_equilibria " << this->solution_equilibria << "\n"; + s_oss << indent1; + s_oss << "-n_solution " << this->n_solution << "\n"; + + s_oss << indent1 << "# Surface workspace variables #\n"; + s_oss << indent1; + s_oss << "-transport " << this->transport << "\n"; + s_oss << indent1; + s_oss << "-totals " << "\n"; + this->totals.dump_raw(s_oss, indent + 2); + + return; +} + +void +cxxSurface::read_raw(CParser & parser, bool check) +{ + int i = 0; + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + bool useLastLine(false); + + // Read surface number and description + this->read_number_description(parser); + this->Set_new_def(false); + + bool only_counter_ions_defined(false); + bool thickness_defined(false); + bool type_defined(false); + bool dl_type_defined(false); + bool sites_units_defined(false); + bool debye_lengths_defined(false); + bool DDL_viscosity_defined(false); + bool DDL_limit_defined(false); + bool transport_defined(false); + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + opt = parser.getOptionFromLastLine(vopts, next_char, true); + } + useLastLine = false; + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input in SURFACE keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + break; + + case 0: // diffuse_layer + parser.incr_input_error(); + parser.error_msg("Diffuse layer is obsolete, use -type.", + PHRQ_io::OT_CONTINUE); + break; + + case 1: // edl + parser.incr_input_error(); + parser.error_msg("-edl is obsolete, use -type.", + PHRQ_io::OT_CONTINUE); + break; + + case 2: // only_counter_ions + if (!(parser.get_iss() >> this->only_counter_ions)) + { + this->only_counter_ions = false; + parser.incr_input_error(); + parser. + error_msg("Expected boolean value for only_counter_ions.", + PHRQ_io::OT_CONTINUE); + } + only_counter_ions_defined = true; + break; + + case 3: // donnan + parser.incr_input_error(); + parser.error_msg("-Donnan is obsolete, use -dl_type.", + PHRQ_io::OT_CONTINUE); + break; + + case 4: // thickness + if (!(parser.get_iss() >> this->thickness)) + { + this->thickness = 0.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for thickness.", + PHRQ_io::OT_CONTINUE); + } + thickness_defined = true; + break; + case 5: // component + { + std::string str; + if (!(parser.get_iss() >> str)) + { + parser.incr_input_error(); + parser.error_msg("Expected string value for component name.", + PHRQ_io::OT_CONTINUE); + } + else + { + cxxSurfaceComp temp_comp(this->io); + temp_comp.Set_formula(str.c_str()); + cxxSurfaceComp *comp_ptr = this->Find_comp(str); + if (comp_ptr) + { + temp_comp = *comp_ptr; + } + temp_comp.read_raw(parser, check); + if (comp_ptr) + { + for (size_t j = 0; j < this->surface_comps.size(); j++) + { + if (Utilities::strcmp_nocase(this->surface_comps[j].Get_formula().c_str(), str.c_str()) == 0) + { + this->surface_comps[j] = temp_comp; + } + } + } + else + { + this->surface_comps.push_back(temp_comp); + } + useLastLine = true; + } + } + break; + case 6: // charge_component + { + std::string str; + if (!(parser.get_iss() >> str)) + { + parser.incr_input_error(); + parser.error_msg("Expected string value for charge name.", + PHRQ_io::OT_CONTINUE); + } + else + { + cxxSurfaceCharge temp_charge(this->io); + temp_charge.Set_name(str.c_str()); + cxxSurfaceCharge *charge_ptr = this->Find_charge(str); + if (charge_ptr) + { + temp_charge = *charge_ptr; + } + temp_charge.read_raw(parser, check); + if (charge_ptr) + { + for (size_t j = 0; j < this->surface_charges.size(); j++) + { + if (Utilities::strcmp_nocase(this->surface_charges[j].Get_name().c_str(), str.c_str()) == 0) + { + this->surface_charges[j] = temp_charge; + } + } + } + else + { + this->surface_charges.push_back(temp_charge); + } + useLastLine = true; + } + } + useLastLine = true; + break; + case 7: // type + i = 0; + if (!(parser.get_iss() >> i)) + { + this->type = NO_EDL; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for type.", + PHRQ_io::OT_CONTINUE); + } + this->type = (SURFACE_TYPE) i; + type_defined = true; + break; + case 8: // dl_type + i = 0; + if (!(parser.get_iss() >> i)) + { + this->dl_type = NO_DL; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for dl_type.", + PHRQ_io::OT_CONTINUE); + } + this->dl_type = (DIFFUSE_LAYER_TYPE) i; + dl_type_defined = true; + break; + case 9: // sites_units + i = 0; + if (!(parser.get_iss() >> i)) + { + this->sites_units = SITES_ABSOLUTE; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for sites_units.", + PHRQ_io::OT_CONTINUE); + } + this->sites_units = (SITES_UNITS) i; + sites_units_defined = true; + break; + + case 10: // debye_lengths + if (!(parser.get_iss() >> this->debye_lengths)) + { + this->debye_lengths = 0.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for debye_lengths.", + PHRQ_io::OT_CONTINUE); + } + debye_lengths_defined = true; + break; + + case 11: // DDL_viscosity + if (!(parser.get_iss() >> this->DDL_viscosity)) + { + this->DDL_viscosity = 0.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for DDL_viscosity.", + PHRQ_io::OT_CONTINUE); + } + DDL_viscosity_defined = true; + break; + + case 12: // DDL_limit + if (!(parser.get_iss() >> this->DDL_limit)) + { + this->DDL_limit = 0.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for DDL_limit.", + PHRQ_io::OT_CONTINUE); + } + DDL_limit_defined = true; + break; + + case 13: // transport + if (!(parser.get_iss() >> this->transport)) + { + this->transport = false; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for transport.", + PHRQ_io::OT_CONTINUE); + } + transport_defined = true; + break; + + case 14: // new_def + if (!(parser.get_iss() >> this->new_def)) + { + this->new_def = false; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for new_def.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 15: // new_def + if (!(parser.get_iss() >> this->solution_equilibria)) + { + this->solution_equilibria = false; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for solution_equilibria.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 16: // n_solution + if (!(parser.get_iss() >> this->n_solution)) + { + this->n_solution = -999; + parser.incr_input_error(); + parser.error_msg("Expected integer value for n_solution.", + PHRQ_io::OT_CONTINUE); + } + break; + case 17: // totals + if (this->totals.read_raw(parser, next_char) != CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected element name and molality for Surface totals.", + PHRQ_io::OT_CONTINUE); + } + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (check) + { + // members that must be defined + if (only_counter_ions_defined == false) + { + parser.incr_input_error(); + parser. + error_msg("Only_counter_ions not defined for SURFACE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (thickness_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Thickness not defined for SURFACE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (type_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Surface type not defined for SURFACE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (dl_type_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Dl_type not defined for SURFACE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (sites_units_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Sites_units not defined for SURFACE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (debye_lengths_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Debye_lengths not defined for SURFACE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (DDL_viscosity_defined == false) + { + parser.incr_input_error(); + parser.error_msg("DDL_viscosity not defined for SURFACE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (DDL_limit_defined == false) + { + parser.incr_input_error(); + parser.error_msg("DDL_limit not defined for SURFACE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (transport_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Transport not defined for SURFACE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + } + this->Sort_comps(); +} + +void +cxxSurface::totalize() +{ + this->totals.clear(); + // component structures + for (size_t i = 0; i != this->surface_comps.size(); i++) + { + cxxSurfaceComp * comp_ptr = &(this->surface_comps[i]); + this->totals.add_extensive(comp_ptr->Get_totals(), 1.0); + this->totals.add("Charge", comp_ptr->Get_charge_balance()); + } + return; +} +void +cxxSurface::add(const cxxSurface & addee_in, LDBLE extensive) + // + // Add surface to "this" surface + // +{ + cxxSurface addee = addee_in; + if (extensive == 0.0) + return; + if (this->surface_comps.size() == 0) + { + this->only_counter_ions = addee.only_counter_ions; + this->dl_type = addee.dl_type; + this->type = addee.type; + this->sites_units = addee.sites_units; + this->thickness = addee.thickness; + this->debye_lengths = addee.debye_lengths; + this->DDL_viscosity = addee.DDL_viscosity; + this->DDL_limit = addee.DDL_limit; + this->solution_equilibria = addee.solution_equilibria; + this->n_solution = addee.n_solution; + this->transport = addee.transport; + } + + for (size_t i_add = 0; i_add < addee.Get_surface_comps().size(); i_add++) + { + const cxxSurfaceComp & comp_add_ptr = addee.Get_surface_comps()[i_add]; + size_t i_this; + for (i_this = 0; i_this < this->surface_comps.size(); i_this++) + { + cxxSurfaceComp & comp_this_ptr = this->surface_comps[i_this]; + if(comp_add_ptr.Get_formula() == comp_this_ptr.Get_formula()) + { + comp_this_ptr.add(addee.surface_comps[i_add], extensive); + break; + } + } + if (i_this == this->surface_comps.size()) + { + + cxxSurfaceComp entity = comp_add_ptr; + entity.multiply(extensive); + this->surface_comps.push_back(entity); + } + } + for (size_t i_add = 0; i_add < addee.Get_surface_charges().size(); i_add++) + { + const cxxSurfaceCharge & charge_add_ptr = addee.Get_surface_charges()[i_add]; + + size_t i_this; + for (i_this = 0; i_this < this->surface_charges.size(); i_this++) + { + cxxSurfaceCharge & charge_this_ptr = this->Get_surface_charges()[i_this]; + if(charge_add_ptr.Get_name() == charge_this_ptr.Get_name()) + { + charge_this_ptr.add(addee.surface_charges[i_add], extensive); + break; + } + } + if (i_this == this->surface_charges.size()) + { + + cxxSurfaceCharge entity = charge_add_ptr; + entity.multiply(extensive); + this->surface_charges.push_back(entity); + } + } +} + +void +cxxSurface::multiply(LDBLE extensive) + // + // Add surface to "this" surface + // +{ + + for (size_t i = 0; i < this->surface_comps.size(); i++) + { + cxxSurfaceComp *comp_ptr = &(this->surface_comps[i]); + comp_ptr->multiply(extensive); + } + for (size_t i = 0; i < this->surface_charges.size(); i++) + { + cxxSurfaceCharge *charge_ptr = &(this->surface_charges[i]); + charge_ptr->multiply(extensive); + } +} +cxxSurfaceComp * cxxSurface:: +Find_comp(std::string str) +{ + for (size_t i = 0; i < this->surface_comps.size(); i++) + { + if (Utilities::strcmp_nocase(str.c_str(), this->surface_comps[i].Get_formula().c_str()) == 0) + return &(this->surface_comps[i]); + } + return NULL; +} + +cxxSurfaceCharge * cxxSurface:: +Find_charge(std::string str) +{ + for (size_t i = 0; i < this->surface_charges.size(); i++) + { + if (Utilities::strcmp_nocase(str.c_str(), this->surface_charges[i].Get_name().c_str()) == 0) + return &(this->surface_charges[i]); + } + return NULL; +} +const cxxSurfaceCharge * cxxSurface:: +Find_charge(std::string str)const +{ + for (size_t i = 0; i < this->surface_charges.size(); i++) + { + if (Utilities::strcmp_nocase(str.c_str(), this->surface_charges[i].Get_name().c_str()) == 0) + return &(this->surface_charges[i]); + } + return NULL; +} +void cxxSurface:: +Sort_comps(void) +{ + // sort comps + { + std::map comp_map; + for (size_t i = 0; i < this->surface_comps.size(); i++) + { + comp_map[this->surface_comps[i].Get_formula()] = this->surface_comps[i]; + } + this->surface_comps.clear(); + std::map::iterator it; + for (it = comp_map.begin(); it != comp_map.end(); it++) + { + this->surface_comps.push_back(it->second); + } + } + + // sort charge too + { + std::map charge_map; + for (size_t i = 0; i < this->surface_charges.size(); i++) + { + charge_map[this->surface_charges[i].Get_name()] = this->surface_charges[i]; + } + this->surface_charges.clear(); + std::map::iterator it; + for (it = charge_map.begin(); it != charge_map.end(); it++) + { + this->surface_charges.push_back(it->second); + } + } +} +/* ---------------------------------------------------------------------- */ +void +cxxSurface::Serialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles) +/* ---------------------------------------------------------------------- */ +{ + + ints.push_back(this->n_user); + ints.push_back((int) this->surface_comps.size()); + { + for (size_t i = 0; i < this->surface_comps.size(); i++) + { + surface_comps[i].Serialize(dictionary, ints, doubles); + } + } + ints.push_back((int) this->surface_charges.size()); + { + for (size_t i = 0; i < this->surface_charges.size(); i++) + { + surface_charges[i].Serialize(dictionary, ints, doubles); + } + } + ints.push_back(this->new_def ? 1 : 0); + ints.push_back((int) this->type); + ints.push_back((int) this->dl_type); + ints.push_back((int) this->sites_units); + ints.push_back(this->only_counter_ions ? 1 : 0); + doubles.push_back(this->thickness); + doubles.push_back(this->debye_lengths); + doubles.push_back(this->DDL_viscosity); + doubles.push_back(this->DDL_limit); + ints.push_back(this->transport ? 1 : 0); + this->totals.Serialize(dictionary, ints, doubles); + ints.push_back(this->solution_equilibria ? 1 : 0); + ints.push_back((int) this->n_solution); + +} + +/* ---------------------------------------------------------------------- */ +void +cxxSurface::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +/* ---------------------------------------------------------------------- */ +{ + this->n_user = ints[ii++]; + this->n_user_end = this->n_user; + this->description = " "; + { + int count = ints[ii++]; + this->surface_comps.clear(); + for (int n = 0; n < count; n++) + { + cxxSurfaceComp sc; + sc.Deserialize(dictionary, ints, doubles, ii, dd); + this->surface_comps.push_back(sc); + } + } + { + int count = ints[ii++]; + this->surface_charges.clear(); + for (int n = 0; n < count; n++) + { + cxxSurfaceCharge sc; + sc.Deserialize(dictionary, ints, doubles, ii, dd); + this->surface_charges.push_back(sc); + } + } + this->new_def = (ints[ii++] != 0); + this->type = (SURFACE_TYPE) ints[ii++]; + this->dl_type = (DIFFUSE_LAYER_TYPE) ints[ii++]; + this->sites_units = (SITES_UNITS) ints[ii++]; + this->only_counter_ions = (ints[ii++] != 0); + this->thickness = doubles[dd++]; + this->debye_lengths = doubles[dd++]; + this->DDL_viscosity = doubles[dd++]; + this->DDL_limit = doubles[dd++]; + this->transport = (ints[ii++] != 0); + this->totals.Deserialize(dictionary, ints, doubles, ii, dd); + this->solution_equilibria = (ints[ii++] != 0); + this->n_solution = ints[ii++]; + +} + + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("diffuse_layer"), // 0 + std::vector< std::string >::value_type("edl"), // 1 + std::vector< std::string >::value_type("only_counter_ions"), // 2 + std::vector< std::string >::value_type("donnan"), // 3 + std::vector< std::string >::value_type("thickness"), // 4 + std::vector< std::string >::value_type("component"), // 5 + std::vector< std::string >::value_type("charge_component"), // 6 + std::vector< std::string >::value_type("type "), // 7 + std::vector< std::string >::value_type("dl_type"), // 8 + std::vector< std::string >::value_type("sites_units"), // 9 + std::vector< std::string >::value_type("debye_lengths"), // 10 + std::vector< std::string >::value_type("ddl_viscosity"), // 11 + std::vector< std::string >::value_type("ddl_limit"), // 12 + std::vector< std::string >::value_type("transport"), // 13 + std::vector< std::string >::value_type("new_def"), // 14 + std::vector< std::string >::value_type("solution_equilibria"), // 15 + std::vector< std::string >::value_type("n_solution"), // 16 + std::vector< std::string >::value_type("totals") // 17 +}; +const std::vector< std::string > cxxSurface::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/Surface.h b/phreeqcpp/Surface.h new file mode 100644 index 00000000..4a197453 --- /dev/null +++ b/phreeqcpp/Surface.h @@ -0,0 +1,98 @@ +#if !defined(SURFACE_H_INCLUDED) +#define SURFACE_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NumKeyword.h" +#include "SurfaceComp.h" +#include "SurfaceCharge.h" +class cxxMix; + +class cxxSurface:public cxxNumKeyword +{ + +public: + enum SURFACE_TYPE + { UNKNOWN_DL, NO_EDL, DDL, CD_MUSIC, CCM }; + enum DIFFUSE_LAYER_TYPE + { NO_DL, BORKOVEK_DL, DONNAN_DL }; + enum SITES_UNITS + { SITES_ABSOLUTE, SITES_DENSITY }; + + cxxSurface(PHRQ_io *io=NULL); + cxxSurface(std::map < int, cxxSurface > &entity_map, cxxMix & mx, + int n_user, PHRQ_io *io=NULL); + ~cxxSurface(); + + //void dump_xml(std::ostream & os, unsigned int indent = 0) const; + void dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out=NULL) const; + void read_raw(CParser & parser, bool check = true); + bool Get_related_phases(void) const; + bool Get_related_rate(void) const; + void totalize(); + cxxSurfaceComp *Find_comp(std::string str); + cxxSurfaceCharge *Find_charge(const std::string str); + const cxxSurfaceCharge *Find_charge(const std::string str)const; + void Sort_comps(); + + void add(const cxxSurface & addee, LDBLE extensive); + void multiply(LDBLE extensive); + + std::vector < cxxSurfaceComp > & Get_surface_comps() {return this->surface_comps;} + const std::vector < cxxSurfaceComp > & Get_surface_comps()const {return this->surface_comps;} + void Set_surface_comps(std::vector < cxxSurfaceComp > &sc) {this->surface_comps = sc;} + std::vector < cxxSurfaceCharge > & Get_surface_charges() {return this->surface_charges;} + void Set_surface_charges(std::vector < cxxSurfaceCharge > &sc) {this->surface_charges = sc;} + bool Get_new_def(void) {return new_def;} + void Set_new_def(bool tf) {new_def = tf;} + SURFACE_TYPE Get_type(void) const {return this->type;} + void Set_type(SURFACE_TYPE t) {this->type = t;} + DIFFUSE_LAYER_TYPE Get_dl_type(void) const {return dl_type;} + void Set_dl_type(DIFFUSE_LAYER_TYPE t) {dl_type = t;} + SITES_UNITS Get_sites_units(void) const {return sites_units;} + void Set_sites_units(SITES_UNITS u) {sites_units = u;} + bool Get_only_counter_ions(void) const {return only_counter_ions;} + void Set_only_counter_ions(bool tf) {only_counter_ions = tf;} + LDBLE Get_thickness(void) const {return thickness;} + void Set_thickness(LDBLE t) {thickness = t;} + LDBLE Get_debye_lengths(void) const {return debye_lengths;} + void Set_debye_lengths(LDBLE t) {debye_lengths = t;} + LDBLE Get_DDL_viscosity(void) const {return DDL_viscosity;} + void Set_DDL_viscosity(LDBLE t) {DDL_viscosity = t;} + LDBLE Get_DDL_limit(void) const {return DDL_limit;} + void Set_DDL_limit(LDBLE t) {DDL_limit = t;} + bool Get_transport(void) const {return transport;} + void Set_transport(bool tf) {transport = tf;} + cxxNameDouble & Get_totals() {return this->totals;} + void Get_totals(cxxNameDouble &nd) {this->totals = nd;} + bool Get_solution_equilibria(void)const {return solution_equilibria;} + void Set_solution_equilibria(bool tf) {solution_equilibria = tf;} + int Get_n_solution(void)const {return n_solution;} + void Set_n_solution(int i) {n_solution = i;} + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + std::vector < cxxSurfaceComp > surface_comps; + std::vector < cxxSurfaceCharge > surface_charges; + bool new_def; + SURFACE_TYPE type; + DIFFUSE_LAYER_TYPE dl_type; + SITES_UNITS sites_units; + bool only_counter_ions; + LDBLE thickness; + LDBLE debye_lengths; + LDBLE DDL_viscosity; + LDBLE DDL_limit; + bool transport; + cxxNameDouble totals; + bool solution_equilibria; + int n_solution; + const static std::vector < std::string > vopts; +}; + +#endif // !defined(SURFACE_H_INCLUDED) diff --git a/phreeqcpp/SurfaceCharge.cxx b/phreeqcpp/SurfaceCharge.cxx new file mode 100644 index 00000000..6ee4ff1c --- /dev/null +++ b/phreeqcpp/SurfaceCharge.cxx @@ -0,0 +1,579 @@ +// SurfaceCharge.cxx: implementation of the cxxSurfaceCharge class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "SurfaceCharge.h" +#include "phqalloc.h" +#include "Dictionary.h" + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxSurfaceCharge::cxxSurfaceCharge(PHRQ_io *io) +: +PHRQ_base(io) +// +// default constructor for cxxSurfaceCharge +// +{ + specific_area = 0.0; + grams = 0.0; + charge_balance = 0.0; + mass_water = 0.0; + la_psi = 0.0; + capacitance[0] = 1.0; + capacitance[1] = 5.0; + sigma0 = sigma1 = sigma2 = sigmaddl = 0; + diffuse_layer_totals.type = cxxNameDouble::ND_ELT_MOLES; +} +cxxSurfaceCharge::~cxxSurfaceCharge() +{ +} + +void +cxxSurfaceCharge::dump_xml(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Surf_Charge element and attributes + s_oss << indent0 << "name=\"" << this->name << "\"" << "\n"; + s_oss << indent0 << "specific_area=\"" << this-> + specific_area << "\"" << "\n"; + s_oss << indent0 << "grams=\"" << this->grams << "\"" << "\n"; + s_oss << indent0 << "charge_balance=\"" << this-> + charge_balance << "\"" << "\n"; + s_oss << indent0 << "mass_water=\"" << this-> + mass_water << "\"" << "\n"; + s_oss << indent0 << "la_psi=\"" << this->la_psi << "\"" << "\n"; + s_oss << indent0 << "capacitance=\"" << this-> + capacitance[0] << " " << this->capacitance[0] << "\"" << "\n"; + + // totals + s_oss << indent0; + s_oss << "diffuse_layer_totals.dump_xml(s_oss, indent + 1); + +} + +void +cxxSurfaceCharge::dump_raw(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Surf_Charge element and attributes + s_oss << indent0 << "# SURFACE_MODIFY candidate identifiers #\n"; + s_oss << indent0 << "-specific_area " << this->specific_area << "\n"; + s_oss << indent0 << "-grams " << this->grams << "\n"; + s_oss << indent0 << "-charge_balance " << this->charge_balance << "\n"; + s_oss << indent0 << "-mass_water " << this->mass_water << "\n"; + s_oss << indent0 << "-la_psi " << this->la_psi << "\n"; + s_oss << indent0 << "-capacitance0 " << this->capacitance[0] << "\n"; + s_oss << indent0 << "-capacitance1 " << this->capacitance[1] << "\n"; + // totals + s_oss << indent0; + s_oss << "-diffuse_layer_totals" << "\n"; + this->diffuse_layer_totals.dump_raw(s_oss, indent + 1); + + // DL species + //s_oss << indent0; + //s_oss << "-diffuse_layer_species" << "\n"; + if (dl_species_map.size() > 0) + { + s_oss << indent0; + s_oss << "-diffuse_layer_species" << "\n"; + std::map::const_iterator it = this->dl_species_map.begin(); + for ( ; it != dl_species_map.end(); it++) + { + s_oss << indent1; + s_oss << it->first << " " << it->second << "\n"; + } + } + s_oss << indent0 << "# Surface workspace variables #\n"; + s_oss << indent0 << "-sigma0 " << this->sigma0 << "\n"; + s_oss << indent0 << "-sigma1 " << this->sigma1 << "\n"; + s_oss << indent0 << "-sigma2 " << this->sigma2 << "\n"; + s_oss << indent0 << "-sigmaddl " << this->sigmaddl << "\n"; + std::map::const_iterator git; + for (git = this->g_map.begin(); git != g_map.end(); git++) + { + s_oss << indent0 << "-g_map " << git->first << "\t"; + s_oss << git->second.Get_g() << "\t"; + s_oss << git->second.Get_dg() << "\t"; + s_oss << git->second.Get_psi_to_z() << "\n"; + } +} + +void +cxxSurfaceCharge::read_raw(CParser & parser, bool check) +{ + std::string str; + + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + opt_save = CParser::OPT_ERROR; + bool specific_area_defined(false); + bool grams_defined(false); + bool charge_balance_defined(false); + bool mass_water_defined(false); + bool la_psi_defined(false); + bool capacitance0_defined(false); + bool capacitance1_defined(false); + bool g_map_first(true); + + for (;;) + { + int opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_KEYWORD; + // Allow return to Surface for more processing + break; + + case 0: // name + warning_msg("-name ignored. Defined with -charge_component."); + break; + + case 1: // specific_area + if (!(parser.get_iss() >> this->specific_area)) + { + this->specific_area = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for specific_area.", + PHRQ_io::OT_CONTINUE); + } + specific_area_defined = true; + break; + + case 2: // grams + if (!(parser.get_iss() >> this->grams)) + { + this->grams = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for grams.", + PHRQ_io::OT_CONTINUE); + } + grams_defined = true; + break; + + + case 3: // charge_balance + if (!(parser.get_iss() >> this->charge_balance)) + { + this->charge_balance = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for charge_balance.", + PHRQ_io::OT_CONTINUE); + } + charge_balance_defined = true; + break; + + case 4: // mass_water + if (!(parser.get_iss() >> this->mass_water)) + { + this->mass_water = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for mass_water.", + PHRQ_io::OT_CONTINUE); + } + mass_water_defined = true; + break; + + + case 5: // la_psi + if (!(parser.get_iss() >> this->la_psi)) + { + this->la_psi = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for la_psi.", + PHRQ_io::OT_CONTINUE); + } + la_psi_defined = true; + break; + + + case 6: // diffuse_layer_totals + if (this->diffuse_layer_totals.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected element name and molality for SurfaceCharge diffuse_layer_totals.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 6; + break; + + case 7: // la_psi1 + parser.warning_msg("-la_psi1 identifier not used"); + break; + + case 8: // la_psi2 + parser.warning_msg("-la_psi2 identifier not used"); + break; + + case 9: // capacitance0 + if (!(parser.get_iss() >> this->capacitance[0])) + { + this->capacitance[0] = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for capacitance0.", + PHRQ_io::OT_CONTINUE); + } + capacitance0_defined = true; + break; + + case 10: // capacitance1 + if (!(parser.get_iss() >> this->capacitance[1])) + { + this->capacitance[1] = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for capacitance1.", + PHRQ_io::OT_CONTINUE); + } + capacitance1_defined = true; + break; + case 11: // sigma0 + if (!(parser.get_iss() >> this->sigma0)) + { + this->sigma0 = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for sigma0.", + PHRQ_io::OT_CONTINUE); + } + break; + case 12: // sigma1 + if (!(parser.get_iss() >> this->sigma1)) + { + this->sigma1 = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for sigma1.", + PHRQ_io::OT_CONTINUE); + } + break; + case 13: // sigma2 + if (!(parser.get_iss() >> this->sigma2)) + { + this->sigma2 = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for sigma2.", + PHRQ_io::OT_CONTINUE); + } + break; + case 14: // sigmaddl + if (!(parser.get_iss() >> this->sigmaddl)) + { + this->sigmaddl = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for sigmaddl.", + PHRQ_io::OT_CONTINUE); + } + break; + case 15: // g_map + { + if (g_map_first) + { + this->g_map.clear(); + g_map_first = false; + } + LDBLE z, dummy; + if (!(parser.get_iss() >> z)) + break; + cxxSurfDL temp_surf_dl; + this->g_map[z] = temp_surf_dl; + std::map::iterator git = g_map.find(z); + if (!(parser.get_iss() >> dummy)) + break; + else + git->second.Set_g(dummy); + if (!(parser.get_iss() >> dummy)) + break; + else + git->second.Set_dg(dummy); + if (!(parser.get_iss() >> dummy)) + break; + else + git->second.Set_psi_to_z(dummy); + } + break; + case 16: // dl_species_map + int s_num; + if (parser.peek_token() != CParser::TT_EMPTY) + { + if (!(parser.get_iss() >> s_num)) + { + parser.incr_input_error(); + parser.error_msg("Expected integer for species number.", + PHRQ_io::OT_CONTINUE); + } + else + { + double d; + if (!(parser.get_iss() >> d)) + { + parser.incr_input_error(); + parser.error_msg("Expected double for species concentration.", + PHRQ_io::OT_CONTINUE); + } + this->dl_species_map[s_num] = d; + } + } + opt_save = 16; + + + + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (check) + { + // members that must be defined + if (specific_area_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Specific_area not defined for SurfaceCharge input.", + PHRQ_io::OT_CONTINUE); + } + if (grams_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Grams not defined for SurfaceCharge input.", + PHRQ_io::OT_CONTINUE); + } + if (charge_balance_defined == false) + { + parser.incr_input_error(); + parser. + error_msg("Charge_balance not defined for SurfaceCharge input.", + PHRQ_io::OT_CONTINUE); + } + if (mass_water_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Mass_water not defined for SurfaceCharge input.", + PHRQ_io::OT_CONTINUE); + } + if (la_psi_defined == false) + { + parser.incr_input_error(); + parser.error_msg("La_psi not defined for SurfaceCharge input.", + PHRQ_io::OT_CONTINUE); + } + if (capacitance0_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Capacitance0 not defined for SurfaceCharge input.", + PHRQ_io::OT_CONTINUE); + } + if (capacitance1_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Capacitance1 not defined for SurfaceCharge input.", + PHRQ_io::OT_CONTINUE); + } + } +} + +void +cxxSurfaceCharge::add(const cxxSurfaceCharge & addee, LDBLE extensive) +{ + if (extensive == 0.0) + return; + + if (this->name.size() == 0 && addee.name.size() == 0) + { + return; + } + assert(this->name == addee.name); + + LDBLE ext1, ext2, f1, f2; + ext1 = this->specific_area * this->grams; + ext2 = addee.specific_area * addee.grams * extensive; + if (ext1 + ext2 != 0) + { + f1 = ext1 / (ext1 + ext2); + f2 = ext2 / (ext1 + ext2); + } + else + { + f1 = 0.5; + f2 = 0.5; + } + this->specific_area = f1 * this->specific_area + f2 * addee.specific_area; + this->grams += addee.grams * extensive; + this->charge_balance += addee.charge_balance * extensive; + this->mass_water += addee.mass_water * extensive; + this->la_psi = this->la_psi * f1 + addee.la_psi * f2; + this->capacitance[0] = + this->capacitance[0] * f1 + this->capacitance[0] * f2; + this->capacitance[1] = + this->capacitance[1] * f1 + this->capacitance[1] * f2; + this->diffuse_layer_totals.add_extensive(addee.diffuse_layer_totals, extensive); +} + +void +cxxSurfaceCharge::multiply(LDBLE extensive) +{ + this->grams *= extensive; + this->charge_balance *= extensive; + this->mass_water *= extensive; + this->diffuse_layer_totals.multiply(extensive); +} +void +cxxSurfaceCharge::Serialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles) +{ + + ints.push_back(dictionary.Find(this->name)); + doubles.push_back(this->specific_area); + doubles.push_back(this->grams); + doubles.push_back(this->charge_balance); + doubles.push_back(this->mass_water); + doubles.push_back(this->la_psi); + doubles.push_back(this->capacitance[0]); + doubles.push_back(this->capacitance[1]); + this->diffuse_layer_totals.Serialize(dictionary, ints, doubles); + doubles.push_back(this->sigma0); + doubles.push_back(this->sigma1); + doubles.push_back(this->sigma2); + doubles.push_back(this->sigmaddl); + ints.push_back((int) this->g_map.size()); + { + std::map::iterator it; + for (it = this->g_map.begin(); it != this->g_map.end(); it++) + { + doubles.push_back(it->first); + it->second.Serialize(dictionary, ints, doubles); + } + } + ints.push_back((int) this->dl_species_map.size()); + { + std::map::iterator it; + for (it = this->dl_species_map.begin(); it != this->dl_species_map.end(); it++) + { + ints.push_back(it->first); + doubles.push_back(it->second); + } + } +} + +void +cxxSurfaceCharge::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +{ + this->name = dictionary.GetWords()[ints[ii++]]; + this->specific_area = doubles[dd++]; + this->grams = doubles[dd++]; + this->charge_balance = doubles[dd++]; + this->mass_water = doubles[dd++]; + this->la_psi = doubles[dd++]; + this->capacitance[0] = doubles[dd++]; + this->capacitance[1] = doubles[dd++]; + this->diffuse_layer_totals.Deserialize(dictionary, ints, doubles, ii, dd); + this->sigma0 = doubles[dd++]; + this->sigma1 = doubles[dd++]; + this->sigma2 = doubles[dd++]; + this->sigmaddl = doubles[dd++]; + { + this->g_map.clear(); + int count = ints[ii++]; + for (int i = 0; i < count; i++) + { + double d = doubles[dd++]; + cxxSurfDL sdl; + sdl.Deserialize(dictionary, ints, doubles, ii, dd); + this->g_map[d] = sdl; + } + } + { + this->dl_species_map.clear(); + int count = ints[ii++]; + for (int i = 0; i < count; i++) + { + int j = ints[ii++]; + double d = doubles[dd++]; + dl_species_map[j] = d; + } + } + + +} +void +cxxSurfDL::Serialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles) +{ + doubles.push_back(this->g); + doubles.push_back(this->dg); + doubles.push_back(this->psi_to_z); +} + +void +cxxSurfDL::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +{ + this->g = doubles[dd++]; + this->dg = doubles[dd++]; + this->psi_to_z = doubles[dd++]; +} +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("name"), // 0 + std::vector< std::string >::value_type("specific_area"), // 1 + std::vector< std::string >::value_type("grams"), // 2 + std::vector< std::string >::value_type("charge_balance"), // 3 + std::vector< std::string >::value_type("mass_water"), // 4 + std::vector< std::string >::value_type("la_psi"), // 5 + std::vector< std::string >::value_type("diffuse_layer_totals"), // 6 + std::vector< std::string >::value_type("la_psi1"), // 7 + std::vector< std::string >::value_type("la_psi2"), // 8 + std::vector< std::string >::value_type("capacitance0"), // 9 + std::vector< std::string >::value_type("capacitance1"), // 10 + std::vector< std::string >::value_type("sigma0"), // 11 + std::vector< std::string >::value_type("sigma1"), // 12 + std::vector< std::string >::value_type("sigma2"), // 13 + std::vector< std::string >::value_type("sigmaddl"), // 14 + std::vector< std::string >::value_type("g_map"), // 15 + std::vector< std::string >::value_type("diffuse_layer_species") // 16 +}; +const std::vector< std::string > cxxSurfaceCharge::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); \ No newline at end of file diff --git a/phreeqcpp/SurfaceCharge.h b/phreeqcpp/SurfaceCharge.h new file mode 100644 index 00000000..669172a1 --- /dev/null +++ b/phreeqcpp/SurfaceCharge.h @@ -0,0 +1,131 @@ +#if !defined(SURFACECHARGE_H_INCLUDED) +#define SURFACECHARGE_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NameDouble.h" +#include "PHRQ_base.h" +class cxxSpeciesDL +{ +public: + cxxSpeciesDL() + { + g_moles = dg_g_moles = dx_moles = dh2o_moles = drelated_moles = 0; + } + LDBLE Get_g_moles(void) const {return g_moles;} + void Set_g_moles(LDBLE t) {g_moles = t;} + LDBLE Get_dg_g_moles(void) const {return dg_g_moles;} + void Set_dg_g_moles(LDBLE t) {dg_g_moles = t;} + LDBLE Get_dx_moles(void) const {return dx_moles;} + void Set_dx_moles(LDBLE t) {dx_moles = t;} + LDBLE Get_dh2o_moles(void) const {return dh2o_moles;} + void Set_dh2o_moles(LDBLE t) {dh2o_moles = t;} + LDBLE Get_drelated_moles(void) const {return drelated_moles;} + void Set_drelated_moles(LDBLE t) {drelated_moles = t;} + + LDBLE *Get_g_moles_address(void) {return &g_moles;} + LDBLE *Get_dg_g_moles_address(void) {return &dg_g_moles;} + LDBLE *Get_dx_moles_address(void) {return &dx_moles;} + LDBLE *Get_dh2o_moles_address(void) {return &dh2o_moles;} + LDBLE *Get_drelated_moles_address(void) {return &drelated_moles;} + +protected: + LDBLE g_moles; + LDBLE dg_g_moles; /* g_moles*dgterm */ + LDBLE dx_moles; + LDBLE dh2o_moles; /* moles*g*Ws/Waq */ + LDBLE drelated_moles; /* for related phase */ +}; +class cxxSurfDL +{ +public: + cxxSurfDL() + { + g = dg = psi_to_z = 0; + } + LDBLE Get_g(void) const {return g;} + void Set_g(LDBLE t) {g = t;} + LDBLE Get_dg(void) const {return dg;} + void Set_dg(LDBLE t) {dg = t;} + LDBLE Get_psi_to_z(void) const {return psi_to_z;} + void Set_psi_to_z(LDBLE t) {psi_to_z = t;} + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + LDBLE g; + LDBLE dg; + LDBLE psi_to_z; +}; +class cxxSurfaceCharge: public PHRQ_base +{ + +public: + + cxxSurfaceCharge(PHRQ_io *io=NULL); + cxxSurfaceCharge(struct surface_charge *, PHRQ_io *io=NULL); + virtual ~cxxSurfaceCharge(); + + struct master *Get_psi_master(); + void dump_xml(std::ostream & os, unsigned int indent = 0) const; + void dump_raw(std::ostream & s_oss, unsigned int indent) const; + void read_raw(CParser & parser, bool check = true); + void add(const cxxSurfaceCharge & comp, LDBLE extensive); + void multiply(LDBLE extensive); + + const std::string &Get_name() const {return this->name;} + void Set_name(const char * f) {this->name = f ? f : "";} + LDBLE Get_specific_area() const {return this->specific_area;} + void Set_specific_area(LDBLE d) {this->specific_area = d;} + LDBLE Get_grams() const {return this->grams;} + void Set_grams(LDBLE d) {this->grams = d;} + LDBLE Get_charge_balance() const {return this->charge_balance;} + void Set_charge_balance(LDBLE d) {this->charge_balance = d;} + LDBLE Get_mass_water() const {return this->mass_water;} + void Set_mass_water(LDBLE d) {this->mass_water = d;} + LDBLE Get_la_psi() const {return this->la_psi;} + void Set_la_psi(LDBLE d) {this->la_psi = d;} + LDBLE Get_capacitance0() const {return this->capacitance[0];} + void Set_capacitance0(LDBLE d) {this->capacitance[0] = d;} + LDBLE Get_capacitance1() const {return this->capacitance[1];} + void Set_capacitance1(LDBLE d) {this->capacitance[1] = d;} + LDBLE Get_sigma0() const {return this->sigma0;} + void Set_sigma0(LDBLE d) {this->sigma0 = d;} + LDBLE Get_sigma1() const {return this->sigma1;} + void Set_sigma1(LDBLE d) {this->sigma1 = d;} + LDBLE Get_sigma2() const {return this->sigma2;} + void Set_sigma2(LDBLE d) {this->sigma2 = d;} + LDBLE Get_sigmaddl() const {return this->sigmaddl;} + void Set_sigmaddl(LDBLE d) {this->sigmaddl = d;} + cxxNameDouble & Get_diffuse_layer_totals(void) { return this->diffuse_layer_totals; } + const cxxNameDouble & Get_diffuse_layer_totals(void)const { return this->diffuse_layer_totals; } + void Set_diffuse_layer_totals(cxxNameDouble & nd) {this->diffuse_layer_totals = nd;} + std::map &Get_g_map(void) {return g_map;} + void Set_g_map(std::map & t) {g_map = t;} + std::map &Get_z_gMCD_map(void) { return z_gMCD_map; } // z, exp(-zF Psi / RT) * fraction of dl water + std::map & Get_dl_species_map(void) {return this->dl_species_map;} + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + std::string name; + LDBLE specific_area; + LDBLE grams; + LDBLE charge_balance; + LDBLE mass_water; + LDBLE la_psi; + LDBLE capacitance[2]; + cxxNameDouble diffuse_layer_totals; + // workspace variables + LDBLE sigma0, sigma1, sigma2, sigmaddl; + std::map g_map; + std::map z_gMCD_map; + const static std::vector < std::string > vopts; + std::map dl_species_map; +}; + +#endif // !defined(SURFACECHARGE_H_INCLUDED) diff --git a/phreeqcpp/SurfaceComp.cxx b/phreeqcpp/SurfaceComp.cxx new file mode 100644 index 00000000..5a23eb16 --- /dev/null +++ b/phreeqcpp/SurfaceComp.cxx @@ -0,0 +1,499 @@ +// SurfaceComp.cxx: implementation of the cxxSurfaceComp class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Phreeqc.h" +#include "SurfaceComp.h" +#include "phqalloc.h" +#include "Dictionary.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxSurfaceComp::cxxSurfaceComp(PHRQ_io *io) +: +PHRQ_base(io) +// +// default constructor for cxxSurfaceComp +// +{ + formula_z = 0.0; + moles = 0.0; + totals.type = cxxNameDouble::ND_ELT_MOLES; + la = 0.0; + charge_balance = 0; + phase_proportion = 0.0; + Dw = 0.0; +} +cxxSurfaceComp::~cxxSurfaceComp() +{ +} + +void +cxxSurfaceComp::dump_xml(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Surf_Comp element and attributes + + //s_oss << indent0 << "formula=\"" << this->formula << "\"" << "\n"; + s_oss << indent0 << "formula_z=\"" << this->formula_z << "\"" << "\n"; + s_oss << indent0 << "moles=\"" << this->moles << "\"" << "\n"; + s_oss << indent0 << "la=\"" << this->la << "\"" << "\n"; + s_oss << indent0 << "charge_balance=\"" << this->charge_balance << "\"" << "\n"; + s_oss << indent0 << "phase_proportion=\"" << this->phase_proportion << "\"" << "\n"; + s_oss << indent0 << "Dw=\"" << this->Dw << "\"" << "\n"; + s_oss << indent0 << "charge_name=\"" << this->charge_name << "\"" << "\n"; + if (this->phase_name.size() != 0) + { + s_oss << indent0 << "phase_name=\"" << this->phase_name << "\"" << "\n"; + } + if (this->rate_name.size() != 0) + { + s_oss << indent0 << "rate_name=\"" << this->rate_name << "\"" << "\n"; + } + + // totals + s_oss << indent0; + s_oss << "totals.dump_xml(s_oss, indent + 1); +} + +void +cxxSurfaceComp::dump_raw(std::ostream & s_oss, unsigned int indent) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + s_oss << indent0 << "# SURFACE_MODIFY candidate identifiers #\n"; + s_oss << indent0 << "-formula_z " << this->formula_z << "\n"; + s_oss << indent0 << "-moles " << this->moles << "\n"; + s_oss << indent0 << "-la " << this->la << "\n"; + s_oss << indent0 << "-charge_balance " << this->charge_balance << "\n"; + if (this->phase_name.size() != 0) + { + s_oss << indent0 << "-phase_name " << this->phase_name << "\n"; + } + if (this->rate_name.size() != 0) + { + s_oss << indent0 << "-rate_name " << this->rate_name << "\n"; + } + s_oss << indent0 << "-phase_proportion " << this->phase_proportion << "\n"; + s_oss << indent0 << "-Dw " << this->Dw << "\n"; + s_oss << indent0 << "-charge_name " << this->charge_name << "\n"; + s_oss << indent0 << "-master_element " << this->master_element << "\n"; + // totals + s_oss << indent0; + s_oss << "-totals" << "\n"; + this->totals.dump_raw(s_oss, indent + 1); +} + +void +cxxSurfaceComp::read_raw(CParser & parser, bool check) +{ + std::string str; + + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + opt_save = CParser::OPT_ERROR; + + bool master_element_defined(false); + bool charge_name_defined(false); + bool moles_defined(false); + bool la_defined(false); + bool charge_balance_defined(false); + bool formula_z_defined(false); + bool Dw_defined(false); + bool totals_defined(false); + + for (;;) + { + int opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_KEYWORD; + // Allow return to Surface for more processing + break; + + case 0: // formula + output_msg("-formula is obsolete in surface comp raw"); + break; + + case 1: // moles + if (!(parser.get_iss() >> this->moles)) + { + this->moles = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for moles.", + PHRQ_io::OT_CONTINUE); + } + moles_defined = true; + break; + + case 2: // la + if (!(parser.get_iss() >> this->la)) + { + this->la = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for la.", + PHRQ_io::OT_CONTINUE); + } + la_defined = true; + break; + case 3: // charge_number not used + parser.warning_msg("-charge_number identifier is obsolete."); + break; + + case 4: // charge_balance + if (!(parser.get_iss() >> this->charge_balance)) + { + this->charge_balance = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for charge_balance.", + PHRQ_io::OT_CONTINUE); + } + charge_balance_defined = true; + break; + + case 5: // phase_name + if (!(parser.get_iss() >> str)) + { + this->phase_name.clear(); + parser.incr_input_error(); + parser.error_msg("Expected string value for phase_name.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->phase_name = str; + } + break; + + case 6: // rate_name + if (!(parser.get_iss() >> str)) + { + this->rate_name.clear(); + parser.incr_input_error(); + parser.error_msg("Expected string value for rate_name.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->rate_name = str; + } + break; + + case 7: // phase_proportion + if (!(parser.get_iss() >> this->phase_proportion)) + { + this->phase_proportion = 0; + parser.incr_input_error(); + parser. + error_msg("Expected numeric value for phase_proportion.", + PHRQ_io::OT_CONTINUE); + } + break; + + case 8: // totals + if (this->totals.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected element name and molality for SurfaceComp totals.", + PHRQ_io::OT_CONTINUE); + } + totals_defined = true; + opt_save = 8; + break; + + case 9: // formula_z + if (!(parser.get_iss() >> this->formula_z)) + { + this->formula_z = 0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for formula_z.", + PHRQ_io::OT_CONTINUE); + } + formula_z_defined = true; + break; + + case 10: // formula_totals + parser.warning_msg("-formula_totals is an obsolete identifier."); + break; + + case 11: // Dw + if (!(parser.get_iss() >> this->Dw)) + { + this->Dw = 0.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for Dw.", + PHRQ_io::OT_CONTINUE); + } + Dw_defined = true; + break; + case 12: // charge_name + if (!(parser.get_iss() >> str)) + { + this->charge_name.clear(); + parser.incr_input_error(); + parser.error_msg("Expected string value for charge_name.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->charge_name = str; + } + charge_name_defined = true; + break; + case 13: // master_element + if (!(parser.get_iss() >> str)) + { + this->master_element.clear(); + parser.incr_input_error(); + parser.error_msg("Expected string value for master_element.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->master_element = str; + } + master_element_defined = true; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (check) + { + // members that must be defined + if (charge_name_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Charge_name not defined for SurfaceComp input.", + PHRQ_io::OT_CONTINUE); + } + if (formula_z_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Formula_z not defined for ExchComp input.", + PHRQ_io::OT_CONTINUE); + } + if (moles_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Moles not defined for SurfaceComp input.", + PHRQ_io::OT_CONTINUE); + } + if (la_defined == false) + { + parser.incr_input_error(); + parser.error_msg("La not defined for SurfaceComp input.", + PHRQ_io::OT_CONTINUE); + } + + if (charge_balance_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Charge_balance not defined for SurfaceComp input.", + PHRQ_io::OT_CONTINUE); + } + if (Dw_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Dw not defined for SurfaceComp input.", + PHRQ_io::OT_CONTINUE); + } + if (master_element_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Master_element name not defined for SurfaceComp input.", + PHRQ_io::OT_CONTINUE); + } + if (totals_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Totals not defined for SurfaceComp input.", + PHRQ_io::OT_CONTINUE); + } + } +} + +void +cxxSurfaceComp::add(const cxxSurfaceComp & addee, LDBLE extensive) +{ + if (extensive == 0.0) + return; + if (addee.formula.size() == 0) + return; + + if (this->formula.size() == 0 && addee.formula.size() == 0) + { + return; + } + assert(this->formula == addee.formula); + assert(this->formula_z == addee.formula_z); + if (this->formula.size() == 0 && addee.formula.size() != 0) + { + this->formula = addee.formula; + } + + // this and addee must have same formula + // otherwise generate a new exchcomp with multiply + LDBLE ext1, ext2, f1, f2; + ext1 = this->moles; + ext2 = addee.moles * extensive; + if (ext1 + ext2 != 0) + { + f1 = ext1 / (ext1 + ext2); + f2 = ext2 / (ext1 + ext2); + } + else + { + f1 = 0.5; + f2 = 0.5; + } + this->moles += addee.moles * extensive; + this->totals.add_extensive(addee.totals, extensive); + this->la = f1 * this->la + f2 * addee.la; + this->charge_balance += addee.charge_balance * extensive; + if (this->phase_name != addee.phase_name) + { + std::ostringstream oss; + oss << + "Cannot mix two Surface components with same formula and different related phases, " + << this->formula; + error_msg(oss.str().c_str(), CONTINUE); + return; + } + else if (this->phase_name.size() != 0) + { + this->phase_proportion = + this->phase_proportion * f1 + addee.phase_proportion * f2; + } + + if (this->rate_name != addee.rate_name) + { + std::ostringstream oss; + oss << + "Cannot mix two exchange components with same formula and different related kinetics, " + << this->formula; + error_msg(oss.str().c_str(), CONTINUE); + return; + } + else if (this->rate_name.size() != 0) + { + //LDBLE phase_proportion; + this->phase_proportion = + this->phase_proportion * f1 + addee.phase_proportion * f2; + } + if ((this->rate_name.size() != 0 && addee.phase_name.size() != 0) || + (this->phase_name.size() != 0 && addee.rate_name.size() != 0)) + { + std::ostringstream oss; + oss << + "Cannot mix exchange components related to phase with exchange components related to kinetics, " + << this->formula; + error_msg(oss.str().c_str(), CONTINUE); + return; + } + +} +void +cxxSurfaceComp::multiply(LDBLE extensive) +{ + this->moles *= extensive; + + this->totals.multiply(extensive); + + this->charge_balance *= extensive; +} +void +cxxSurfaceComp::Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles) +{ + ints.push_back(dictionary.Find(this->formula)); + doubles.push_back(this->formula_z); + doubles.push_back(this->moles); + this->totals.Serialize(dictionary, ints, doubles); + doubles.push_back(this->la); + ints.push_back(dictionary.Find(this->charge_name)); + doubles.push_back(this->charge_balance); + ints.push_back(dictionary.Find(this->phase_name)); + doubles.push_back(this->phase_proportion); + ints.push_back(dictionary.Find(this->rate_name)); + doubles.push_back(this->Dw); + ints.push_back(dictionary.Find(this->master_element)); +} + +void +cxxSurfaceComp::Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd) +{ + this->formula = dictionary.GetWords()[ints[ii++]]; + this->formula_z = doubles[dd++]; + this->moles = doubles[dd++]; + this->totals.Deserialize(dictionary, ints, doubles, ii, dd); + this->la = doubles[dd++]; + this->charge_name = dictionary.GetWords()[ints[ii++]]; + this->charge_balance = doubles[dd++]; + this->phase_name = dictionary.GetWords()[ints[ii++]]; + this->phase_proportion = doubles[dd++]; + this->rate_name = dictionary.GetWords()[ints[ii++]]; + this->Dw = doubles[dd++]; + this->master_element = dictionary.GetWords()[ints[ii++]]; +} + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("formula"), // 0 + std::vector< std::string >::value_type("moles"), // 1 + std::vector< std::string >::value_type("la"), // 2 + std::vector< std::string >::value_type("charge_number"), // 3 + std::vector< std::string >::value_type("charge_balance"), // 4 + std::vector< std::string >::value_type("phase_name"), // 5 + std::vector< std::string >::value_type("rate_name"), // 6 + std::vector< std::string >::value_type("phase_proportion"), // 7 + std::vector< std::string >::value_type("totals"), // 8 + std::vector< std::string >::value_type("formula_z"), // 9 + std::vector< std::string >::value_type("formula_totals"), // 10 + std::vector< std::string >::value_type("dw"), // 11 + std::vector< std::string >::value_type("charge_name"), // 12 + std::vector< std::string >::value_type("master_element") // 13 +}; +const std::vector< std::string > cxxSurfaceComp::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/SurfaceComp.h b/phreeqcpp/SurfaceComp.h new file mode 100644 index 00000000..d3708acf --- /dev/null +++ b/phreeqcpp/SurfaceComp.h @@ -0,0 +1,70 @@ +#if !defined(SURFACECOMP_H_INCLUDED) +#define SURFACECOMP_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector +#include "NameDouble.h" + +class cxxSurfaceComp: public PHRQ_base +{ + +public: + + cxxSurfaceComp(PHRQ_io *io=NULL); + virtual ~cxxSurfaceComp(); + + void dump_xml(std::ostream & os, unsigned int indent = 0) const; + void dump_raw(std::ostream & s_oss, unsigned int indent) const; + void read_raw(CParser & parser, bool check = true); + void add(const cxxSurfaceComp & comp, LDBLE extensive); + void multiply(LDBLE extensive); + + const std::string &Get_formula() const {return this->formula;} + void Set_formula(const char * f) {this->formula = f ? f : "";} + LDBLE Get_formula_z(void) const {return formula_z;}; + void Set_formula_z(LDBLE t) {this->formula_z = t;} + LDBLE Get_moles(void) const {return moles;} + void Set_moles(LDBLE t) {this->moles = t;} + cxxNameDouble & Get_totals() {return (this->totals);} + void Set_totals(cxxNameDouble & nd) {this->totals = nd;} + LDBLE Get_la(void) const {return la;}; + void Set_la(LDBLE t) {this->la = t;} + LDBLE Get_charge_balance() const {return this->charge_balance;} + void Set_charge_balance(LDBLE d) {this->charge_balance = d;} + const std::string &Get_charge_name() const {return this->charge_name;} + void Set_charge_name(const char * f) {this->charge_name = f ? f : "";} + const std::string &Get_phase_name() const {return this->phase_name;} + void Set_phase_name(const char * f) {this->phase_name = f ? f : "";} + LDBLE Get_phase_proportion(void) const {return phase_proportion;} + void Set_phase_proportion(LDBLE d) {this->phase_proportion = d;} + const std::string &Get_rate_name() const {return this->rate_name;} + void Set_rate_name(const char * f) {this->rate_name = f ? f : "";} + LDBLE Get_Dw(void) const {return Dw;} + void Set_Dw(LDBLE d) {this->Dw = d;} + const std::string &Get_master_element() const {return this->master_element;} + void Set_master_element(const char * f) {this->master_element = f ? f : "";} + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + std::string formula; + LDBLE formula_z; + LDBLE moles; + cxxNameDouble totals; + LDBLE la; + std::string charge_name; + LDBLE charge_balance; + std::string phase_name; + LDBLE phase_proportion; + std::string rate_name; + LDBLE Dw; + std::string master_element; + const static std::vector < std::string > vopts; +public: + +}; + +#endif // !defined(SURFACECOMP_H_INCLUDED) diff --git a/phreeqcpp/System.cxx b/phreeqcpp/System.cxx new file mode 100644 index 00000000..467363de --- /dev/null +++ b/phreeqcpp/System.cxx @@ -0,0 +1,95 @@ +#include // std::replace + +#include "Phreeqc.h" +#include "System.h" +#include "SSassemblage.h" +#include "Solution.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "cxxKinetics.h" +#include "PPassemblage.h" +#include "SS.h" +#include "SSassemblage.h" +#include "Surface.h" +#include "cxxMix.h" +#include "Reaction.h" +#include "Temperature.h" + +cxxSystem::cxxSystem(PHRQ_io *io) +: +PHRQ_base(io) +{ + this->solution = NULL; + this->exchange = NULL; + this->ppassemblage = NULL; + this->gasphase = NULL; + this->ssassemblage = NULL; + this->kinetics = NULL; + this->surface = NULL; + this->mix = NULL; + this->reaction = NULL; + this->temperature = NULL; + this->pressure = NULL; +} +cxxSystem::~cxxSystem(void) +{ +} +void +cxxSystem::Initialize(void) +{ + this->solution = NULL; + this->exchange = NULL; + this->ppassemblage = NULL; + this->gasphase = NULL; + this->ssassemblage = NULL; + this->kinetics = NULL; + this->surface = NULL; + this->mix = NULL; + this->reaction = NULL; + this->temperature = NULL; + this->pressure = NULL; +} +void +cxxSystem::totalize(Phreeqc * phreeqc_ptr) +{ + //initialize + this->totals.clear(); + //add solution + if (this->solution != NULL) + { + char token[MAX_LENGTH]; + strcpy(token, "O"); + this->totals[token] = this->solution->Get_total_o(); + strcpy(token, "H"); + this->totals[token] = this->solution->Get_total_h(); + strcpy(token, "Charge"); + this->totals[token] = this->solution->Get_cb(); + this->totals.add_extensive(this->solution->Get_totals(), 1.0); + } + if (this->exchange != NULL) + { + this->exchange->totalize(); + this->totals.add_extensive(this->exchange->Get_totals(), 1.0); + } + if (this->ppassemblage != NULL) + { + this->ppassemblage->totalize(phreeqc_ptr); + this->totals.add_extensive(this->ppassemblage->Get_assemblage_totals(), 1.0); + } + if (this->gasphase != NULL) + { + this->gasphase->totalize(phreeqc_ptr); + this->totals.add_extensive(this->gasphase->Get_totals(), 1.0); + } + if (this->ssassemblage != NULL) + { + this->ssassemblage->totalize(phreeqc_ptr); + this->totals.add_extensive(this->ssassemblage->Get_totals(), 1.0); + } + if (this->surface != NULL) + { + this->surface->totalize(); + this->totals.add_extensive(this->surface->Get_totals(), 1.0); + } + return; +} diff --git a/phreeqcpp/System.h b/phreeqcpp/System.h new file mode 100644 index 00000000..328ed235 --- /dev/null +++ b/phreeqcpp/System.h @@ -0,0 +1,89 @@ +#if !defined(SYSTEM_H_INCLUDED) +#define SYSTEM_H_INCLUDED +#include "NameDouble.h" +#include "PHRQ_base.h" +class cxxSolution; +class cxxExchange; +class cxxGasPhase; +class cxxKinetics; +class cxxPPassemblage; +class cxxSSassemblage; +class cxxSurface; +class cxxReaction; +class cxxTemperature; +class cxxPressure; +class cxxMix; + +class cxxSystem: public PHRQ_base +{ +public: + cxxSystem(PHRQ_io *io=NULL); + virtual ~cxxSystem(void); + void Initialize(void); + void Set_Solution(cxxSolution * entity) + { + this->solution = entity; + } + void Set_Exchange(cxxExchange * entity) + { + this->exchange = entity; + } + void Set_PPassemblage(cxxPPassemblage * entity) + { + this->ppassemblage = entity; + } + void Set_GasPhase(cxxGasPhase * entity) + { + this->gasphase = entity; + } + void Set_SSassemblage(cxxSSassemblage * entity) + { + this->ssassemblage = entity; + } + void Set_Kinetics(cxxKinetics * entity) + { + this->kinetics = entity; + } + void Set_Surface(cxxSurface * entity) + { + this->surface = entity; + } + void Set_Mix(cxxMix * entity) + { + this->mix = entity; + } + void Set_Reaction(cxxReaction * entity) + { + this->reaction = entity; + } + void Set_Temperature(cxxTemperature * entity) + { + this->temperature = entity; + } + void Set_Pressure(cxxPressure * entity) + { + this->pressure = entity; + } + void totalize(Phreeqc * phreeqc_ptr); + cxxNameDouble &Get_Totals(void) + { + return this->totals; + } + +protected: + cxxSolution * solution; + cxxExchange * exchange; + cxxPPassemblage * ppassemblage; + cxxGasPhase * gasphase; + cxxSSassemblage * ssassemblage; + cxxKinetics * kinetics; + cxxSurface * surface; + cxxMix * mix; + cxxReaction * reaction; + cxxTemperature * temperature; + cxxPressure * pressure; + cxxNameDouble totals; +}; + + +#endif // !defined(SYSTEM_H_INCLUDED) diff --git a/phreeqcpp/Temperature.cxx b/phreeqcpp/Temperature.cxx new file mode 100644 index 00000000..a1df2da2 --- /dev/null +++ b/phreeqcpp/Temperature.cxx @@ -0,0 +1,452 @@ +// Temperature.cxx: implementation of the cxxTemperature class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Parser.h" +#include "Phreeqc.h" +#include "Temperature.h" +#include "phqalloc.h" + + + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxTemperature::cxxTemperature(PHRQ_io *io) + // + // default constructor for cxxTemperature + // +: cxxNumKeyword(io) +{ + countTemps = 0; + equalIncrements = false; +} +cxxTemperature::~cxxTemperature() +{ +} + +#ifdef SKIP +void +cxxTemperature::dump_xml(std::ostream & s_oss, unsigned int indent) const const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Temperature element and attributes + s_oss << indent0; + s_oss << " + pitzer_temperature_gammas << "\"" << "\n"; + + // components + s_oss << indent1; + s_oss << "::const_iterator it = + temperatureComps.begin(); it != temperatureComps.end(); ++it) + { + it->dump_xml(s_oss, indent + 2); + } + + return; +} +#endif + +int +cxxTemperature::read(CParser & parser) +{ +/* + * Reads temperature data for reaction steps + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + // Number and description set in read_reaction_temperature + + PHRQ_io::LINE_TYPE lt; + bool done = false; + for (;;) + { + std::istream::pos_type ptr; + std::istream::pos_type next_char = 0; + std::string token, str; + lt = parser.check_line(str, false, true, true, true); + + if (lt == PHRQ_io::LT_EMPTY || + lt == PHRQ_io::LT_KEYWORD || + lt == PHRQ_io::LT_EOF) + { + break; + } + if (lt == PHRQ_io::LT_OPTION) + { + this->error_msg("Expected numeric value for temperatures.", PHRQ_io::OT_CONTINUE); + break; + } + + if (done) + { + this->error_msg("Unknown input following equal increment definition.", PHRQ_io::OT_CONTINUE); + continue; + } + + // LT_OK + + for (;;) + { + if (done) break; + // new token + std::string token; + CParser::TOKEN_TYPE k = parser.copy_token(token, next_char); + + // need new line + if (k == CParser::TT_EMPTY) + { + break; + } + + // read a pressure + if (k == CParser::TT_DIGIT) + { + std::istringstream iss(token); + LDBLE d; + if (!(iss >> d)) + { + this->error_msg("Expected numeric value for temperatures.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->temps.push_back(d); + } + continue; + } + + // non digit, must be "in" + if (k == CParser::TT_UPPER || k == CParser::TT_LOWER) + { + if (this->temps.size() != 2) + { + this->error_msg("To define equal increments, exactly two temperatures should be defined.", CONTINUE); + } + else + { + int i = parser.copy_token(token, next_char); + if (i == EMPTY) + { + error_msg("To define equal increments, define 'in n steps'.", CONTINUE); + } + else + { + std::istringstream iss(token); + if ((iss >> i) && i > 0) + { + this->equalIncrements = true; + this->countTemps = i; + } + else + { + error_msg("Unknown input for temperature steps.", CONTINUE); + } + } + done = true; + } + if (k == CParser::TT_UNKNOWN) + { + error_msg("Unknown input for temperature steps.", CONTINUE); + } + } + } // tokens + } // lines + return lt; +} + +void +cxxTemperature::dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + s_oss << indent0; + int n_user_local = (n_out != NULL) ? *n_out : this->n_user; + s_oss << "REACTION_TEMPERATURE_RAW " << n_user_local << " " << this->description << "\n"; + + s_oss << indent1; + s_oss << "-count_temps " << this->Get_countTemps() << "\n"; + + s_oss << indent1; + s_oss << "-equal_increments " << this->equalIncrements << "\n"; + + // Temperature element and attributes + + s_oss << indent1; + s_oss << "-temps " << "\n"; + { + int i = 0; + s_oss << indent2; + for (std::vector < LDBLE >::const_iterator it = this->temps.begin(); + it != this->temps.end(); it++) + { + if (i++ == 5) + { + s_oss << "\n"; + s_oss << indent2; + i = 0; + } + s_oss << *it << " "; + } + s_oss << "\n"; + } +} + +void +cxxTemperature::read_raw(CParser & parser, bool check) +{ + LDBLE d; + CParser::TOKEN_TYPE k; + // clear steps for modify operation, if pressures are read + bool cleared_once = false; + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + bool useLastLine(false); + + // Read temperature number and description + this->read_number_description(parser); + + opt_save = CParser::OPT_ERROR; + bool equalIncrements_defined(false); + bool countTemps_defined(false); + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + opt = parser.getOptionFromLastLine(vopts, next_char, true); + } + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input in TEMPERATURE_COMP_RAW keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + useLastLine = false; + break; + + case 0: // temps + if (!cleared_once) + { + this->temps.clear(); + cleared_once = true; + } + while ((k = + parser.copy_token(token, next_char)) == CParser::TT_DIGIT) + { + std::istringstream iss(token); + if (!(iss >> d)) + { + parser.incr_input_error(); + parser.error_msg("Expected numeric value for temps.", + PHRQ_io::OT_CONTINUE); + } + else + { + this->temps.push_back(d); + } + } + opt_save = 0; + useLastLine = false; + break; + + case 1: // equal_increments + if (!(parser.get_iss() >> this->equalIncrements)) + { + this->equalIncrements = 0; + parser.incr_input_error(); + parser. + error_msg("Expected boolean value for equalIncrements.", + PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + useLastLine = false; + equalIncrements_defined = true; + break; + + case 2: // countTemps + if (!(parser.get_iss() >> this->countTemps)) + { + this->countTemps = 0; + parser.incr_input_error(); + parser.error_msg("Expected integer value for countTemps.", + PHRQ_io::OT_CONTINUE); + } + opt_save = CParser::OPT_DEFAULT; + useLastLine = false; + countTemps_defined = true; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + // members that must be defined + if (check) + { + if (equalIncrements_defined == false) + { + parser.incr_input_error(); + parser. + error_msg + ("Equal_increments not defined for REACTION_TEMPERATURE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (countTemps_defined == false) + { + parser.incr_input_error(); + parser. + error_msg + ("Count_temps not defined for REACTION_TEMPERATURE_RAW input.", + PHRQ_io::OT_CONTINUE); + } + } +} +/* ---------------------------------------------------------------------- */ +LDBLE cxxTemperature:: +Temperature_for_step(int step_number) +/* ---------------------------------------------------------------------- */ +{ +/* + * Determine pressure of reaction step + */ + LDBLE t_temp; + if (this->temps.size() == 0) + { + t_temp = 1; + } + else if (this->equalIncrements) + { + if (this->temps.size() != 2) + { + error_msg("Number of temperatures not equal to 2 for equal increments.", 0); + } + if (step_number > this->countTemps) + { + t_temp = this->temps[1]; + } + else + { + LDBLE denom; + denom = (this->countTemps <= 1) ? 1 : (LDBLE) (this->countTemps - 1); + t_temp = this->temps[0] + ( this->temps[1] - this->temps[0]) * + ((LDBLE) (step_number - 1)) / (denom); + } + } + else + { + if (step_number > (int) this->temps.size()) + { + t_temp = this->temps[this->temps.size() - 1]; + } + else + { + t_temp = this->temps[step_number - 1]; + } + + } + + return (t_temp); +} +int cxxTemperature:: +Get_countTemps(void) const +{ + if (equalIncrements) + { + return this->countTemps; + } + return (int) this->temps.size(); +} +void +cxxTemperature::Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles) +{ + ints.push_back(this->n_user); + { + ints.push_back((int) this->temps.size()); + for (size_t i = 0; i < this->temps.size(); i++) + { + doubles.push_back(temps[i]); + } + } + ints.push_back(this->countTemps); + ints.push_back(this->equalIncrements ? 1 : 0); +} + +void +cxxTemperature::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +{ + this->n_user = ints[ii++]; + this->n_user_end = this->n_user; + this->description = " "; + + { + int count = ints[ii++]; + this->temps.clear(); + for (int i = 0; i < count; i++) + { + this->temps.push_back(doubles[dd++]); + } + } + this->countTemps = ints[ii++]; + this->equalIncrements = (ints[ii++] != 0); +} +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("temps"), //0 + std::vector< std::string >::value_type("equal_increments"), //1 + std::vector< std::string >::value_type("count_temps") //2 +}; +const std::vector< std::string > cxxTemperature::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); \ No newline at end of file diff --git a/phreeqcpp/Temperature.h b/phreeqcpp/Temperature.h new file mode 100644 index 00000000..6af70b38 --- /dev/null +++ b/phreeqcpp/Temperature.h @@ -0,0 +1,42 @@ +#if !defined(TEMPERATURE_H_INCLUDED) +#define TEMPERATURE_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector + +#include "NumKeyword.h" + +class cxxTemperature:public cxxNumKeyword +{ + + public: + cxxTemperature(PHRQ_io *io=NULL); + ~cxxTemperature(); + + //void dump_xml(std::ostream& os, unsigned int indent = 0)const; + + void dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out=NULL) const; + + void read_raw(CParser & parser, bool check = false); + int read(CParser & parser); + LDBLE Temperature_for_step(int step_number); + std::vector & Get_temps(void) {return temps;} + const std::vector & Get_temps(void)const {return temps;} + int Get_countTemps(void) const; + void Set_countTemps(int i) {countTemps = i;} + bool Get_equalIncrements(void) const {return equalIncrements;} + void Set_equalIncrements(bool tf) {equalIncrements = tf;} + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + +protected: + std::vector < LDBLE >temps; + int countTemps; + bool equalIncrements; + const static std::vector < std::string > vopts; +}; + +#endif // !defined(TEMPERATURE_H_INCLUDED) diff --git a/phreeqcpp/Use.cpp b/phreeqcpp/Use.cpp new file mode 100644 index 00000000..e6f8c349 --- /dev/null +++ b/phreeqcpp/Use.cpp @@ -0,0 +1,70 @@ +#include +#include "Use.h" + +cxxUse::cxxUse() +{ + this->init(); + +} + + +cxxUse::~cxxUse(void) +{ +} +void cxxUse:: +init(void) +{ + solution_in = false; + n_solution_user = -999; + solution_ptr = NULL; + + pp_assemblage_in = false; + n_pp_assemblage_user = -999; + pp_assemblage_ptr = NULL; + + mix_in = false; + n_mix_user = -999; + mix_ptr = NULL; + n_mix_user_orig = -999; + + reaction_in = false; + n_reaction_user = -999; + reaction_ptr = NULL; + + exchange_in = false; + n_exchange_user = -999; + exchange_ptr = NULL; + + kinetics_in = false; + n_kinetics_user = -999; + kinetics_ptr = NULL; + + surface_in = false; + n_surface_user = -999; + surface_ptr = NULL; + + pressure_in = false; + n_pressure_user = -999; + pressure_ptr = NULL; + + temperature_in = false; + n_temperature_user = -999; + temperature_ptr = NULL; + + inverse_in = false; + n_inverse_user = -999; + inverse_ptr = NULL; + + gas_phase_in = false; + n_gas_phase_user = -999; + gas_phase_ptr = NULL; + + ss_assemblage_in = false; + n_ss_assemblage_user = -999; + ss_assemblage_ptr = NULL; + + trans_in = false; + advect_in = false; +} + + diff --git a/phreeqcpp/Use.h b/phreeqcpp/Use.h new file mode 100644 index 00000000..90a22a5c --- /dev/null +++ b/phreeqcpp/Use.h @@ -0,0 +1,159 @@ +#if !defined(USE_H_INCLUDED) +#define USE_H_INCLUDED +class cxxPPassemblage; +class cxxMix; +class cxxReaction; +class cxxExchange; +class cxxGasPhase; +class cxxPressure; +class cxxTemperature; +class cxxSSassemblage; +class cxxKinetics; +class cxxSurface; +class cxxSolution; + +class cxxUse +{ +public: + cxxUse(); + virtual ~cxxUse(void); + void init(void); + + bool Get_solution_in(void) const {return this->solution_in;} + bool Get_pp_assemblage_in(void) const {return this->pp_assemblage_in;} + bool Get_mix_in(void) const {return this->mix_in;} + bool Get_reaction_in(void) const {return this->reaction_in;} + bool Get_exchange_in(void) const {return this->exchange_in;} + bool Get_kinetics_in(void) const {return this->kinetics_in;} + bool Get_surface_in(void) const {return this->surface_in;} + bool Get_pressure_in(void) const {return this->pressure_in;} + bool Get_temperature_in(void) const {return this->temperature_in;} + bool Get_gas_phase_in(void) const {return this->gas_phase_in;} + bool Get_inverse_in(void) const {return this->inverse_in;} + bool Get_ss_assemblage_in(void) const {return this->ss_assemblage_in;} + bool Get_advect_in(void) const {return this->advect_in;} + bool Get_trans_in(void) const {return this->trans_in;} + + void Set_solution_in(bool tf) {solution_in = tf;} + void Set_pp_assemblage_in(bool tf) {pp_assemblage_in = tf;} + void Set_mix_in(bool tf) {mix_in = tf;} + void Set_reaction_in(bool tf) {reaction_in = tf;} + void Set_exchange_in(bool tf) {exchange_in = tf;} + void Set_kinetics_in(bool tf) {kinetics_in = tf;} + void Set_surface_in(bool tf) {surface_in = tf;} + void Set_pressure_in(bool tf) {pressure_in = tf;} + void Set_temperature_in(bool tf) {temperature_in = tf;} + void Set_gas_phase_in(bool tf) {gas_phase_in = tf;} + void Set_inverse_in(bool tf) {inverse_in = tf;} + void Set_ss_assemblage_in(bool tf) {ss_assemblage_in = tf;} + void Set_advect_in(bool tf) {advect_in = tf;} + void Set_trans_in(bool tf) {trans_in = tf;} + + int Get_n_solution_user(void) const {return n_solution_user;} + int Get_n_pp_assemblage_user(void) const {return n_pp_assemblage_user;} + int Get_n_mix_user(void) const {return n_mix_user;} + int Get_n_mix_user_orig(void) const {return n_mix_user_orig;} + int Get_n_reaction_user(void) const {return n_reaction_user;} + int Get_n_exchange_user(void) const {return n_exchange_user;} + int Get_n_kinetics_user(void) const {return n_kinetics_user;} + int Get_n_surface_user(void) const {return n_surface_user;} + int Get_n_pressure_user(void) const {return n_pressure_user;} + int Get_n_temperature_user(void) const {return n_temperature_user;} + int Get_n_gas_phase_user(void) const {return n_gas_phase_user;} + int Get_n_inverse_user(void) const {return n_inverse_user;} + int Get_n_ss_assemblage_user(void) const {return n_ss_assemblage_user;} + + void Set_n_solution_user(int i) {n_solution_user = i;} + void Set_n_pp_assemblage_user(int i) {n_pp_assemblage_user = i;} + void Set_n_mix_user(int i) {n_mix_user = i;} + void Set_n_mix_user_orig(int i) {n_mix_user_orig = i;} + void Set_n_reaction_user(int i) {n_reaction_user = i;} + void Set_n_exchange_user(int i) {n_exchange_user = i;} + void Set_n_kinetics_user(int i) {n_kinetics_user = i;} + void Set_n_surface_user(int i) {n_surface_user = i;} + void Set_n_pressure_user(int i) {n_pressure_user = i;} + void Set_n_temperature_user(int i) {n_temperature_user = i;} + void Set_n_gas_phase_user(int i) {n_gas_phase_user = i;} + void Set_n_inverse_user(int i) {n_inverse_user = i;} + void Set_n_ss_assemblage_user(int i) {n_ss_assemblage_user = i;} + + cxxSolution * Get_solution_ptr(void) const {return this->solution_ptr;} + cxxPPassemblage * Get_pp_assemblage_ptr(void) const {return this->pp_assemblage_ptr;} + cxxMix * Get_mix_ptr(void) const {return this->mix_ptr;} + cxxReaction * Get_reaction_ptr(void) const {return this->reaction_ptr;} + cxxExchange * Get_exchange_ptr(void) const {return this->exchange_ptr;} + cxxKinetics * Get_kinetics_ptr(void) const {return this->kinetics_ptr;} + cxxSurface * Get_surface_ptr(void) const {return this->surface_ptr;} + cxxPressure * Get_pressure_ptr(void) const {return this->pressure_ptr;} + cxxTemperature * Get_temperature_ptr(void) const {return this->temperature_ptr;} + cxxGasPhase * Get_gas_phase_ptr(void) const {return this->gas_phase_ptr;} + struct inverse * Get_inverse_ptr(void) const {return this->inverse_ptr;} + cxxSSassemblage * Get_ss_assemblage_ptr(void) {return this->ss_assemblage_ptr;} + + void Set_solution_ptr(cxxSolution * p) {this->solution_ptr = p;} + void Set_pp_assemblage_ptr(cxxPPassemblage * p) {this->pp_assemblage_ptr = p;} + void Set_mix_ptr(cxxMix * p) {this->mix_ptr = p;} + void Set_reaction_ptr(cxxReaction * p) {this->reaction_ptr = p;} + void Set_exchange_ptr(cxxExchange * p) {this->exchange_ptr = p;} + void Set_kinetics_ptr(cxxKinetics * p) {this->kinetics_ptr = p;} + void Set_surface_ptr(cxxSurface * p) {this->surface_ptr = p;} + void Set_pressure_ptr(cxxPressure * p) {this->pressure_ptr = p;} + void Set_temperature_ptr(cxxTemperature * p) {this->temperature_ptr = p;} + void Set_gas_phase_ptr(cxxGasPhase * p) {this->gas_phase_ptr = p;} + void Set_inverse_ptr(struct inverse * p) {this->inverse_ptr = p;} + void Set_ss_assemblage_ptr(cxxSSassemblage * p) {this->ss_assemblage_ptr = p;} + +protected: + bool solution_in; + int n_solution_user; + cxxSolution *solution_ptr; + + bool pp_assemblage_in; + int n_pp_assemblage_user; + cxxPPassemblage *pp_assemblage_ptr; + + bool mix_in; + int n_mix_user; + cxxMix * mix_ptr; + int n_mix_user_orig; + + bool reaction_in; + int n_reaction_user; + cxxReaction * reaction_ptr; + + bool exchange_in; + int n_exchange_user; + cxxExchange * exchange_ptr; + + bool kinetics_in; + int n_kinetics_user; + cxxKinetics *kinetics_ptr; + + bool surface_in; + int n_surface_user; + cxxSurface *surface_ptr; + + bool pressure_in; + int n_pressure_user; + cxxPressure *pressure_ptr; + + bool temperature_in; + int n_temperature_user; + cxxTemperature *temperature_ptr; + + bool inverse_in; + int n_inverse_user; + struct inverse *inverse_ptr; + + bool gas_phase_in; + int n_gas_phase_user; + cxxGasPhase * gas_phase_ptr; + + bool ss_assemblage_in; + int n_ss_assemblage_user; + cxxSSassemblage *ss_assemblage_ptr; + + bool trans_in; + bool advect_in; +}; +#endif // !defined(USE_H_INCLUDED) diff --git a/phreeqcpp/UserPunch.cpp b/phreeqcpp/UserPunch.cpp new file mode 100644 index 00000000..290a427f --- /dev/null +++ b/phreeqcpp/UserPunch.cpp @@ -0,0 +1,23 @@ +#include "UserPunch.h" +#include "Phreeqc.h" +UserPunch::UserPunch(int n, PHRQ_io *io) +: cxxNumKeyword(io) +{ + this->PhreeqcPtr = NULL; + this->rate = NULL; +} + + +UserPunch::~UserPunch(void) +{ + if (this->rate != NULL) + { + if (this->PhreeqcPtr != NULL) + { + this->PhreeqcPtr->rate_free(this->rate); + this->PhreeqcPtr->free_check_null(this->rate); + } + } + this->PhreeqcPtr = NULL; + this->rate = NULL; +} diff --git a/phreeqcpp/UserPunch.h b/phreeqcpp/UserPunch.h new file mode 100644 index 00000000..fb8c123f --- /dev/null +++ b/phreeqcpp/UserPunch.h @@ -0,0 +1,39 @@ +#if !defined(USERPUNCH_H_INCLUDED) +#define USERPUNCH_H_INCLUDED +#include +#include // std::string +#include "NumKeyword.h" +class Phreeqc; +class UserPunch:public cxxNumKeyword +{ +public: + UserPunch(int n=1, PHRQ_io *io=NULL); + ~UserPunch(void); + + // headings + // + std::vector &Get_headings() {return this->headings;} + const std::vector &Get_headings()const {return this->headings;} + + void Set_headings(std::vector & h) {this->headings = h;} + + // phreeqc pointer + // + Phreeqc * Get_PhreeqcPtr() {return this->PhreeqcPtr;} + const Phreeqc * Get_PhreeqcPtr()const {return this->PhreeqcPtr;} + + void Set_PhreeqcPtr(Phreeqc * p) {this->PhreeqcPtr = p;} + + // rate + // + struct rate * Get_rate() {return this->rate;} + const struct rate * Get_rate()const {return this->rate;} + + void Set_rate(struct rate * r) {this->rate = r;} + +protected: + std::vector headings; + struct rate * rate; + Phreeqc * PhreeqcPtr; +}; +#endif // !defined(USERPUNCH_H_INCLUDED) \ No newline at end of file diff --git a/phreeqcpp/ZedGraph.dll b/phreeqcpp/ZedGraph.dll new file mode 100644 index 00000000..8b150d82 Binary files /dev/null and b/phreeqcpp/ZedGraph.dll differ diff --git a/phreeqcpp/advection.cpp b/phreeqcpp/advection.cpp new file mode 100644 index 00000000..276cb1a4 --- /dev/null +++ b/phreeqcpp/advection.cpp @@ -0,0 +1,132 @@ +#include "Utils.h" +#include "Phreeqc.h" +#include "phqalloc.h" +#include "cxxKinetics.h" +#include "Solution.h" + + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +advection(void) +/* ---------------------------------------------------------------------- */ +{ + int i; + LDBLE kin_time; +/* + * Calculate advection + */ + state = ADVECTION; +/* mass_water_switch = TRUE; */ +/* + * Check existence of all solutions + */ + for (i = 0; i <= count_ad_cells; i++) + { + if (Utilities::Rxn_find(Rxn_solution_map, i) == NULL) + //if (solution_bsearch(i, &n, TRUE) == NULL) + { + input_error++; + error_string = sformatf( + "Solution %d is needed for advection, but is not defined.", + i); + error_msg(error_string, CONTINUE); + } + } +/* + * Check kinetics logic + */ + kin_time = advection_kin_time; + if (kin_time <= 0.0) + { + for (i = 1; i <= count_ad_cells; i++) + { + if (Utilities::Rxn_find(Rxn_kinetics_map, i) != NULL) + { + input_error++; + error_string = sformatf( + "KINETIC reaction(s) defined, but time_step is not defined in ADVECTION keyword."); + error_msg(error_string, CONTINUE); + break; + } + } + } +/* + * Quit on error + */ + if (get_input_errors() > 0) + { + error_msg("Program terminating due to input errors.", STOP); + } +/* + * Equilibrate solutions with phases, exchangers, surfaces + */ + last_model.force_prep = TRUE; + rate_sim_time_start = 0; + for (advection_step = 1; advection_step <= count_ad_shifts; + advection_step++) + { + log_msg(sformatf( + "\nBeginning of advection time step %d, cumulative pore volumes %f.\n", + advection_step, + (double) (((LDBLE) advection_step) / + ((LDBLE) count_ad_cells)))); + if (pr.use == TRUE && pr.all == TRUE) + { + output_msg(sformatf( + "Beginning of advection time step %d, cumulative pore volumes %f.\n", + advection_step, + (double) (((LDBLE) advection_step) / + ((LDBLE) count_ad_cells)))); + } +/* + * Advect + */ + for (i = count_ad_cells; i > 0; i--) + { + //solution_duplicate(i - 1, i); + Utilities::Rxn_copy(Rxn_solution_map, i -1, i); + } +/* + * Equilibrate and (or) mix + */ + for (i = 1; i <= count_ad_cells; i++) + { + set_initial_moles(i); + cell_no = i; + set_advection(i, TRUE, TRUE, i); + run_reactions(i, kin_time, TRUE, 1.0); + if (advection_kin_time_defined == TRUE) + { + rate_sim_time = rate_sim_time_start + kin_time; + } + log_msg(sformatf( "\nCell %d.\n\n", i)); + if (pr.use == TRUE && pr.all == TRUE && + advection_step % print_ad_modulus == 0 && + advection_print[i - 1] == TRUE) + { + output_msg(sformatf( "\nCell %d.\n\n", i)); + } + if (advection_step % punch_ad_modulus == 0 && + advection_punch[i - 1] == TRUE) + { + punch_all(); + } + if (advection_step % print_ad_modulus == 0 && + advection_print[i - 1] == TRUE) + { + print_all(); + } + if (i > 1) + Utilities::Rxn_copy(Rxn_solution_map, -2, i - 1); + //solution_duplicate(-2, i - 1); + saver(); + } + Utilities::Rxn_copy(Rxn_solution_map, -2, count_ad_cells); + //solution_duplicate(-2, count_ad_cells); + rate_sim_time_start += kin_time; + } + initial_total_time += rate_sim_time_start; + /* free_model_allocs(); */ + mass_water_switch = FALSE; + return (OK); +} diff --git a/phreeqcpp/basicsubs.cpp b/phreeqcpp/basicsubs.cpp new file mode 100644 index 00000000..525dc18a --- /dev/null +++ b/phreeqcpp/basicsubs.cpp @@ -0,0 +1,4438 @@ +#include "Phreeqc.h" +#include "phqalloc.h" + +#include "Utils.h" +#include "NameDouble.h" +#include "PBasic.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "cxxKinetics.h" +#include "Solution.h" +#include "Parser.h" +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +activity(const char *species_name) +/* ---------------------------------------------------------------------- */ +{ + struct species *s_ptr; + LDBLE a; + + s_ptr = s_search(species_name); + if (s_ptr == s_h2o) + { + a = pow((LDBLE) 10., s_h2o->la); + } + else if (s_ptr == s_eminus) + { + a = pow((LDBLE) 10., s_eminus->la); + } + else if (s_ptr == NULL || s_ptr->in == FALSE) + { + a = 1e-99; + } + else + { + a = pow((LDBLE) 10., s_ptr->lm + s_ptr->lg); + } + return (a); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +activity_coefficient(const char *species_name) +/* ---------------------------------------------------------------------- */ +{ + struct species *s_ptr; + LDBLE g, dum = 0.0; + + s_ptr = s_search(species_name); + if (s_ptr != NULL && s_ptr->in != FALSE && ((s_ptr->type < EMINUS) || (s_ptr->type == EX) || (s_ptr->type == SURF))) + { + if (s_ptr->type == EX && s_ptr->equiv && s_ptr->alk) + dum = log10(s_ptr->equiv / s_ptr->alk); + g = pow((LDBLE) 10., s_ptr->lg - dum); + } + else + { + g = 0; + } + return (g); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +log_activity_coefficient(const char *species_name) +/* ---------------------------------------------------------------------- */ +{ + struct species *s_ptr; + LDBLE g, dum = 0.0; + + s_ptr = s_search(species_name); + if (s_ptr != NULL && s_ptr->in != FALSE && ((s_ptr->type < EMINUS) || (s_ptr->type == EX) || (s_ptr->type == SURF))) + { + if (s_ptr->type == EX && s_ptr->equiv && s_ptr->alk) + dum = log10(s_ptr->equiv / s_ptr->alk); + g = s_ptr->lg - dum; + } + else + { + g = 0; + } + return (g); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +aqueous_vm(const char *species_name) +/* ---------------------------------------------------------------------- */ +{ + struct species *s_ptr; + LDBLE g; + + s_ptr = s_search(species_name); + if (s_ptr != NULL && s_ptr->in != FALSE && s_ptr->type < EMINUS) + { + g = s_ptr->logk[vm_tc]; + } + else + { + g = 0; + } + return (g); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +phase_vm(const char *phase_name) +/* ---------------------------------------------------------------------- */ +{ + struct phase *phase_ptr; + int l; + LDBLE g; + + phase_ptr = phase_bsearch(phase_name, &l, FALSE); + if (phase_ptr == NULL) + { + g = 0.0; + } + else + { + g = phase_ptr->logk[vm0]; + } + return (g); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +sa_declercq(double sa_type, double Sa, double d, double m, double m0, double gfw) +/* ---------------------------------------------------------------------- */ +{ + if (sa_type == 0) + { + // surface-area-calculation-Fixed_Surface + return Sa; + } + else if (sa_type == 1) + // surface-area-calculation-Square + { + double mass0 = m0 * gfw; + double V0 = mass0 / d; + double St0 = mass0 * Sa; // total surface + double a0 = pow(V0, 1.0/3.0); // side length + double Sp0 = 6.0 * a0*a0; // surface particle + double np = St0 / Sp0; // number of particles + double RATS = Sa / St0; + double mass = m * gfw; + double V = mass / d; + double a = pow(V, 1.0/3.0); + double St = 6.0 * a*a*np; + return St * RATS; // total current surface + } + else if (sa_type == 2) + { + //double pi = 3.14159265359; + double mass0 = m0 * gfw; + double V0 = mass0 / d; // volume + double St0 = mass0 * Sa; // total surface + double a0 = pow(3.0 * V0/(4.0 * pi), 1.0/3.0); // ((3*V0)/(4 * 3.14159265359))^(1/3) + double Sp0 = (4.0 * pi) * a0 * a0; // surface particle + double np = St0 / Sp0; // number of particles + double RATS = Sa / St0; + + double mass = m * gfw; + double V = mass / d; + double a = pow(3.0 * V/(4.0 * pi), 1.0/3.0); //((3*V)/(4 * 3.14159265359))^(1/3) + double St = 4.0 * pi * a * a * np; + return St * RATS; // total current surface + } + error_string = sformatf( "Unknown surface area type in SA_DECLERCQ %d.", (int) sa_type); + error_msg(error_string, CONTINUE); + input_error++; + return (MISSING); + +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +diff_c(const char *species_name) +/* ---------------------------------------------------------------------- */ +{ + struct species *s_ptr; + LDBLE g; + + s_ptr = s_search(species_name); + if (s_ptr != NULL /*&& s_ptr->in != FALSE && s_ptr->type < EMINUS*/) + { + g = s_ptr->dw; + if (s_ptr->dw_t) + g *= exp(s_ptr->dw_t / tk_x - s_ptr->dw_t / 298.15); + g *= viscos_0_25 / viscos; + } + else + { + g = 0; + } + return (g); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +setdiff_c(const char *species_name, double d) +/* ---------------------------------------------------------------------- */ +{ + struct species *s_ptr; + LDBLE g; + + s_ptr = s_search(species_name); + if (s_ptr != NULL) + { + s_ptr->dw = d; + g = s_ptr->dw; + if (s_ptr->dw_t) + g *= exp(s_ptr->dw_t / tk_x - s_ptr->dw_t / 298.15); + g *= viscos_0_25 / viscos; + } + else + { + g = 0; + } + return (g); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_SC(void) +/* ---------------------------------------------------------------------- */ +{ + //int i; + //LDBLE lm, a, l_z, Dw, SC, ff; + + //SC = 0; +# ifdef SKIP + for (i = 0; i < count_species_list; i++) + { + if (species_list[i].s->type == EX) + continue; + if (species_list[i].s->type == SURF) + continue; + if (i > 0 + && strcmp(species_list[i].s->name, + species_list[i - 1].s->name) == 0) + continue; + if (species_list[i].s == s_h2o) + continue; + if ((Dw = species_list[i].s->dw) == 0) + continue; + if ((l_z = fabs(species_list[i].s->z)) == 0) + continue; + + lm = species_list[i].s->lm; + if (lm > -9) + { + ff = (mu_x < .36 * l_z ? 0.6 / sqrt(l_z) : sqrt(mu_x) / l_z); + + ff *= species_list[i].s->lg; + if (ff > 0) ff = 0; + a = under(lm + ff); + if (species_list[i].s->dw_t) + Dw *= exp(species_list[i].s->dw_t / tk_x - species_list[i].s->dw_t / 298.15); // the viscosity multiplier is done in SC + SC += a * l_z * l_z * Dw; + } + } + SC *= 1e7 * F_C_MOL * F_C_MOL / (R_KJ_DEG_MOL * 298160.0); + /* correct for temperature dependency... + SC_T = SC_298 * (Dw_T / T) * (298 / Dw_298) and + Dw_T = Dw_298 * (T / 298) * (viscos_298 / viscos_T) give: + SC_T = SC_298 * (viscos_298 / viscos_T) + */ + SC *= viscos_0_25 / viscos; + + return (SC); +//# endif + for (i = 0; i < count_s_x; i++) + { + if (s_x[i]->type != AQ && s_x[i]->type != HPLUS) + continue; + if ((Dw = s_x[i]->dw) == 0) + continue; + if ((l_z = fabs(s_x[i]->z)) == 0) + continue; + + lm = s_x[i]->lm; + if (lm > -9) + { + ff = (mu_x < .36 * l_z ? 0.6 / sqrt(l_z) : sqrt(mu_x) / l_z); + + ff *= s_x[i]->lg; + if (ff > 0) ff = 0; + a = under(lm + ff); + if (s_x[i]->dw_t) + Dw *= exp(s_x[i]->dw_t / tk_x - s_x[i]->dw_t / 298.15); // the viscosity multiplier is done in SC + SC += a * l_z * l_z * Dw; + } + } + SC *= 1e7 * F_C_MOL * F_C_MOL / (R_KJ_DEG_MOL * 298160.0); + /* correct for temperature dependency... + SC_T = SC_298 * (Dw_T / T) * (298 / Dw_298) and + Dw_T = Dw_298 * (T / 298) * (viscos_298 / viscos_T) give: + SC_T = SC_298 * (viscos_298 / viscos_T) + */ + SC *= viscos_0_25 / viscos; + + return (SC); +# endif + int i; + LDBLE ka, l_z, Dw, ff, sqrt_mu; + sqrt_mu = sqrt(mu_x); + + SC = 0; + //LDBLE ta1, ta2, ta3, ta4; + for (i = 0; i < count_s_x; i++) + { + // ** for optimizing, get numbers from -analyt for H+ = H+... + //if (!strcmp(s_x[i]->name, "H+")) + //{ + // ta1 = s_x[i]->logk[2]; + // ta2 = s_x[i]->logk[3]; + // ta3 = s_x[i]->logk[4]; + // ta4 = s_x[i]->logk[5]; + // break; + //} + // + } + for (i = 0; i < count_s_x; i++) + { + if (s_x[i]->type != AQ && s_x[i]->type != HPLUS) + continue; + if ((Dw = s_x[i]->dw) == 0) + { + if (correct_Dw) + Dw = default_Dw; + else + continue; + } + if ((l_z = fabs(s_x[i]->z)) == 0) + l_z = 1; // only a 1st approximation for correct_Dw in electrical field + if (s_x[i]->dw_t) + Dw *= exp(s_x[i]->dw_t / tk_x - s_x[i]->dw_t / 298.15); // the viscosity multiplier is done in SC + if (s_x[i]->dw_a2) + ka = DH_B * s_x[i]->dw_a2 * sqrt_mu / (1 + pow(mu_x, 0.75)); + else + { + ka = DH_B * 4.73 * sqrt_mu / (1 + pow(mu_x , 0.75)); + //ka = DH_B * ta1 * sqrt_mu / (1 + pow(mu_x, ta2)); + //ka = DH_B * ta1 * sqrt_mu / (1 + mu_x / ta2); + } + if (s_x[i]->dw_a) + { + ff = exp(-s_x[i]->dw_a * DH_A * l_z * sqrt_mu / (1 + ka)); + if (print_viscosity) + { + ff *= pow((viscos_0 / viscos), s_x[i]->dw_a_visc); + } + } + else + { + ff = exp(-1.6 * DH_A * l_z * sqrt_mu / (1 + ka)); + //ff = exp(-ta3 * DH_A * l_z * sqrt_mu / (1 + ka)); + } + Dw *= ff; + s_x[i]->dw_corr = Dw; + + if (s_x[i]->z == 0) + continue; + s_x[i]->dw_t_SC = s_x[i]->moles / mass_water_aq_x * l_z * l_z * Dw; + SC += s_x[i]->dw_t_SC; + } + SC *= 1e7 * F_C_MOL * F_C_MOL / (R_KJ_DEG_MOL * 298150.0); + /* correct for temperature dependency... + SC_T = SC_298 * (Dw_T / T) * (298 / Dw_298) and + Dw_T = Dw_298 * (T / 298) * (viscos_298 / viscos_T) give: + SC_T = SC_298 * (viscos_298 / viscos_T) + */ + SC *= viscos_0_25 / viscos_0; + return (SC); +} +#ifdef SKIP +/*Debye-Onsager according to Robinson and Stokes, 1954, JACS 75, 1991, + but with sqrt charge multiplier for B2 and mu^ff dependent ka */ + LDBLE q, B1, B2, m_plus, m_min, eq_plus, eq_min, eq_dw_plus, eq_dw_min, Sum_m_dw, z_plus, z_min, t1, t2, Dw_SC; + + m_plus = m_min = eq_plus = eq_min = eq_dw_plus = eq_dw_min = Sum_m_dw = z_plus = z_min = 0; + SC = 0; + for (i = 0; i < count_s_x; i++) + { + if (s_x[i]->type != AQ && s_x[i]->type != HPLUS) + continue; + if ((l_z = s_x[i]->z) == 0) + continue; + if ((lm = s_x[i]->lm) < -9) + continue; + if ((Dw = s_x[i]->dw) == 0) + Dw = 1e-9; + if (s_x[i]->dw_t) + Dw *= exp(s_x[i]->dw_t / tk_x - s_x[i]->dw_t / 298.15); // the viscosity multiplier cancels in q... + if (l_z > 0) + { + m_plus += s_x[i]->moles; + t1 = s_x[i]->moles * l_z; + eq_plus += t1; + eq_dw_plus += t1 * Dw; + Sum_m_dw += s_x[i]->moles * Dw; + } + else + { + m_min += s_x[i]->moles; + t1 = s_x[i]->moles * l_z; + eq_min -= t1; + eq_dw_min -= t1 * Dw; + Sum_m_dw += s_x[i]->moles * Dw; + } + } + // Falkenhagen, q = (Sum(z1 * m1*Dw1) + Sum(z2 *m2*Dw2)) / ((Sum(m1*Dw1) + Sum(m2*Dw2))(av_z1 + av_z2)) + z_plus = eq_plus / m_plus; // |av_z1| + z_min = eq_min / m_min; // |av_z2| + q = (eq_dw_plus + eq_dw_min) / (Sum_m_dw * (z_min + z_plus)); + t1 = 1.60218e-19 * 1.60218e-19 / (6 * pi); + B1 = t1 / (2 * 8.8542e-12 * eps_r * 1.38066e-23 * tk_x) * + q / (1 + sqrt(q)) * DH_B * 1e10 * z_plus * z_min; // DH_B is per Angstrom (*1e10) + t2 = viscos_0; // (1 - 0.5) * viscos_0 + 0.5 * viscos; + B2 = t1 * AVOGADRO / t2 * DH_B * 1e17; // DH_B per Angstrom (*1e10), viscos in mPa.s (*1e3), B2 in cm2 (*1e4) + + Dw_SC = viscos_0_25 / t2 * 1e4 * F_C_MOL * F_C_MOL / (R_KJ_DEG_MOL * 298160.0); + for (i = 0; i < count_s_x; i++) + { + if (s_x[i]->type != AQ && s_x[i]->type != HPLUS) + continue; + if ((l_z = fabs(s_x[i]->z)) == 0) + continue; + if ((lm = s_x[i]->lm) < -9) + continue; + if ((Dw = s_x[i]->dw) == 0) + Dw = 1e-9; + + Dw *= Dw_SC; + if (s_x[i]->dw_t) + Dw *= exp(s_x[i]->dw_t / tk_x - s_x[i]->dw_t / 298.15); // the viscosity factor is in Dw_SC + a = (s_x[i]->dw_a ? s_x[i]->dw_a : 3.5); + ka = DH_B * a; + if (s_x[i]->dw_a2) + ka *= pow((double) mu_x, s_x[i]->dw_a2); + else + ka *= sqrt_mu; + + // Falkenhagen... + //SC += under(lm) * l_z * l_z * (Dw - B2 * l_z * sqrt_mu / (1 + ka)) * (1 - B1 * sqrt_mu / + // ((1 + ka) * (1 + ka * sqrt(q) + ka * ka / 6))); + + t1 = (Dw - (B1 * Dw + B2) * sqrt_mu / (1 + ka)); + //t1 = (Dw - (B1 * Dw + B2 * sqrt(l_z)) * sqrt_mu / (1 + ka)); + //t1 = (Dw - (B1 * Dw + B2 * l_z * l_z) * sqrt_mu / (1 + ka)); + if (t1 > 0) + SC += under(lm) * l_z * l_z * t1; + } + return (SC * 1e3); +#endif + +/* VP: Density Start */ +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_dens(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Calculates density based on the formulas and parameters from Millero, + * 2000. + * + * Millero, F.J. (2000) The equation of state of lakes. Aquatic geochemistry + * volume 6, pages 1-17 + */ + int i; + LDBLE M_T, /*rho_new,*/ gfw; + /* 2 options: original VP, assign the volumes of species with zero molar volume to their master species, + but this doubles counts of complexes with -Vm defined. And, cation-OH and H-anion + complexes are counted once. Also, must add H+ and OH-... */ + //struct species *s_ptr; + + //V_solutes = M_T = 0.0; + //for (i = 0; i < count_species_list; i++) + //{ + // if (species_list[i].s->type != AQ && species_list[i].s->type != HPLUS) + // continue; + + // //if (species_list[i].master_s->secondary != NULL) + // // gfw = species_list[i].master_s->secondary->gfw; + // //else + // // gfw = species_list[i].master_s->primary->gfw; + + // /* OH-... */ + // if (!strcmp(species_list[i].s->name, "OH-")) + // s_ptr = s_search("OH-"); + // else if (species_list[i].s->logk[vm_tc] == 0) + // { + // s_ptr = species_list[i].master_s; + // if (s_ptr->secondary) + // gfw = s_ptr->secondary->gfw; + // else + // gfw = s_ptr->primary->gfw; + // } + // else + // { + // s_ptr = species_list[i].s; + // compute_gfw(species_list[i].s->name, &gfw); + // } + + // /* Special case: CO3-2 species */ + // if (strcmp(s_ptr->name, "CO3-2") == 0) + // { + // if (strstr(species_list[i].s->name, "HCO3") != NULL) + // { + // s_ptr = s_search("HCO3-"); + // } else if (strstr(species_list[i].s->name, "CO3") != NULL) + // { + // compute_gfw("CO3-2", &gfw); + // } + // } + // if (!gfw || !strcmp(species_list[i].s->name, "CO2")) /* CO2, H+ and OH- */ + // compute_gfw(species_list[i].s->name, &gfw); + + // M_T += (species_list[i].s->moles * gfw); + // V_solutes += species_list[i].s->moles * s_ptr->logk[vm_tc]; + //} + + /* 2nd option, use species_x, vm = 0 for complexes with undefined volume... */ + V_solutes = M_T = 0.0; + for (i = 0; i < count_s_x; i++) + { + if (s_x[i]->type != AQ && s_x[i]->type != HPLUS) + continue; + + //compute_gfw(s_x[i]->name, &gfw); + gfw = s_x[i]->gfw; + M_T += s_x[i]->moles * gfw; + V_solutes += s_x[i]->moles * s_x[i]->logk[vm_tc]; + } + /* If pure water then return rho_0 */ + if (M_T == 0) + return rho_0; + else + return rho_0 * (1e3 + M_T / mass_water_aq_x) / (rho_0 * V_solutes / mass_water_aq_x + 1e3); + + //M_T /= 1e3; + //solution_mass = mass_water_aq_x + M_T; + //V_solutes = M_T - rho_0 * V_solutes / 1e3; + + //rho_new = halve(f_rho, 0.5, 2.0, 1e-7); + //if (!PHR_ISFINITE(rho_new) || rho_new > 1.99999) rho_new = 1.99999; + + //return rho_new; +} +/* VP: Density End */ +/* DP: Function for interval halving */ + +LDBLE Phreeqc:: +f_rho(LDBLE rho_old, void *cookie) +/* ---------------------------------------------------------------------- */ +{ + LDBLE rho = 1.0; + Phreeqc * pThis; + + pThis = (Phreeqc *) cookie; + + pThis->solution_volume = pThis->solution_mass / rho_old; + if (pThis->solution_volume != 0) + { + rho = pThis->V_solutes / pThis->solution_volume; + } + rho = rho + pThis->rho_0; + return (rho - rho_old); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_solution_volume(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Calculates solution volume based on sum of mass of element plus density + */ + LDBLE total_mass = 0; + LDBLE gfw; + //compute_gfw("H", &gfw); + gfw = s_hplus->primary->gfw; + total_mass = total_h_x * gfw; + //compute_gfw("O", &gfw); + gfw = s_h2o->primary->gfw; + total_mass += total_o_x * gfw; + + for (int i = 0; i < count_master; i++) + { + if (master[i]->s->type != AQ) continue; + struct master *master_ptr = master[i]; + if (master_ptr->primary == TRUE && strcmp(master_ptr->elt->name, "Alkalinity")) + { + total_mass += master_ptr->total_primary * master_ptr->elt->gfw; + } + } + LDBLE rho = calc_dens(); + LDBLE vol = 1e-3 * total_mass / rho; + return (vol); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_logk_n(const char *name) +/* ---------------------------------------------------------------------- */ +{ + char token[MAX_LENGTH]; + int i; + LDBLE lk; + struct logk *logk_ptr; + LDBLE l_logk[MAX_LOG_K_INDICES]; + struct name_coef add_logk; + + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + l_logk[i] = 0.0; + } + strcpy(token, name); + logk_ptr = logk_search(token); + if (logk_ptr != NULL) + { + add_logk.name = token; + add_logk.coef = 1.0; + add_other_logk(l_logk, 1, &add_logk); + lk = k_calc(l_logk, tk_x, patm_x * PASCAL_PER_ATM); + return (lk); + } + return (-999.99); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_logk_p(const char *name) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + char token[MAX_LENGTH]; + struct phase *phase_ptr; + LDBLE lk=-999.9; + LDBLE l_logk[MAX_LOG_K_INDICES]; + + strcpy(token, name); + phase_ptr = phase_bsearch(token, &j, FALSE); + + if (phase_ptr != NULL) + { + struct reaction *reaction_ptr; + if (phase_ptr->replaced) + reaction_ptr = phase_ptr->rxn_s; + else + reaction_ptr = phase_ptr->rxn; + /* + * Print saturation index + */ + reaction_ptr->logk[delta_v] = calc_delta_v(reaction_ptr, true) - + phase_ptr->logk[vm0]; + if (reaction_ptr->logk[delta_v]) + mu_terms_in_logk = true; + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + l_logk[i] = 0.0; + } + //lk = k_calc(reaction_ptr->logk, tk_x, patm_x * PASCAL_PER_ATM); + select_log_k_expression(reaction_ptr->logk, l_logk); + add_other_logk(l_logk, phase_ptr->count_add_logk, phase_ptr->add_logk); + lk = k_calc(l_logk, tk_x, patm_x * PASCAL_PER_ATM); + } + return (lk); +} +#ifdef SKIP +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_logk_s(const char *name) +/* ---------------------------------------------------------------------- */ +{ + int i; + char token[MAX_LENGTH]; + struct species *s_ptr; + LDBLE lk, l_logk[MAX_LOG_K_INDICES]; + + strcpy(token, name); + s_ptr = s_search(token); + if (s_ptr != NULL) + { + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + l_logk[i] = 0.0; + } + //if (s_ptr->moles) + //select_log_k_expression(s_ptr->rxn_x->logk, l_logk); + select_log_k_expression(s_ptr->rxn->logk, l_logk); + //{ + // perhaps calculate species' delta_v if absent? + // select_log_k_expression(s_ptr->rxn_s->logk, l_logk); + //} + add_other_logk(l_logk, s_ptr->count_add_logk, s_ptr->add_logk); + lk = k_calc(l_logk, tk_x, patm_x * PASCAL_PER_ATM); + return (lk); + } + return (-999.99); +} +#endif +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_logk_s(const char *name) +/* ---------------------------------------------------------------------- */ +{ + int i; + char token[MAX_LENGTH]; + struct species *s_ptr; + LDBLE lk, l_logk[MAX_LOG_K_INDICES]; + + strcpy(token, name); + s_ptr = s_search(token); + if (s_ptr != NULL) + { + //if (s_ptr->logk[vm_tc]) + /* calculate delta_v for the reaction... */ + s_ptr->logk[delta_v] = calc_delta_v(s_ptr->rxn, false); + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + l_logk[i] = 0.0; + } + select_log_k_expression(s_ptr->logk, l_logk); + mu_terms_in_logk = true; + add_other_logk(l_logk, s_ptr->count_add_logk, s_ptr->add_logk); + lk = k_calc(l_logk, tk_x, patm_x * PASCAL_PER_ATM); + return (lk); + } + return (-999.99); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_surface_charge(const char *surface_name) +/* ---------------------------------------------------------------------- */ +{ + char token[MAX_LENGTH], token1[MAX_LENGTH]; + char *ptr; + int i, j, k; + LDBLE charge; + struct rxn_token_temp *token_ptr; + struct master *master_ptr; + + /* + * Go through species, sum charge + */ + charge = 0; + for (k = 0; k < count_s_x; k++) + { + if (s_x[k]->type != SURF) + continue; + /* + * Match surface_name + */ + count_trxn = 0; + trxn_add(s_x[k]->rxn_s, 1.0, FALSE); /* rxn_s is set in tidy_model */ + for (i = 1; i < count_trxn; i++) + { + token_ptr = &(trxn.token[i]); + if (token_ptr->s->type != SURF) + continue; + master_ptr = trxn.token[i].s->primary; + strcpy(token, master_ptr->elt->name); + replace("_", " ", token); + ptr = token; + copy_token(token1, &ptr, &j); + if (strcmp(surface_name, token1) == 0) + { + charge += s_x[k]->moles * s_x[k]->z; + } + } + } + return (charge); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +diff_layer_total(const char *total_name, const char *surface_name) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in DDL layer + */ + cxxSurfaceCharge *surface_charge_ptr1; + std::string name, token, surface_name_local; + struct master *master_ptr; + + LDBLE mass_water_surface; + LDBLE molality, moles_excess, moles_surface, charge; + + if (use.Get_surface_ptr() == NULL || (dl_type_x == cxxSurface::NO_DL && + strcmp_nocase("psi", total_name) != 0 && + strcmp_nocase("psi1", total_name) != 0 && + strcmp_nocase("psi2", total_name) != 0 && + strcmp_nocase("charge", total_name) != 0 + && strcmp_nocase("charge1", + total_name) != 0 + && strcmp_nocase("charge2", + total_name) != 0 + && strcmp_nocase("sigma", + total_name) != 0 + && strcmp_nocase("sigma1", + total_name) != 0 + && strcmp_nocase("sigma2", + total_name) != 0)) + return (0); + +/* + * Find surface... + */ + int j; + for (j = 0; j < count_unknowns; j++) + { + if (use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM) + { + if (x[j]->type != SURFACE_CB) + continue; + name = x[j]->master[0]->elt->name; + Utilities::replace("_psi", "", name); + } + else if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + if (x[j]->type != SURFACE_CB) + continue; + name = x[j]->master[0]->elt->name; + Utilities::replace("_psi", "", name); + } + else + { + if (x[j]->type != SURFACE) + continue; + token = x[j]->master[0]->elt->name; + Utilities::replace("_", " ", token); + std::string::iterator b = token.begin(); + std::string::iterator e = token.end(); + CParser::copy_token(name, b, e); + } + if (surface_name != NULL) + { + if (strcmp(name.c_str(), surface_name) == 0) + break; + } + else + { + break; + } + } + if (j >= count_unknowns) + return (0); + surface_name_local = name; + /* + * Psi, charge, sigma + */ + if (strcmp_nocase("psi", total_name) == 0) + { + if (use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM) + { + return ((LDBLE) (x[j]->master[0]->s->la * 2 * R_KJ_DEG_MOL * + tk_x * LOG_10 / F_KJ_V_EQ)); + } + else if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + master_ptr = surface_get_psi_master(surface_name, SURF_PSI); + if (master_ptr != NULL) + { + return ((LDBLE) + (-master_ptr->s->la * R_KJ_DEG_MOL * tk_x * LOG_10 / + F_KJ_V_EQ)); + } + else + { + return (0.0); + } + } + else + { + return (0); + } + } + else if (strcmp_nocase("psi1", total_name) == 0) + { + master_ptr = surface_get_psi_master(surface_name, SURF_PSI1); + if (master_ptr != NULL) + { + return ((LDBLE) + (-master_ptr->s->la * R_KJ_DEG_MOL * tk_x * LOG_10 / + F_KJ_V_EQ)); + } + else + { + return (0.0); + } + } + else if (strcmp_nocase("psi2", total_name) == 0) + { + master_ptr = surface_get_psi_master(surface_name, SURF_PSI2); + if (master_ptr != NULL) + { + return ((LDBLE) + (-master_ptr->s->la * R_KJ_DEG_MOL * tk_x * LOG_10 / + F_KJ_V_EQ)); + } + else + { + return (0.0); + } + } + else if (strcmp_nocase("charge", total_name) == 0) + { + if ((use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM) && dl_type_x == cxxSurface::NO_DL) + { + return ((LDBLE) (x[j]->f)); + } + else if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + return ((charge_ptr->Get_sigma0() * + (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()) / F_C_MOL)); + } + else + { + return (calc_surface_charge(surface_name_local.c_str())); + } + } + else if (strcmp_nocase("charge1", total_name) == 0) + { + if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + return ((charge_ptr->Get_sigma1() * + (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()) / F_C_MOL)); + } + else + { + return (0); + } + } + else if (strcmp_nocase("charge2", total_name) == 0) + { + if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + return ((charge_ptr->Get_sigma2() * + (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()) / F_C_MOL)); + } + else + { + return (0); + } + } + else if (strcmp_nocase("sigma", total_name) == 0) + { + if (use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + if (dl_type_x != cxxSurface::NO_DL) + { + charge = calc_surface_charge(surface_name_local.c_str()); + } + else + { + charge = x[j]->f; + } + if ((charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()) > 0) + { + return ((charge * F_C_MOL / + (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()))); + } + else + { + return (0); + } + } + else if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + return ((LDBLE) (charge_ptr->Get_sigma0())); + } + else + { + return (0); + } + } + else if (strcmp_nocase("sigma1", total_name) == 0) + { + if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + return ((LDBLE) (charge_ptr->Get_sigma1())); + } + else + { + return (0); + } + } + else if (strcmp_nocase("sigma2", total_name) == 0) + { + if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + return ((LDBLE) (charge_ptr->Get_sigma2())); + } + else + { + return (0); + } + } + else if (strcmp_nocase("water", total_name) == 0) + { + if (dl_type_x != cxxSurface::NO_DL) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + return (charge_ptr->Get_mass_water()); + } + else + { + return (0); + } + } +/* + * find total moles of each element in diffuse layer... + */ + surface_charge_ptr1 = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + if (surface_charge_ptr1) + { + mass_water_surface = surface_charge_ptr1->Get_mass_water(); + count_elts = 0; + paren_count = 0; + for (j = 0; j < count_s_x; j++) + { + if (s_x[j]->type > HPLUS) + continue; + molality = under(s_x[j]->lm); + LDBLE g = surface_charge_ptr1->Get_g_map()[s_x[j]->z].Get_g(); + + moles_excess = mass_water_aq_x * molality * (g * s_x[j]->erm_ddl + + mass_water_surface / + mass_water_aq_x * (s_x[j]->erm_ddl - 1)); + moles_surface = mass_water_surface * molality + moles_excess; +/* + * Accumulate elements in diffuse layer + */ + add_elt_list(s_x[j]->next_elt, moles_surface); + } + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), Phreeqc:: elt_list_compare); + elt_list_combine(); + } +/* + * Return totals + */ + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, total_name) == 0) + { + return ((LDBLE) elt_list[j].coef); + } + } + } + return (0); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_t_sc(const char *name) +/* ---------------------------------------------------------------------- */ +{ + char token[MAX_LENGTH]; + struct species *s_ptr; + + strcpy(token, name); + s_ptr = s_search(token); + if (s_ptr != NULL) + { + if (!s_ptr->z) + return (0); + calc_SC(); + if (!SC) + return (0); + LDBLE t = s_ptr->dw_t_SC * 1e7 * F_C_MOL * F_C_MOL / (R_KJ_DEG_MOL * 298150.0) * viscos_0_25 / viscos_0; + return (t / SC); + } + return (-999.99); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +equi_phase(const char *phase_name) +/* ---------------------------------------------------------------------- */ +{ + int j; + + if (use.Get_pp_assemblage_in() == FALSE || use.Get_pp_assemblage_ptr() == NULL) + return (0); + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != PP) + continue; + if (strcmp_nocase(x[j]->pp_assemblage_comp_name, phase_name) == 0) + { + break; + } + } +/* + * Print pure phase assemblage data + */ + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + if (j == count_unknowns) + { + /* if not an unknown */ + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + if (strcmp_nocase + (it->second.Get_name().c_str(), phase_name) == 0) + { + return (it->second.Get_moles()); + } + } + } + else + { + /* if an unknown */ + if (x[j]->moles < 0.0) + x[j]->moles = 0.0; + return (x[j]->moles); + } + return (0); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +equi_phase_delta(const char *phase_name) +/* ---------------------------------------------------------------------- */ +{ + int j; + + if (use.Get_pp_assemblage_in() == FALSE || use.Get_pp_assemblage_ptr() == NULL) + return (0); + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != PP) + continue; + if (strcmp_nocase(x[j]->pp_assemblage_comp_name, phase_name) == 0) + { + break; + } + } +/* + * Print pure phase assemblage data + */ + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + if (j == count_unknowns) + { + /* if not an unknown */ + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + if (strcmp_nocase + (it->second.Get_name().c_str(), phase_name) == 0) + { + cxxPPassemblageComp * comp_ptr = &(it->second); + if (state != TRANSPORT && state != PHAST) + { + //LDBLE moles = it->second.Get_moles(); + //LDBLE delta_moles = moles - comp_ptr->Get_moles() - + // comp_ptr->Get_delta(); + return(0); + } + else + { + LDBLE moles = it->second.Get_moles(); + LDBLE delta_moles = moles - comp_ptr->Get_initial_moles(); + return(delta_moles); + } + } + } + } + else + { + //cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[j]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[j]->pp_assemblage_comp_ptr; + if (state != TRANSPORT && state != PHAST) + { + LDBLE delta_moles = + x[j]->moles - comp_ptr->Get_moles() - + comp_ptr->Get_delta(); + return(delta_moles); + } + else + { + LDBLE delta_moles = + x[j]->moles - comp_ptr->Get_initial_moles(); + return(delta_moles); + } + } + return (0); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +equivalent_fraction(const char *name, LDBLE *eq, std::string &elt_name) +/* ---------------------------------------------------------------------- */ +{ + struct species *s_ptr = s_search(name); + *eq = 0; + elt_name.clear(); + LDBLE f = 0; + if (s_ptr != NULL && (s_ptr->type == EX || s_ptr->type == SURF)) + { + *eq = s_ptr->equiv; + struct elt_list *next_elt; + LDBLE tot=0.0; + for (next_elt = s_ptr->next_elt; next_elt->elt != NULL; next_elt++) + { + if (next_elt->elt->master->s->type == SURF || + next_elt->elt->master->s->type == EX) + { + tot = total_mole(next_elt->elt->name); + elt_name = next_elt->elt->name; + } + } + if (s_ptr->in == TRUE && tot > 0.0) + { + f = s_ptr->moles * s_ptr->equiv / tot; + } + } + return f; +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +find_gas_comp(const char *gas_comp_name) +/* ---------------------------------------------------------------------- */ +{ + int i; + + if (use.Get_gas_phase_in() == FALSE || use.Get_gas_phase_ptr() == NULL) + return (0); + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++) + { + if (strcmp_nocase(gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str(), gas_comp_name) == 0) + { + struct phase *phase_ptr = phase_bsearch(gas_comp_name, &i, false); + if (phase_ptr) + { + return (phase_ptr->moles_x); + } + } + } + return (0); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +find_gas_p(void) +/* ---------------------------------------------------------------------- */ +{ + if (use.Get_gas_phase_in() == FALSE || use.Get_gas_phase_ptr() == NULL) + return (0); + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + if (gas_unknown == NULL) + return (0); + if (gas_unknown->moles < 1e-12) + return (0); + } + return (gas_phase_ptr->Get_total_p()); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +find_gas_vm(void) +/* ---------------------------------------------------------------------- */ +{ + if (use.Get_gas_phase_in() == FALSE || use.Get_gas_phase_ptr() == NULL) + return (0); + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + if (gas_unknown == NULL) + return (0); + if (gas_unknown->moles < 1e-12) + return (0); + gas_phase_ptr->Set_total_moles(gas_unknown->moles); + gas_phase_ptr->Set_volume(gas_phase_ptr->Get_total_moles() * R_LITER_ATM * tk_x / + gas_phase_ptr->Get_total_p()); + if (gas_phase_ptr->Get_v_m() >= 0.01) + gas_phase_ptr->Set_volume(gas_phase_ptr->Get_v_m() * gas_unknown->moles); + } + return gas_phase_ptr->Get_volume() / gas_phase_ptr->Get_total_moles(); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +find_misc1(const char *ss_name) +/* ---------------------------------------------------------------------- */ +{ + if (use.Get_ss_assemblage_in() == FALSE || use.Get_ss_assemblage_ptr() == NULL) + return (0.0); + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t j = 0; j < ss_ptrs.size(); j++) + { + cxxSS *ss_ptr = ss_ptrs[j]; + if (strcmp_nocase(ss_ptr->Get_name().c_str(), ss_name) == 0) + { + if (ss_ptr->Get_miscibility()) + { + return (ss_ptr->Get_xb1()); + } + else + { + return (1.0); + } + } + } + return (0); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +find_misc2(const char *ss_name) +/* ---------------------------------------------------------------------- */ +{ + if (use.Get_ss_assemblage_in() == FALSE || use.Get_ss_assemblage_ptr() == NULL) + return (0.0); + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t j = 0; j < ss_ptrs.size(); j++) + { + cxxSS *ss_ptr = ss_ptrs[j]; + if (strcmp_nocase(ss_ptr->Get_name().c_str(), ss_name) == 0) + { + if (ss_ptr->Get_miscibility()) + { + return (ss_ptr->Get_xb2()); + } + else + { + return (1.0); + } + } + } + return (0); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +find_ss_comp(const char *ss_comp_name) +/* ---------------------------------------------------------------------- */ +{ + if (use.Get_ss_assemblage_in() == FALSE || use.Get_ss_assemblage_ptr() == NULL) + return (0); + + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t j = 0; j < ss_ptrs.size(); j++) + { + cxxSS *ss_ptr = ss_ptrs[j]; + for (size_t i = 0; i < ss_ptr->Get_ss_comps().size(); i++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[i]); + if (strcmp_nocase(comp_ptr->Get_name().c_str(), ss_comp_name) == 0) + { + if (ss_ptr->Get_ss_in()) + { + return (comp_ptr->Get_moles()); + } + else + { + return (0.0); + } + } + } + } + return (0); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +get_calculate_value(const char *name) +/* ---------------------------------------------------------------------- */ +/* + * Gets value from a calclate_value structure + * arguments: name + * return: LDBLE of value + */ +{ + struct calculate_value *calculate_value_ptr; + calculate_value_ptr = calculate_value_search(name); + if (calculate_value_ptr == NULL) + { + error_string = sformatf( "CALC_VALUE Basic function, %s not found.", + name); + error_msg(error_string, CONTINUE); + input_error++; + return (MISSING); + } + if (name == NULL) + { + error_string = sformatf( + "Definition for calculated value not found, %s", name); + input_error++; + error_msg(error_string, CONTINUE); + return (MISSING); + } + + char l_command[] = "run"; + PBasic interp(this, this->phrq_io); + if (calculate_value_ptr->new_def == TRUE) + { + if (interp.basic_compile + (calculate_value_ptr->commands, + &calculate_value_ptr->linebase, + &calculate_value_ptr->varbase, + &calculate_value_ptr->loopbase) != 0) + { + error_string = sformatf( + "Fatal Basic error in CALCULATE_VALUES %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } + calculate_value_ptr->new_def = FALSE; + } + + if (interp.basic_run(l_command, + calculate_value_ptr->linebase, + calculate_value_ptr->varbase, + calculate_value_ptr->loopbase) != 0) + { + error_string = sformatf( "Fatal Basic error in calculate_value %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } +#ifdef NPP + if (isnan(rate_moles)) +#else + if (rate_moles == NAN) +#endif + { + error_string = sformatf( "Calculated value not SAVEed for %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } + else + { + calculate_value_ptr->calculated = TRUE; + calculate_value_ptr->value = rate_moles; + } + return (calculate_value_ptr->value); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +kinetics_moles(const char *kinetics_name) +/* ---------------------------------------------------------------------- */ +{ + + if (use.Get_kinetics_in() == FALSE || use.Get_kinetics_ptr() == NULL) + return (0); + for (size_t i = 0; i < use.Get_kinetics_ptr()->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp *kinetics_comp_ptr = &(use.Get_kinetics_ptr()->Get_kinetics_comps()[i]); + if (strcmp_nocase + (kinetics_comp_ptr->Get_rate_name().c_str(), kinetics_name) == 0) + { + return (kinetics_comp_ptr->Get_m()); + } + } + + error_string = sformatf( "No data for rate %s in KINETICS keyword.", + kinetics_name); + warning_msg(error_string); + return (0); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +kinetics_moles_delta(const char *kinetics_name) +/* ---------------------------------------------------------------------- */ +{ + + if (use.Get_kinetics_in() == FALSE || use.Get_kinetics_ptr() == NULL) + return (0); + for (size_t i = 0; i < use.Get_kinetics_ptr()->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp *kinetics_comp_ptr = &(use.Get_kinetics_ptr()->Get_kinetics_comps()[i]); + if (strcmp_nocase + (kinetics_comp_ptr->Get_rate_name().c_str(), kinetics_name) == 0) + { + //return (kinetics_comp_ptr->Get_m()); + + if (state != TRANSPORT && state != PHAST) + { + //LDBLE moles = kinetics_comp_ptr->Get_m(); + LDBLE delta_moles = - kinetics_comp_ptr->Get_moles(); + return delta_moles; + } + else + { + //moles = kinetics_comp_ptr->Get_m(); + LDBLE delta_moles = + kinetics_comp_ptr->Get_m() - + kinetics_comp_ptr->Get_initial_moles(); + return delta_moles; + } + } + } + + //error_string = sformatf( "No data for rate %s in KINETICS keyword.", + // kinetics_name); + //warning_msg(error_string); + return (0); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +log_activity(const char *species_name) +/* ---------------------------------------------------------------------- */ +{ + struct species *s_ptr; + LDBLE la; + + s_ptr = s_search(species_name); + + if (s_ptr == s_eminus) + { + la = s_eminus->la; + } + else if (s_ptr == NULL || s_ptr->in == FALSE) + { + la = -99.99; + } + else if (s_ptr == s_h2o) + { + la = s_h2o->la; + } + else + { + la = s_ptr->lm + s_ptr->lg; + } + return (la); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +log_molality(const char *species_name) +/* ---------------------------------------------------------------------- */ +{ + struct species *s_ptr; + LDBLE lm; + + s_ptr = s_search(species_name); + + if (s_ptr == s_eminus) + { + lm = -99.99; + } + else if (s_ptr == NULL || s_ptr->in == FALSE) + { + lm = -99.99; + } + else if (s_ptr == s_h2o) + { + lm = log10(s_ptr->moles / mass_water_aq_x); + } + else + { + lm = s_ptr->lm; + } + return (lm); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +molality(const char *species_name) +/* ---------------------------------------------------------------------- */ +{ + struct species *s_ptr; + LDBLE m; + + s_ptr = s_search(species_name); + if (s_ptr == NULL || s_ptr == s_eminus || s_ptr->in == FALSE) + { + m = 1e-99; + } + else + { + /* m = pow(10., s_ptr->lm); */ + m = s_ptr->moles / mass_water_aq_x; + } + return (m); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +pr_pressure(const char *phase_name) +/* ---------------------------------------------------------------------- */ +{ + struct phase *phase_ptr; + int l; + + phase_ptr = phase_bsearch(phase_name, &l, FALSE); + if (phase_ptr == NULL) + { + error_string = sformatf( "Gas %s, not found.", phase_name); + warning_msg(error_string); + return (1e-99); + } + else if (phase_ptr->in != FALSE && phase_ptr->pr_in) + { + return phase_ptr->pr_p; + } + return (0.0); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +pressure(void) +/* ---------------------------------------------------------------------- */ +{ + return (patm_x); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +pr_phi(const char *phase_name) +/* ---------------------------------------------------------------------- */ +{ + struct phase *phase_ptr; + int l; + + phase_ptr = phase_bsearch(phase_name, &l, FALSE); + if (phase_ptr == NULL) + { + error_string = sformatf( "Gas %s, not found.", phase_name); + warning_msg(error_string); + return (1e-99); + } + else if (phase_ptr->in != FALSE && phase_ptr->pr_in) + { + return phase_ptr->pr_phi; + } + return (1.0); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +saturation_ratio(const char *phase_name) +/* ---------------------------------------------------------------------- */ +{ + struct rxn_token *rxn_ptr; + struct phase *phase_ptr; + int l; + LDBLE si, iap; + + iap = 0.0; + phase_ptr = phase_bsearch(phase_name, &l, FALSE); + if (phase_ptr == NULL) + { + error_string = sformatf( "Mineral %s, not found.", phase_name); + warning_msg(error_string); + return (1e-99); + } + else if (phase_ptr->in != FALSE) + { + for (rxn_ptr = phase_ptr->rxn_x->token + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + iap += rxn_ptr->s->la * rxn_ptr->coef; + } + si = iap - phase_ptr->lk; + return (pow((LDBLE) 10.0, si)); + } + return (0.0); + +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +saturation_index(const char *phase_name, LDBLE * iap, LDBLE * si) +/* ---------------------------------------------------------------------- */ +{ + struct rxn_token *rxn_ptr; + struct phase *phase_ptr; + int l; + + *si = -99.99; + *iap = 0.0; + phase_ptr = phase_bsearch(phase_name, &l, FALSE); + if (phase_ptr == NULL) + { + error_string = sformatf( "Mineral %s, not found.", phase_name); + warning_msg(error_string); + *si = -99; + } + else if (phase_ptr->in != FALSE) + { + for (rxn_ptr = phase_ptr->rxn_x->token + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + *iap += rxn_ptr->s->la * rxn_ptr->coef; + } + *si = *iap - phase_ptr->lk; + } + else + { + return (ERROR); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +sum_match_gases(const char *mytemplate, const char *name) +/* ---------------------------------------------------------------------- */ +{ + int i; + LDBLE tot; + struct elt_list *next_elt; + + if (use.Get_gas_phase_in() == FALSE || use.Get_gas_phase_ptr() == NULL) + return (0); + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + tot = 0; + for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++) + { + struct phase * phase_ptr = phase_bsearch(gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str(), + &i, FALSE); + if (match_elts_in_species(phase_ptr->formula, mytemplate) == TRUE) + { + if (name == NULL) + { + tot += phase_ptr->moles_x; + } + else + { + for (next_elt = phase_ptr->next_elt; + next_elt->elt != NULL; next_elt++) + { + if (strcmp(next_elt->elt->name, name) == 0) + { + tot += next_elt->coef * phase_ptr->moles_x; + break; + } + } + } + } + } + return (tot); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +sum_match_species(const char *mytemplate, const char *name) +/* ---------------------------------------------------------------------- */ +{ + int i; + LDBLE tot; + struct elt_list *next_elt; + + count_elts = 0; + paren_count = 0; + tot = 0; + if (sum_species_map.find(mytemplate) == sum_species_map.end()) + { + std::vector species_list; + for (i = 0; i < count_s_x; i++) + { + struct species *s_ptr = s_x[i]; + if (match_elts_in_species(s_ptr->name, mytemplate) == TRUE) + { + species_list.push_back(s_ptr->name); + } + } + sum_species_map[mytemplate] = species_list; + } + std::vector &species_list = (sum_species_map.find(mytemplate))->second; + for (size_t i=0; i < species_list.size(); i++) + { + struct species *s_ptr = s_search(species_list[i].c_str()); + if (s_ptr->in == FALSE) continue; + if (name == NULL) + { + tot += s_ptr->moles; + } + else + { + for (next_elt = s_ptr->next_elt; next_elt->elt != NULL; + next_elt++) + { + if (strcmp(next_elt->elt->name, name) == 0) + { + tot += next_elt->coef * s_ptr->moles; + break; + } + } + } + } + return (tot); +} + + + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +sum_match_ss(const char *mytemplate, const char *name) +/* ---------------------------------------------------------------------- */ +{ + LDBLE tot; + struct elt_list *next_elt; + + if (use.Get_ss_assemblage_in() == FALSE || use.Get_ss_assemblage_ptr() == NULL) + return (0); + tot = 0; + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t j = 0; j < ss_ptrs.size(); j++) + { + cxxSS *ss_ptr = ss_ptrs[j]; + if (strcmp_nocase(ss_ptr->Get_name().c_str(), mytemplate) == 0) + { + if (!ss_ptr->Get_ss_in()) + { + tot = 0; + break; + } + for (size_t i = 0; i < ss_ptr->Get_ss_comps().size(); i++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[i]); + if (name == NULL) + { + tot += comp_ptr->Get_moles(); + } + else + { + int l; + struct phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + for (next_elt = phase_ptr->next_elt; next_elt->elt != NULL; next_elt++) + { + if (strcmp(next_elt->elt->name, name) == 0) + { + tot += next_elt->coef * comp_ptr->Get_moles(); + break; + } + } + } + } + break; + } + } + return (tot); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +list_ss(std::string ss_name, cxxNameDouble &composition) +/* ---------------------------------------------------------------------- */ +{ + LDBLE tot = 0; + composition.clear(); + if (use.Get_ss_assemblage_in() == FALSE || use.Get_ss_assemblage_ptr() == NULL) + return (0); + + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t j = 0; j < ss_ptrs.size(); j++) + { + cxxSS *ss_ptr = ss_ptrs[j]; + if (strcmp_nocase(ss_ptr->Get_name().c_str(), ss_name.c_str()) == 0) + { + for (size_t i = 0; i < ss_ptr->Get_ss_comps().size(); i++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[i]); + composition.add(comp_ptr->Get_name().c_str(), comp_ptr->Get_moles()); + tot += comp_ptr->Get_moles(); + } + break; + } + } + return (tot); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +match_elts_in_species(const char *name, const char *mytemplate) +/* ---------------------------------------------------------------------- */ +{ +/* + * Makes a list of elements with their coefficients, stores elements + * in elt_list at position count_elts. Global variable count_elts is + * updated with each stored element. Also uses static global variable + * paren_count. + * + * Arguments: + * **t_ptr input, point in token string to start looking + * output, is next position to start looking + * coef input, coefficient to multiply subscripts by + */ + int i, i1, l, case_no, match; + char c, c1; + char *ptr, *ptr1; + LDBLE d; + char element[MAX_LENGTH]; + char token[MAX_LENGTH], equal_list[MAX_LENGTH]; + char token1[MAX_LENGTH], template1[MAX_LENGTH], equal_list1[MAX_LENGTH]; + char str[2]; + + strcpy(token, name); + squeeze_white(token); + replace("(+", "(", token); + if (strstr("token", "++") != NULL) + { + replace("++++++", "+6", token); + replace("+++++", "+5", token); + replace("++++", "+4", token); + replace("+++", "+3", token); + replace("++", "+2", token); + } + if (strstr("token", "--") != NULL) + { + replace("------", "-6", token); + replace("-----", "-5", token); + replace("----", "-4", token); + replace("---", "-3", token); + replace("--", "-2", token); + } + + ptr = token; + std::vector< std::pair > match_vector; + while ((c = *ptr) != '\0') + { + c1 = *(ptr + 1); + str[0] = c; + str[1] = '\0'; +/* + * New element + */ + if (isupper((int) c) || (c == 'e' && c1 == '-') || (c == '[')) + { + /* + * Get new element and subscript + */ + if (get_elt(&ptr, element, &l) == ERROR) + { + return (ERROR); + } + if (get_num(&ptr, &d) == ERROR) + { + return (ERROR); + } + std::pair pr(element, d); + match_vector.push_back(pr); + } + else + { + std::pair pr(str, 1.0); + match_vector.push_back(pr); + ptr += 1; + } + } + /* + * Replace elements with first of equivalent elements + */ + strcpy(template1, mytemplate); + squeeze_white(template1); + ptr = template1; + while (extract_bracket(&ptr, equal_list) == TRUE) + { + replace("{", "", equal_list); + replace("}", "", equal_list); + while (replace(",", " ", equal_list) == TRUE); + ptr1 = equal_list; + /* + * Get first name in a list from template + */ + std::string elt_name; + if (copy_token(elt_name, &ptr1) == EMPTY) + { + error_string = sformatf( + "Expecting a nonempty list of element names in isotope sum. %s", + mytemplate); + error_msg(error_string, CONTINUE); + return (ERROR); + } + std::string replace_name = elt_name; + /* + * Replace in species all equivalent names from template + */ + while (copy_token(elt_name, &ptr1) != EMPTY) + { + for (i = 0; i < (int) match_vector.size(); i++) + { + if (elt_name == match_vector[i].first) + { + match_vector[i].first = replace_name; + } + } + } + } + /* + * Combine contiguous elements + */ + i1 = 0; + for (i = 1; i < (int) match_vector.size(); i++) + { + if ((isupper((int) (match_vector[i].first[0])) != FALSE) + && (match_vector[i].first == match_vector[i1].first)) + { + match_vector[i1].second += match_vector[i].second; + } + else + { + i1++; + match_vector[i1].first = match_vector[i].first; + match_vector[i1].second = match_vector[i].second; + } + } + int count_match_tokens = i1 + 1; + /* + * write out string + */ + token[0] = '\0'; + for (i = 0; i < count_match_tokens; i++) + { + strcat(token, match_vector[i].first.c_str()); + if (match_vector[i].second != 1.0) + { + sprintf(token1, "%g", (double) match_vector[i].second); + strcat(token, token1); + } + } + /* + * Write a template name using first of equivalent elements + */ + strcpy(template1, mytemplate); + squeeze_white(template1); + ptr = template1; + while (extract_bracket(&ptr, equal_list) == TRUE) + { + strcpy(equal_list1, equal_list); + replace("{", "", equal_list); + replace("}", "", equal_list); + while (replace(",", " ", equal_list) == TRUE); + ptr1 = equal_list; + /* + * Get first name in a list + */ + std::string elt_name; + if (copy_token(elt_name, &ptr1) == EMPTY) + { + error_string = sformatf( + "Expecting a nonempty list of element names in isotope sum. %s", + mytemplate); + error_msg(error_string, CONTINUE); + return (ERROR); + } + replace(equal_list1, elt_name.c_str(), template1); + squeeze_white(template1); + ptr = template1; + } + /* + * Compare string + */ + /* Cases: 0 exact match + * 1 leading wild card + * 2 trailing wild card + * 3 leading and trailing wild card + */ + case_no = 0; + if (template1[0] == '*') + case_no = 1; + l = (int) strlen(template1); + if (template1[l - 1] == '*') + { + if (case_no != 1) + { + case_no = 2; + } + else + { + case_no = 3; + } + } + while (replace("*", "", template1)); + match = FALSE; + switch (case_no) + { + case 0: + /* exact match */ + if (strcmp(token, template1) == 0) + match = TRUE; + break; + case 1: + /* leading wild card */ + if ((ptr = strstr(token, template1)) == NULL) + { + match = FALSE; + } + else + { + if (strcmp(ptr, template1) == 0) + match = TRUE; + } + break; + case 2: + /* trailing wild card */ + if (strstr(token, template1) == token) + match = TRUE; + break; + case 3: + /* trailing wild card */ + if (strstr(token, template1) != NULL) + match = TRUE; + break; + } + return (match); +} +#ifdef SKIP +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +match_elts_in_species(const char *name, const char *mytemplate) +/* ---------------------------------------------------------------------- */ +{ +/* + * Makes a list of elements with their coefficients, stores elements + * in elt_list at position count_elts. Global variable count_elts is + * updated with each stored element. Also uses static global variable + * paren_count. + * + * Arguments: + * **t_ptr input, point in token string to start looking + * output, is next position to start looking + * coef input, coefficient to multiply subscripts by + */ + int i, i1, l, case_no, match; + char c, c1; + char *ptr, *ptr1; + const char *replace_name, *name_ptr; + LDBLE d; + char element[MAX_LENGTH]; + char token[MAX_LENGTH], equal_list[MAX_LENGTH], elt_name[MAX_LENGTH]; + char token1[MAX_LENGTH], template1[MAX_LENGTH], equal_list1[MAX_LENGTH]; + char str[2]; + + strcpy(token, name); + squeeze_white(token); + while (replace("(+", "(", token)); + while (replace("++++++", "+6", token)); + while (replace("+++++", "+5", token)); + while (replace("++++", "+4", token)); + while (replace("+++", "+3", token)); + while (replace("++", "+2", token)); + while (replace("------", "-6", token)); + while (replace("-----", "-5", token)); + while (replace("----", "-4", token)); + while (replace("---", "-3", token)); + while (replace("--", "-2", token)); + + ptr = token; + count_match_tokens = 0; + while ((c = *ptr) != '\0') + { + c1 = *(ptr + 1); + str[0] = c; + str[1] = '\0'; +/* + * New element + */ + if (isupper((int) c) || (c == 'e' && c1 == '-') || (c == '[')) + { + /* + * Get new element and subscript + */ + if (get_elt(&ptr, element, &l) == ERROR) + { + return (ERROR); + } + match_tokens[count_match_tokens].name = string_hsave(element); + if (get_num(&ptr, &d) == ERROR) + { + return (ERROR); + } + match_tokens[count_match_tokens++].coef = d; + } + else + { + match_tokens[count_match_tokens].name = string_hsave(str); + match_tokens[count_match_tokens++].coef = 1.0; + ptr += 1; + } + } + /* + * Replace elements with first of equivalent elements + */ + strcpy(template1, mytemplate); + squeeze_white(template1); + ptr = template1; + while (extract_bracket(&ptr, equal_list) == TRUE) + { + replace("{", "", equal_list); + replace("}", "", equal_list); + while (replace(",", " ", equal_list) == TRUE); + ptr1 = equal_list; + /* + * Get first name in a list from template + */ + if (copy_token(elt_name, &ptr1, &l) == EMPTY) + { + error_string = sformatf( + "Expecting a nonempty list of element names in isotope sum. %s", + mytemplate); + error_msg(error_string, CONTINUE); + return (ERROR); + } + replace_name = string_hsave(elt_name); + /* + * Replace in species all equivalent names from template + */ + while (copy_token(elt_name, &ptr1, &l) != EMPTY) + { + name_ptr = string_hsave(elt_name); + for (i = 0; i < count_match_tokens; i++) + { + if (name_ptr == match_tokens[i].name) + { + match_tokens[i].name = replace_name; + } + } + } + } + /* + * Combine contiguous elements + */ + i1 = 0; + for (i = 1; i < count_match_tokens; i++) + { + if ((isupper((int) (match_tokens[i].name[0])) != FALSE) + && (match_tokens[i].name == match_tokens[i1].name)) + { + match_tokens[i1].coef += match_tokens[i].coef; + } + else + { + i1++; + match_tokens[i1].name = match_tokens[i].name; + match_tokens[i1].coef = match_tokens[i].coef; + } + } + count_match_tokens = i1 + 1; + /* + * write out string + */ + token[0] = '\0'; + for (i = 0; i < count_match_tokens; i++) + { + strcat(token, match_tokens[i].name); + if (match_tokens[i].coef != 1.0) + { + sprintf(token1, "%g", (double) match_tokens[i].coef); + strcat(token, token1); + } + } + /* + * Write a template name using first of equivalent elements + */ + strcpy(template1, mytemplate); + squeeze_white(template1); + ptr = template1; + while (extract_bracket(&ptr, equal_list) == TRUE) + { + strcpy(equal_list1, equal_list); + replace("{", "", equal_list); + replace("}", "", equal_list); + while (replace(",", " ", equal_list) == TRUE); + ptr1 = equal_list; + /* + * Get first name in a list + */ + if (copy_token(elt_name, &ptr1, &l) == EMPTY) + { + error_string = sformatf( + "Expecting a nonempty list of element names in isotope sum. %s", + mytemplate); + error_msg(error_string, CONTINUE); + return (ERROR); + } + replace_name = string_hsave(elt_name); + replace(equal_list1, replace_name, template1); + squeeze_white(template1); + ptr = template1; + } + /* + * Compare string + */ + /* Cases: 0 exact match + * 1 leading wild card + * 2 trailing wild card + * 3 leading and trailing wild card + */ + case_no = 0; + if (template1[0] == '*') + case_no = 1; + l = (int) strlen(template1); + if (template1[l - 1] == '*') + { + if (case_no != 1) + { + case_no = 2; + } + else + { + case_no = 3; + } + } + while (replace("*", "", template1)); + match = FALSE; + switch (case_no) + { + case 0: + /* exact match */ + if (strcmp(token, template1) == 0) + match = TRUE; + break; + case 1: + /* leading wild card */ + if ((ptr = strstr(token, template1)) == NULL) + { + match = FALSE; + } + else + { + if (strcmp(ptr, template1) == 0) + match = TRUE; + } + break; + case 2: + /* trailing wild card */ + if (strstr(token, template1) == token) + match = TRUE; + break; + case 3: + /* trailing wild card */ + if (strstr(token, template1) != NULL) + match = TRUE; + break; + } + return (match); +} +#endif +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +extract_bracket(char **string, char *bracket_string) +/* ---------------------------------------------------------------------- */ +{ + char *ptr, *ptr1; + + if ((ptr = strstr(*string, "{")) == NULL) + return (FALSE); + strcpy(bracket_string, ptr); + if ((ptr1 = strstr(bracket_string, "}")) == NULL) + { + error_string = sformatf( + "No matching bracket (}) in isotope template string %s", + *string); + error_msg(error_string, CONTINUE); + input_error++; + return (FALSE); + } + ptr1[1] = '\0'; + *string = strstr(*string, "}"); + *string += 1; + return (TRUE); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +surf_total(const char *total_name, const char *surface_name) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in LDBLE layer + */ + int j; + + if (use.Get_surface_ptr() == NULL || surface_name == NULL || total_name == NULL) + return (0); + + bool redox = false; + if (strstr(total_name, "(") != NULL) + { + redox = true; + } + if (!redox) + { + if (strcmp(total_name, "H") == 0 || strcmp(total_name, "O") == 0) + { + return surf_total_no_redox(total_name, surface_name); + } + } +/* + * Find surface... + */ + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE) + continue; + + std::string token; + token = x[j]->master[0]->elt->name; + replace("_", " ", token); + std::string::iterator b = token.begin(); + std::string::iterator e = token.end(); + std::string name; + CParser::copy_token(name, b, e); + if (strcmp(name.c_str(), surface_name) == 0) + break; + } + if (j >= count_unknowns) + return (0); +/* + * find total moles for redox state + */ + LDBLE t = 0; + for (j = 0; j < count_s_x; j++) + { + if (s_x[j]->type != SURF) + continue; + + std::string token; + bool match = false; + + // find if surface matches + for (int i = 0; s_x[j]->next_elt[i].elt != NULL; i++) + { + if (s_x[j]->next_elt[i].elt->master->type != SURF) continue; + + //strcpy(token, s_x[j]->next_elt[i].elt->name); + //replace("_", " ", token); + //ptr = token; + //copy_token(name, &ptr, &k); + token = s_x[j]->next_elt[i].elt->name; + replace("_", " ", token); + std::string::iterator b = token.begin(); + std::string::iterator e = token.end(); + std::string name; + CParser::copy_token(name, b, e); + if (strcmp(name.c_str(), surface_name) == 0) + { + match = true; + break; + } + } + if (!match) continue; + + // surface matches, now match element or redox state + struct rxn_token *rxn_ptr; + for (rxn_ptr = s_x[j]->rxn_s->token + 1; rxn_ptr->s != NULL; rxn_ptr++) + { + if (redox && rxn_ptr->s->secondary) + { + token = rxn_ptr->s->secondary->elt->name; + } + else if (!redox && rxn_ptr->s->secondary) + { + token = rxn_ptr->s->secondary->elt->primary->elt->name; + } + else if (!redox && rxn_ptr->s->primary) + { + token = rxn_ptr->s->primary->elt->name; + } + else + { + continue; + } + if (strcmp(token.c_str(), total_name) == 0) + { + t += rxn_ptr->coef * s_x[j]->moles; + break; + } + else + // sum all sites in case total_name is a surface name without underscore surf ("Hfo_w", "Hfo") + { + if (rxn_ptr->s->type == SURF) + { + if (token.find("_") != std::string::npos) + { + token = token.substr(0, token.find("_")); + } + if (strcmp(token.c_str(), total_name) == 0) + { + t += rxn_ptr->coef * s_x[j]->moles; + break; + } + } + } + } + } + return t; +} +#ifdef SKIP +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +surf_total(const char *total_name, const char *surface_name) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in LDBLE layer + */ + int j; + + if (use.Get_surface_ptr() == NULL || surface_name == NULL) + return (0); + +/* + * Find surface... + */ + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE) + continue; + + std::string token; + token = x[j]->master[0]->elt->name; + replace("_", " ", token); + std::string::iterator b = token.begin(); + std::string::iterator e = token.end(); + std::string name; + CParser::copy_token(name, b, e); + if (strcmp(name.c_str(), surface_name) == 0) + break; + } + if (j >= count_unknowns) + return (0); +/* + * find total moles for redox state + */ + LDBLE t = 0; + bool redox = false; + if (strstr(total_name, "(") != NULL) + { + redox = true; + } + for (j = 0; j < count_s_x; j++) + { + if (s_x[j]->type != SURF) + continue; + + std::string token; + bool match = false; + + // find if surface matches + for (int i = 0; s_x[j]->next_elt[i].elt != NULL; i++) + { + if (s_x[j]->next_elt[i].elt->master->type != SURF) continue; + + //strcpy(token, s_x[j]->next_elt[i].elt->name); + //replace("_", " ", token); + //ptr = token; + //copy_token(name, &ptr, &k); + token = s_x[j]->next_elt[i].elt->name; + replace("_", " ", token); + std::string::iterator b = token.begin(); + std::string::iterator e = token.end(); + std::string name; + CParser::copy_token(name, b, e); + if (strcmp(name.c_str(), surface_name) == 0) + { + match = true; + break; + } + } + if (!match) continue; + + // surface matches, now match element or redox state + struct rxn_token *rxn_ptr; + for (rxn_ptr = s_x[j]->rxn_s->token + 1; rxn_ptr->s != NULL; rxn_ptr++) + { + if (redox && rxn_ptr->s->secondary) + { + token = rxn_ptr->s->secondary->elt->name; + } + else if (!redox && rxn_ptr->s->secondary) + { + token = rxn_ptr->s->secondary->elt->primary->elt->name; + } + else if (!redox && rxn_ptr->s->primary) + { + token = rxn_ptr->s->primary->elt->name; + } + else + { + continue; + } + if (strcmp(token.c_str(), total_name) == 0) + { + t += rxn_ptr->coef * s_x[j]->moles; + break; + } + else + // sum all sites in case total_name is a surface name without underscore surf ("Hfo_w", "Hfo") + { + if (rxn_ptr->s->type == SURF) + { + if (token.find("_") != std::string::npos) + { + token = token.substr(0, token.find("_")); + } + if (strcmp(token.c_str(), total_name) == 0) + { + t += rxn_ptr->coef * s_x[j]->moles; + break; + } + } + } + } + } + return t; +} +#endif +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +surf_total_no_redox(const char *total_name, const char *surface_name) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in LDBLE layer + */ + int i, j, k; + char name[MAX_LENGTH], token[MAX_LENGTH]; + char surface_name_local[MAX_LENGTH]; + char *ptr; + + if (use.Get_surface_ptr() == NULL) + return (0); + +/* + * Find surface... + */ + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE) + continue; + strcpy(token, x[j]->master[0]->elt->name); + replace("_", " ", token); + ptr = token; + copy_token(name, &ptr, &k); + if (surface_name != NULL) + { + if (strcmp(name, surface_name) == 0) + break; + } + else + { + break; + } + } + if (j >= count_unknowns) + return (0); + strcpy(surface_name_local, name); +/* + * find total moles of each element in diffuse layer... + */ + count_elts = 0; + paren_count = 0; + for (j = 0; j < count_s_x; j++) + { + if (s_x[j]->type != SURF) + continue; + for (i = 0; s_x[j]->next_elt[i].elt != NULL; i++) + { + if (s_x[j]->next_elt[i].elt->master->type != SURF) continue; + + strcpy(token, s_x[j]->next_elt[i].elt->name); + replace("_", " ", token); + ptr = token; + copy_token(name, &ptr, &k); + if (strcmp(name, surface_name_local) == 0) + { +/* + * Accumulate elements in diffuse layer + */ + add_elt_list(s_x[j]->next_elt, s_x[j]->moles); + //fprintf(stderr, "%15s\t%e\t%s\t%s\n", s_x[j]->name, s_x[j]->moles, name, surface_name_local ); + break; + } + } + } + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + } +/* + * Return totals + */ + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, total_name) == 0) + { + return ((LDBLE) elt_list[j].coef); + } + } + return (0); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +total(const char *total_name) +/* ---------------------------------------------------------------------- */ +{ + struct master *master_ptr; + LDBLE t; + int i; + + if (strcmp(total_name, "H") == 0) + { + return (total_h_x / mass_water_aq_x); + } + if (strcmp(total_name, "O") == 0) + { + return (total_o_x / mass_water_aq_x); + } + master_ptr = master_bsearch(total_name); + t = 0.0; + if (master_ptr == NULL) + { + if (strcmp_nocase(total_name, "water") == 0) + { + return (mass_water_aq_x); + } + else if (strcmp_nocase(total_name, "charge") == 0) + { + return (cb_x / mass_water_aq_x); + } +/* + sprintf (error_string, "Cannot find definition for master species, %s.", + total_name); + warning_msg (error_string); +*/ + } +/* + * Primary master species + */ + else if (master_ptr->primary == TRUE) + { + /* + * Not a redox element + */ + if (master_ptr->s->secondary == NULL) + { + t = master_ptr->total / mass_water_aq_x; + /* + * Redox element, need to sum totals of all redox states + */ + } + else + { + t = 0; + for (i = master_ptr->number + 1; + (i < count_master && master[i]->elt->primary == master_ptr); + i++) + { + t += master[i]->total / mass_water_aq_x; + } + } + } +/* + * Secondary master species + */ + else + { + t = master_ptr->total / mass_water_aq_x; + } + return (t); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +total_mole(const char *total_name) +/* ---------------------------------------------------------------------- */ +{ + struct master *master_ptr; + LDBLE t; + int i; + + if (strcmp(total_name, "H") == 0) + { + return (total_h_x); + } + if (strcmp(total_name, "O") == 0) + { + return (total_o_x); + } + master_ptr = master_bsearch(total_name); + t = 0.0; + if (master_ptr == NULL) + { + if (strcmp_nocase(total_name, "water") == 0) + { + return (mass_water_aq_x / gfw_water); + } + else if (strcmp_nocase(total_name, "charge") == 0) + { + return (cb_x); + } +/* + sprintf (error_string, "Cannot find definition for master species, %s.", + total_name); + warning_msg (error_string); +*/ + } +/* + * Primary master species + */ + else if (master_ptr->primary == TRUE) + { + /* + * Not a redox element + */ + if (master_ptr->s->secondary == NULL) + { + t = master_ptr->total; + /* + * Redox element, need to sum totals of all redox states + */ + } + else + { + t = 0; + for (i = master_ptr->number + 1; + (i < count_master && master[i]->elt->primary == master_ptr); + i++) + { + t += master[i]->total; + } + } + } +/* + * Secondary master species + */ + else + { + t = master_ptr->total; + } + return (t); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_edl_species(cxxSurfaceCharge & charge_ref) +/* ---------------------------------------------------------------------- */ +{ + + double mass_water_surface = charge_ref.Get_mass_water(); + space((void **) ((void *) &sys), count_s_x, &max_sys, sizeof(struct system_species)); + count_sys = 0; + for (int j = 0; j < count_s_x; j++) + { + if (s_x[j]->type == H2O) + { + sys[count_sys].name = string_duplicate(s_x[j]->name); + sys[count_sys].moles = mass_water_surface / gfw_water; + sys_tot += sys[count_sys].moles; + count_sys++; + } + else if (s_x[j]->type < H2O) + { + double molality = under(s_x[j]->lm); + double moles_excess = mass_water_aq_x * molality * charge_ref.Get_g_map()[s_x[j]->z].Get_g(); + double moles_surface = mass_water_surface * molality + moles_excess; + sys[count_sys].name = string_duplicate(s_x[j]->name); + sys[count_sys].moles = moles_surface; + sys_tot += sys[count_sys].moles; + count_sys++; +#ifdef SKIP + double g = charge_ref.Get_g_map()[s_x[j]->z].Get_g(); + double moles_excess = mass_water_aq_x * molality * (g * s_x[j]->erm_ddl + + mass_water_surface / + mass_water_aq_x * (s_x[j]->erm_ddl - 1)); + double c = (mass_water_surface * molality + moles_excess) / mass_water_surface; + charge_ref.Get_dl_species_map()[s_x[j]->number] = c; +#endif + } + else + { + continue; + } + } +#ifdef SKIP +/* + * Provides total moles in system and lists of species/phases in sort order + */ + int i; +/* + * find total moles in aq, surface, and exchange + */ + + for (i = 0; i < count_s_x; i++) + { + //if (s_x[i]->type != AQ) + if (s_x[i]->type > HPLUS) + continue; + sys[count_sys].name = string_duplicate(s_x[i]->name); + sys[count_sys].moles = s_x[i]->moles; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("aq"); + count_sys++; + space((void **) ((void *) &sys), count_sys, &max_sys, + sizeof(struct system_species)); + } + +#endif + return (OK); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +edl_species(const char *surf_name, LDBLE * count, char ***names, LDBLE ** moles, LDBLE * area, LDBLE * thickness) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in system and lists of species/phases in sort order + */ + int i; + sys_tot = 0; + count_sys = 0; + max_sys = 100; + space((void **) ((void *) &sys), INIT, &max_sys, + sizeof(struct system_species)); + if (!(dl_type_x == cxxSurface::NO_DL)) + { + cxxSurface *surface_ptr = use.Get_surface_ptr(); + for (size_t i = 0; i < surface_ptr->Get_surface_charges().size(); i++) + { + cxxSurfaceCharge & charge_ref = surface_ptr->Get_surface_charges()[i]; + if (strcmp(charge_ref.Get_name().c_str(), surf_name) == 0) + { + get_edl_species(charge_ref); + *area = charge_ref.Get_specific_area() * charge_ref.Get_grams(); + *thickness = surface_ptr->Get_thickness(); + break; + } + } + } + /* + * Sort system species + */ + if (count_sys > 1) + { + qsort(sys, (size_t) count_sys, + (size_t) sizeof(struct system_species), system_species_compare); + } + /* + * malloc space + */ + *names = (char **) PHRQ_malloc((size_t) (count_sys + 1) * sizeof(char *)); + if (names == NULL) + malloc_error(); + *moles = (LDBLE *) PHRQ_malloc((size_t) (count_sys + 1) * sizeof(LDBLE)); + if (moles == NULL) + malloc_error(); + + (*names)[0] = NULL; + (*moles)[0] = 0; + for (i = 0; i < count_sys; i++) + { + (*names)[i + 1] = sys[i].name; + (*moles)[i + 1] = sys[i].moles; + } + *count = (LDBLE) count_sys; + + PHRQ_free(sys); + return (sys_tot); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +system_total(const char *total_name, LDBLE * count, char ***names, + char ***types, LDBLE ** moles) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in system and lists of species/phases in sort order + */ + int i; + + sys_tot = 0; + count_sys = 0; + max_sys = 100; + space((void **) ((void *) &sys), INIT, &max_sys, + sizeof(struct system_species)); + if (strcmp_nocase(total_name, "elements") == 0) + { + system_total_elements(); + } + else if (strcmp_nocase(total_name, "phases") == 0) + { + system_total_si(); + } + else if (strcmp_nocase(total_name, "aq") == 0) + { + system_total_aq(); + } + else if (strcmp_nocase(total_name, "ex") == 0) + { + system_total_ex(); + } + else if (strcmp_nocase(total_name, "surf") == 0) + { + system_total_surf(); + } + else if (strcmp_nocase(total_name, "s_s") == 0) + { + system_total_ss(); + } + else if (strcmp_nocase(total_name, "gas") == 0) + { + system_total_gas(); + } + else if (strcmp_nocase(total_name, "equi") == 0) + { + system_total_equi(); + } + else if (strcmp_nocase(total_name, "kin") == 0) + { + system_total_kin(); + } + else + { + if (strstr(total_name, "(") == NULL) + { + system_total_elt(total_name); + } + else + { + system_total_elt_secondary(total_name); + } + } + /* + * Sort system species + */ + if (count_sys > 1) + { + qsort(sys, (size_t) count_sys, + (size_t) sizeof(struct system_species), system_species_compare); + } + /* + * malloc space + */ + *names = (char **) PHRQ_malloc((size_t) (count_sys + 1) * sizeof(char *)); + if (names == NULL) + malloc_error(); + *types = (char **) PHRQ_malloc((size_t) (count_sys + 1) * sizeof(char *)); + if (types == NULL) + malloc_error(); + *moles = (LDBLE *) PHRQ_malloc((size_t) (count_sys + 1) * sizeof(LDBLE)); + if (moles == NULL) + malloc_error(); + + (*names)[0] = NULL; + (*types)[0] = NULL; + (*moles)[0] = 0; + for (i = 0; i < count_sys; i++) + { + (*names)[i + 1] = sys[i].name; + (*types)[i + 1] = sys[i].type; + (*moles)[i + 1] = sys[i].moles; + } + *count = (LDBLE) count_sys; + if (strcmp_nocase(total_name, "elements") == 0) + { + sys_tot = 0;; + for (i = 0; i < count_sys; i++) + { + if (strcmp(sys[i].type, "dis") == 0 && + strstr(sys[i].name, "(") == NULL && + strcmp(sys[i].name, "H") != 0 + && strcmp(sys[i].name, "O") != 0) + { + sys_tot += sys[i].moles; + } + } + } + PHRQ_free(sys); + return (sys_tot); +} + +/* ---------------------------------------------------------------------- */ +std::string Phreeqc:: +kinetics_formula(std::string kin_name, cxxNameDouble &stoichiometry) +/* ---------------------------------------------------------------------- */ +{ +/* + * Returns formula of kinetic reactant + * Also returns arrays of elements and stoichiometry in stoichiometry + */ + stoichiometry.clear(); + std::string formula; + + if (use.Get_kinetics_ptr() == NULL) + return (formula); + std::vector comps = use.Get_kinetics_ptr()->Get_kinetics_comps(); + count_elts = 0; + paren_count = 0; + for (size_t i=0 ; i < comps.size(); i++) + { + cxxKineticsComp *comp_ptr = &comps[i]; + if (kin_name == comp_ptr->Get_rate_name().c_str()) + { + cxxNameDouble nd = comp_ptr->Get_namecoef(); + cxxNameDouble::iterator it = nd.begin(); + for ( ; it != nd.end(); it++) + { + // Try Phases + int l; + struct phase *phase_ptr = phase_bsearch(it->first.c_str(), &l, FALSE); + if (phase_ptr != NULL) + { + add_elt_list(phase_ptr->next_elt, it->second); + } + else + { + // add formula + std::string name = it->first; + LDBLE coef = it->second; + char * temp_name = string_duplicate(name.c_str()); + char *ptr = temp_name; + get_elts_in_species(&ptr, coef); + free_check_null(temp_name); + } + } + formula.append(kin_name); + //elt_list[count_elts].elt = NULL; + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + } + stoichiometry = elt_list_NameDouble(); + break; + } + } + return (formula); +} +/* ---------------------------------------------------------------------- */ +std::string Phreeqc:: +phase_formula(std::string phase_name, cxxNameDouble &stoichiometry) +/* ---------------------------------------------------------------------- */ +{ +/* + * Returns formula of mineral + * Also returns arrays of elements and stoichiometry in elts_arg and coef_arg + */ + stoichiometry.clear(); + std::string formula; + + int j; + struct phase *phase_ptr = phase_bsearch(phase_name.c_str(), &j, FALSE); + if (phase_ptr != NULL) + { + formula.append(phase_ptr->formula); + cxxNameDouble nd(phase_ptr->next_elt); + stoichiometry = nd; + } + + return (formula); +} +/* ---------------------------------------------------------------------- */ +std::string Phreeqc:: +species_formula(std::string phase_name, cxxNameDouble &stoichiometry) +/* ---------------------------------------------------------------------- */ +{ +/* + * Returns formula of mineral + * Also returns arrays of elements and stoichiometry in elts_arg and coef_arg + */ + stoichiometry.clear(); + std::string formula; + formula = "none"; + struct species *s_ptr = s_search(phase_name.c_str()); + if (s_ptr != NULL) + { + cxxNameDouble nd(s_ptr->next_elt); + stoichiometry = nd; + stoichiometry["charge"] = s_ptr->z; + if (s_ptr->type == EX) + { + formula = "ex"; + } + else if (s_ptr->type == SURF) + { + formula = "surf"; + } + else + { + formula = "aq"; + } + } + return (formula); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_elements(void) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + LDBLE t; + char name[MAX_LENGTH]; + struct master *master_ptr; + + /* + * Include H and O + */ + sys[count_sys].name = string_duplicate("H"); + sys[count_sys].moles = total_h_x; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("dis"); + count_sys++; + space((void **) ((void *) &sys), count_sys, &max_sys, + sizeof(struct system_species)); + sys[count_sys].name = string_duplicate("O"); + sys[count_sys].moles = total_o_x; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("dis"); + count_sys++; + space((void **) ((void *) &sys), count_sys, &max_sys, + sizeof(struct system_species)); + /* + * Include H(1) and O(-2) + */ + sys[count_sys].name = string_duplicate("H(1)"); + sys[count_sys].moles = solution_sum_secondary("H(1)"); + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("dis"); + count_sys++; + space((void **) ((void *) &sys), count_sys, &max_sys, + sizeof(struct system_species)); + sys[count_sys].name = string_duplicate("O(-2)"); + sys[count_sys].moles = solution_sum_secondary("O(-2)"); + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("dis"); + count_sys++; + space((void **) ((void *) &sys), count_sys, &max_sys, + sizeof(struct system_species)); + + for (i = 0; i < count_master; i++) + { + master_ptr = master[i]; + if (master_ptr->primary == TRUE && master_ptr->total_primary <= 0) + continue; + if (master_ptr->in == FALSE + && (master_ptr->primary == FALSE + || master_ptr->total_primary == 0)) + continue; + /* + * H and O + */ + if (master_ptr->s == s_hplus) + { + continue; + } + else if (master_ptr->s == s_h2o) + { + continue; + } + if (master_ptr->primary == TRUE) + { + if (master_ptr->total_primary > 0) + { + t = master_ptr->total_primary; + /* + * Not a redox element + */ + } + else if (master_ptr->s->secondary == NULL) + { + t = master_ptr->total; + /* + * Redox element, need to sum totals of all redox states + */ + } + else + { + t = 0; + for (j = master_ptr->number + 1; + master[j]->elt->primary == master_ptr; j++) + { + t += master[j]->total; + } + } + /* + * Secondary master species + */ + } + else + { + t = master_ptr->total; + } + strcpy(name, master[i]->elt->name); + sys[count_sys].name = string_duplicate(name); + sys[count_sys].moles = t; + sys_tot += sys[count_sys].moles; + if (master[i]->s->type <= SOLID) + { + sys[count_sys].type = string_duplicate("dis"); + } + else if (master[i]->s->type == EX) + { + sys[count_sys].type = string_duplicate("ex"); + } + else if (master[i]->s->type == SURF || master[i]->s->type == SURF_PSI) + { + sys[count_sys].type = string_duplicate("surf"); + } + count_sys++; + space((void **) ((void *) &sys), count_sys, &max_sys, + sizeof(struct system_species)); + + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_si(void) +/* ---------------------------------------------------------------------- */ +{ + int i; + LDBLE si, iap; + struct rxn_token *rxn_ptr; + char name[MAX_LENGTH]; + + sys_tot = -999.9; + for (i = 0; i < count_phases; i++) + { + if (phases[i]->in == FALSE || phases[i]->type != SOLID) + continue; +/* + * Print saturation index + */ + iap = 0.0; + for (rxn_ptr = phases[i]->rxn_x->token + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + iap += rxn_ptr->s->la * rxn_ptr->coef; + } + si = -phases[i]->lk + iap; + strcpy(name, phases[i]->name); + sys[count_sys].name = string_duplicate(name); + sys[count_sys].moles = si; + if (si > sys_tot) + sys_tot = si; + sys[count_sys].type = string_duplicate("phase"); + count_sys++; + space((void **) ((void *) &sys), count_sys, &max_sys, + sizeof(struct system_species)); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_aq(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in system and lists of species/phases in sort order + */ + int i; +/* + * find total moles in aq, surface, and exchange + */ + for (i = 0; i < count_s_x; i++) + { + //if (s_x[i]->type != AQ) + if (s_x[i]->type > HPLUS) + continue; + sys[count_sys].name = string_duplicate(s_x[i]->name); + sys[count_sys].moles = s_x[i]->moles; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("aq"); + count_sys++; + space((void **) ((void *) &sys), count_sys, &max_sys, + sizeof(struct system_species)); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_ex(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in system and lists of species/phases in sort order + */ + int i; +/* + * find total moles in aq, surface, and exchange + */ + for (i = 0; i < count_s_x; i++) + { + if (s_x[i]->type != EX) + continue; + if (s_x[i]->primary != NULL) + continue; + sys[count_sys].name = string_duplicate(s_x[i]->name); + sys[count_sys].moles = s_x[i]->moles; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("ex"); + count_sys++; + space((void **) ((void *) &sys), count_sys, &max_sys, + sizeof(struct system_species)); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_surf(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in system and lists of species/phases in sort order + */ + int i; +/* + * find total moles in aq, surface, and exchange + */ + for (i = 0; i < count_s_x; i++) + { + if (s_x[i]->type != SURF) + continue; + sys[count_sys].name = string_duplicate(s_x[i]->name); + sys[count_sys].moles = s_x[i]->moles; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("surf"); + count_sys++; + space((void **) ((void *) &sys), count_sys, &max_sys, + sizeof(struct system_species)); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_gas(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in system and lists of species/phases in sort order + */ + int i; + +/* + * find total in gas phase + */ + if (use.Get_gas_phase_ptr() == NULL) + return (OK); + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++) + { + struct phase *phase_ptr = phase_bsearch(gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str(), + &i, FALSE); + assert(phase_ptr); + sys[count_sys].name = string_duplicate(phase_ptr->name); + sys[count_sys].moles = phase_ptr->moles_x; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("gas"); + count_sys++; + space((void **) ((void *) &sys), count_sys, &max_sys, + sizeof(struct system_species)); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_equi(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Equilibrium phases + */ + if (use.Get_pp_assemblage_ptr() == NULL) + return (OK); + std::map comps = use.Get_pp_assemblage_ptr()->Get_pp_assemblage_comps(); + std::map ::iterator it = comps.begin(); + for ( ; it != comps.end(); it++) + { + cxxPPassemblageComp *comp_ptr = &(it->second); + int l; + struct phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + sys[count_sys].name = string_duplicate(phase_ptr->name); + //sys[count_sys].moles = comp_ptr->Get_moles(); + sys[count_sys].moles = equi_phase(sys[count_sys].name); + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("equi"); + count_sys++; + space((void **) ((void *) &sys), count_sys, &max_sys, + sizeof(struct system_species)); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_kin(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Equilibrium phases + */ + if (use.Get_kinetics_ptr() == NULL) + return (OK); + std::vector comps = use.Get_kinetics_ptr()->Get_kinetics_comps(); + for (size_t i=0 ; i < comps.size(); i++) + { + cxxKineticsComp *comp_ptr = &comps[i]; + sys[count_sys].name = string_duplicate(comp_ptr->Get_rate_name().c_str()); + sys[count_sys].moles = comp_ptr->Get_m(); + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("kin"); + count_sys++; + space((void **) ((void *) &sys), count_sys, &max_sys, + sizeof(struct system_species)); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_ss(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in system and lists of species/phases in sort order + */ + +/* + * Solid solutions + */ + if (use.Get_ss_assemblage_ptr() == NULL) + return (OK); + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t k = 0; k < ss_ptrs.size(); k++) + { + cxxSS *ss_ptr = ss_ptrs[k]; + for (size_t i = 0; i < ss_ptr->Get_ss_comps().size(); i++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[i]); + int l; + struct phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + sys[count_sys].name = string_duplicate(phase_ptr->name); + sys[count_sys].moles = comp_ptr->Get_moles(); + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("s_s"); + count_sys++; + space((void **) ((void *) &sys), count_sys, &max_sys, + sizeof(struct system_species)); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_elt(const char *total_name) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in system and lists of species/phases in sort order + */ + int i, j, k; + LDBLE molality, moles_excess, moles_surface, mass_water_surface; + char name[MAX_LENGTH]; + +/* + * find total moles in aq, surface, and exchange + */ + for (i = 0; i < count_s_x; i++) + { + count_elts = 0; + paren_count = 0; + add_elt_list(s_x[i]->next_elt, s_x[i]->moles); + + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + } + /* + * Look for element + */ + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, total_name) == 0) + { + sys[count_sys].name = string_duplicate(s_x[i]->name); + sys[count_sys].moles = elt_list[j].coef; + sys_tot += sys[count_sys].moles; + if (s_x[i]->type == AQ) + { + sys[count_sys].type = string_duplicate("aq"); + } + else if (s_x[i]->type == EX) + { + sys[count_sys].type = string_duplicate("ex"); + /* subtract again the dummy moles of primary exchange species... */ + if (s_x[i]->primary != NULL) + { + sys_tot -= elt_list[j].coef; + } + } + else if (s_x[i]->type == SURF) + { + sys[count_sys].type = string_duplicate("surf"); + } + else if (s_x[i]->type == HPLUS) + { + sys[count_sys].type = string_duplicate("aq"); + /* sys[count_sys].moles = total_h_x; */ + } + else if (s_x[i]->type == H2O) + { + sys[count_sys].type = string_duplicate("aq"); + /* sys[count_sys].moles = total_o_x; */ + } + else + { + error_msg("System_total", STOP); + } + count_sys++; + space((void **) ((void *) &sys), count_sys, &max_sys, + sizeof(struct system_species)); + break; + } + } + } + if (use.Get_surface_ptr() != NULL && dl_type_x != cxxSurface::NO_DL) + { + /* + * Find position of component in surface charge data + */ + i = -1; + for (k = 0; k < count_unknowns; k++) + { + if (x[k]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[k]->surface_charge); + i++; + /* + * Loop through all surface components, calculate each H2O surface (diffuse layer), + * H2O aq, and H2O bulk (diffuse layers plus aqueous). + */ + mass_water_surface = charge_ptr->Get_mass_water(); + count_elts = 0; + paren_count = 0; + for (j = 0; j < count_s_x; j++) + { + if (s_x[j]->type > HPLUS) + continue; + molality = under(s_x[j]->lm); + moles_excess = + mass_water_aq_x * molality * + (charge_ptr->Get_g_map()[s_x[j]->z].Get_g() * s_x[j]->erm_ddl + + mass_water_surface / mass_water_aq_x * (s_x[j]->erm_ddl - + 1)); + moles_surface = mass_water_surface * molality + moles_excess; + /* + * Accumulate elements in diffuse layer + */ + add_elt_list(s_x[j]->next_elt, moles_surface); + } + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + } + /* + * Print totals + */ + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, total_name) == 0) + { + strcpy(name, x[k]->master[0]->elt->name); + replace("_psi", "", name); + sys[count_sys].name = string_duplicate(name); + sys[count_sys].moles = elt_list[j].coef; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("diff"); + count_sys++; + space((void **) ((void *) &sys), count_sys, &max_sys, + sizeof(struct system_species)); + break; + } + } + } + } +/* + * find total moles in mineral phases + */ + if (use.Get_pp_assemblage_in() == TRUE && use.Get_pp_assemblage_ptr() != NULL) + { + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type != PP) + continue; + //std::map::iterator it; + //it = pp_assemblage_ptr->Get_pp_assemblage_comps().find(x[i]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp * ) x[i]->pp_assemblage_comp_ptr; + //if (it->second.Get_add_formula().size() > 0) + if (comp_ptr->Get_add_formula().size() > 0) + continue; + count_elts = 0; + paren_count = 0; + int j; + //struct phase * phase_ptr = phase_bsearch(x[i]->pp_assemblage_comp_name, &j, FALSE); + struct phase * phase_ptr = x[i]->phase; + add_elt_list(phase_ptr->next_elt, x[i]->moles); + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + } + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, total_name) == 0) + { + sys[count_sys].name = + string_duplicate(phase_ptr->name); + sys[count_sys].moles = elt_list[j].coef; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("equi"); + count_sys++; + space((void **) ((void *) &sys), count_sys, &max_sys, + sizeof(struct system_species)); + break; + } + } + } + } +/* + * Solid solutions + */ + if (use.Get_ss_assemblage_ptr() != NULL) + { + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t k = 0; k < ss_ptrs.size(); k++) + { + cxxSS *ss_ptr = ss_ptrs[k]; + if (ss_ptr->Get_ss_in()) + { + for (size_t i = 0; i < ss_ptr->Get_ss_comps().size(); i++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[i]); + int l; + struct phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + count_elts = 0; + paren_count = 0; + add_elt_list(phase_ptr->next_elt, + comp_ptr->Get_moles()); + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), + elt_list_compare); + elt_list_combine(); + } + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, total_name) == 0) + { + sys[count_sys].name = + string_duplicate(phase_ptr->name); + sys[count_sys].moles = elt_list[j].coef; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("s_s"); + count_sys++; + space((void **) ((void *) &sys), count_sys, + &max_sys, sizeof(struct system_species)); + break; + } + } + } + } + } + } +/* + * find total in gas phase + */ + if (use.Get_gas_phase_ptr() != NULL) + { + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + struct phase *phase_ptr = + phase_bsearch(gas_phase_ptr->Get_gas_comps()[i].Get_phase_name().c_str(), &k, FALSE); + assert(phase_ptr); + if (phase_ptr->in == TRUE) + { + count_elts = 0; + paren_count = 0; + add_elt_list(phase_ptr->next_elt, phase_ptr->moles_x); + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + } + /* + * Look for element + */ + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, total_name) == 0) + { + sys[count_sys].name = string_duplicate(phase_ptr->name); + sys[count_sys].moles = elt_list[j].coef; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("gas"); + count_sys++; + space((void **) ((void *) &sys), count_sys, &max_sys, + sizeof(struct system_species)); + break; + } + } + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_elt_secondary(const char *total_name) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in system and lists of species/phases in sort order + */ + int i, j, k, l; + LDBLE molality, moles_excess, moles_surface, mass_water_surface, sum, + coef; + char name[MAX_LENGTH]; +/* + * find total moles in aq, surface, and exchange + */ + for (i = 0; i < count_s_x; i++) + { + count_elts = 0; + paren_count = 0; + if (s_x[i]->next_secondary != NULL) + { + add_elt_list(s_x[i]->next_secondary, s_x[i]->moles); + } + else + { + add_elt_list(s_x[i]->next_sys_total, s_x[i]->moles); + } + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + } + /* + * Look for element + */ + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, total_name) == 0) + { + sys[count_sys].name = string_duplicate(s_x[i]->name); + sys[count_sys].moles = elt_list[j].coef; + sys_tot += sys[count_sys].moles; + if (s_x[i]->type == AQ) + { + sys[count_sys].type = string_duplicate("aq"); + } + else if (s_x[i]->type == EX) + { + sys[count_sys].type = string_duplicate("ex"); + } + else if (s_x[i]->type == SURF) + { + sys[count_sys].type = string_duplicate("surf"); + } + else if (s_x[i]->type == HPLUS) + { + sys[count_sys].type = string_duplicate("aq"); + /* sys[count_sys].moles = total_h_x; */ + } + else if (s_x[i]->type == H2O) + { + sys[count_sys].type = string_duplicate("aq"); + /* sys[count_sys].moles = total_o_x; */ + } + else + { + error_msg("System_total", STOP); + } + count_sys++; + space((void **) ((void *) &sys), count_sys, &max_sys, + sizeof(struct system_species)); + break; + } + } + } + if (use.Get_surface_ptr() != NULL && dl_type_x != cxxSurface::NO_DL) + { + /* + * Find position of component in surface charge data + */ + i = -1; + for (k = 0; k < count_unknowns; k++) + { + if (x[k]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[k]->surface_charge); + i++; + /* + * Loop through all surface components, calculate each H2O surface (diffuse layer), + * H2O aq, and H2O bulk (diffuse layers plus aqueous). + */ + mass_water_surface = charge_ptr->Get_mass_water(); + sum = 0; + for (j = 0; j < count_s_x; j++) + { + count_elts = 0; + paren_count = 0; + if (s_x[i]->next_secondary != NULL) + { + add_elt_list(s_x[i]->next_secondary, 1); + } + else + { + add_elt_list(s_x[i]->next_sys_total, 1); + } + for (l = 0; l < count_elts; l++) + { + if (strcmp(elt_list[l].elt->name, total_name) == 0) + { + coef = elt_list[l].coef; + if (s_x[j]->type > H2O) + continue; + molality = under(s_x[j]->lm); + moles_excess = + mass_water_aq_x * molality * + charge_ptr->Get_g_map()[s_x[j]->z].Get_g(); + moles_surface = + mass_water_surface * molality + moles_excess; + sum += moles_surface * coef; + break; + } + } + if (l >= count_elts) + continue; + strcpy(name, x[k]->master[0]->elt->name); + replace("_psi", "", name); + sys[count_sys].name = string_duplicate(name); + sys[count_sys].moles = sum; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("diff"); + count_sys++; + space((void **) ((void *) &sys), count_sys, &max_sys, + sizeof(struct system_species)); + break; + } + } + } +/* + * find total moles in mineral phases + */ + if (use.Get_pp_assemblage_in() == TRUE && use.Get_pp_assemblage_ptr() != NULL) + { + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type != PP) + continue; + //std::map::iterator it; + //it = pp_assemblage_ptr->Get_pp_assemblage_comps().find(x[i]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp * ) x[i]->pp_assemblage_comp_ptr; + //if (it->second.Get_add_formula().size() > 0) + if (comp_ptr->Get_add_formula().size() > 0) + continue; + count_elts = 0; + paren_count = 0; + int j; + //struct phase * phase_ptr = phase_bsearch(x[i]->pp_assemblage_comp_name, &j, FALSE); + struct phase * phase_ptr = x[i]->phase; + add_elt_list(phase_ptr->next_sys_total, x[i]->moles); + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + } + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, total_name) == 0) + { + sys[count_sys].name = + string_duplicate(phase_ptr->name); + sys[count_sys].moles = elt_list[j].coef; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("equi"); + count_sys++; + space((void **) ((void *) &sys), count_sys, &max_sys, + sizeof(struct system_species)); + break; + } + } + } + } +/* + * Solid solutions + */ + if (use.Get_ss_assemblage_ptr() != NULL) + { + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t i = 0; i < ss_ptrs.size(); i++) + { + cxxSS *ss_ptr = ss_ptrs[i]; + if (ss_ptr->Get_ss_in()) + { + for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[k]); + int l; + struct phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + count_elts = 0; + paren_count = 0; + add_elt_list(phase_ptr->next_sys_total, + comp_ptr->Get_moles()); + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), + elt_list_compare); + elt_list_combine(); + } + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, total_name) == 0) + { + sys[count_sys].name = + string_duplicate(phase_ptr->name); + sys[count_sys].moles = elt_list[j].coef; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("s_s"); + count_sys++; + space((void **) ((void *) &sys), count_sys, + &max_sys, sizeof(struct system_species)); + break; + } + } + } + } + } + } +/* + * find total in gas phase + */ + if (use.Get_gas_phase_ptr() != NULL) + { + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++) + { + struct phase *phase_ptr = + phase_bsearch(gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str(), &i, FALSE); + assert(phase_ptr); + if (phase_ptr->in == TRUE) + { + count_elts = 0; + paren_count = 0; + add_elt_list(phase_ptr->next_sys_total, + phase_ptr->moles_x); + + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + } + /* + * Look for element + */ + for (size_t j1 = 0; j1 < (size_t) count_elts; j1++) + { + if (strcmp(elt_list[j1].elt->name, total_name) == 0) + { + sys[count_sys].name = + string_duplicate(phase_ptr->name); + sys[count_sys].moles = elt_list[j1].coef; + sys_tot += sys[count_sys].moles; + sys[count_sys].type = string_duplicate("gas"); + count_sys++; + space((void **) ((void *) &sys), count_sys, &max_sys, + sizeof(struct system_species)); + break; + } + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +solution_number(void) +/* ---------------------------------------------------------------------- */ +{ + Phreeqc * PhreeqcPtr = this; + int soln_no = -999; + if (PhreeqcPtr->state == TRANSPORT) + { + soln_no = PhreeqcPtr->cell_no; + } + else if (PhreeqcPtr->state == PHAST) + { + soln_no = PhreeqcPtr->cell_no; + } + else if (PhreeqcPtr->state == ADVECTION) + { + soln_no = PhreeqcPtr->cell_no; + } + else if (PhreeqcPtr->state < REACTION) + { + soln_no = PhreeqcPtr->use.Get_solution_ptr()->Get_n_user(); + } + else + { + if (PhreeqcPtr->use.Get_mix_in()) + { + soln_no = PhreeqcPtr->use.Get_n_mix_user(); + } + else + { + soln_no = PhreeqcPtr->use.Get_n_solution_user(); + } + } + return soln_no; +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +solution_sum_secondary(const char *total_name) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in system and lists of species/phases in sort order + */ + int i, j; + LDBLE sum; +/* + * find total moles in aq, surface, and exchange + */ + sum = 0; + for (i = 0; i < count_s_x; i++) + { + if (s_x[i]->type > H2O) + continue; + count_elts = 0; + paren_count = 0; + if (s_x[i]->next_secondary != NULL) + { + add_elt_list(s_x[i]->next_secondary, s_x[i]->moles); + } + else + { + add_elt_list(s_x[i]->next_sys_total, s_x[i]->moles); + } + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + } + /* + * Look for element + */ + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, total_name) == 0) + { + sum += elt_list[j].coef; + break; + } + } + } + return (sum); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_species_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + const struct system_species *a, *b; + + a = (const struct system_species *) ptr1; + b = (const struct system_species *) ptr2; + if (a->moles < b->moles) + return (1); + if (a->moles > b->moles) + return (-1); + return (0); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_total_solids(cxxExchange *exchange_ptr, + cxxPPassemblage *pp_assemblage_ptr, + cxxGasPhase *gas_phase_ptr, + cxxSSassemblage *ss_assemblage_ptr, + cxxSurface *surface_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Provides total moles in solid phases + */ + count_elts = 0; + paren_count = 0; +/* + * find total moles in exchanger + */ + if (exchange_ptr != NULL) + { + for (size_t i = 0; i < exchange_ptr->Get_exchange_comps().size(); i++) + { + add_elt_list(exchange_ptr->Get_exchange_comps()[i].Get_totals(), 1.0); + } + } + if (surface_ptr != NULL) + { + for (size_t i = 0; i < surface_ptr->Get_surface_comps().size(); i++) + { + add_elt_list(surface_ptr->Get_surface_comps()[i].Get_totals(), 1.0); + } + } + if (ss_assemblage_ptr != NULL) + { + std::vector ss_ptrs = ss_assemblage_ptr->Vectorize(); + for (size_t i = 0; i < ss_ptrs.size(); i++) + { + cxxSS *ss_ptr = ss_ptrs[i]; + for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[j]); + int l; + struct phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + add_elt_list(phase_ptr->next_elt, + comp_ptr->Get_moles()); + } + } + } + if (gas_phase_ptr != NULL) + { + for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++) + { + int i; + struct phase *phase_ptr = + phase_bsearch(gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str(), &i, FALSE); + add_elt_list(phase_ptr->next_elt, gas_phase_ptr->Get_gas_comps()[j].Get_moles()); + } + } + if (pp_assemblage_ptr != NULL) + { + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + int j; + struct phase * phase_ptr = phase_bsearch(it->first.c_str(), &j, FALSE); + add_elt_list(phase_ptr->next_elt, + it->second.Get_moles()); + } + } + + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + } + return (OK); +} + +LDBLE Phreeqc:: +iso_value(const char *total_name) +{ + int j; + char token[MAX_LENGTH]; + char my_total_name[MAX_LENGTH]; + strcpy(token, ""); + strcpy(my_total_name, total_name); + while (replace(" ","_",my_total_name)); + for (j = 0; j < count_isotope_ratio; j++) + { + if (isotope_ratio[j]->ratio == MISSING) + continue; + if (strcmp(my_total_name, isotope_ratio[j]->name) != 0) + continue; + return (isotope_ratio[j]->converted_ratio); + } + strcpy(my_total_name, total_name); + while (replace("[","",my_total_name)); + while (replace("]","",my_total_name)); + strcat(token,"R("); + strcat(token,my_total_name); + strcat(token,")"); + for (j = 0; j < count_isotope_ratio; j++) + { + if (isotope_ratio[j]->ratio == MISSING) + continue; + if (strcmp(token, isotope_ratio[j]->name) != 0) + continue; + return (isotope_ratio[j]->converted_ratio); + } + return -1000.; +} + +char * Phreeqc:: +iso_unit(const char *total_name) +{ + int j; + char token[MAX_LENGTH], unit[MAX_LENGTH]; + struct master_isotope *master_isotope_ptr; + char my_total_name[MAX_LENGTH]; + strcpy(token, ""); + strcpy(my_total_name, total_name); + while (replace(" ","_",my_total_name)); + strcpy(unit, "unknown"); + for (j = 0; j < count_isotope_ratio; j++) + { + if (isotope_ratio[j]->ratio == MISSING) + continue; + if (strcmp(my_total_name, isotope_ratio[j]->name) != 0) + continue; + master_isotope_ptr = master_isotope_search(isotope_ratio[j]->isotope_name); + if (master_isotope_ptr != NULL) + { + strcpy(unit, master_isotope_ptr->units); + } + return string_duplicate(unit); + } + strcpy(my_total_name, total_name); + while (replace("[","",my_total_name)); + while (replace("]","",my_total_name)); + strcat(token,"R("); + strcat(token,my_total_name); + strcat(token,")"); + for (j = 0; j < count_isotope_ratio; j++) + { + if (isotope_ratio[j]->ratio == MISSING) + continue; + if (strcmp(token, isotope_ratio[j]->name) != 0) + continue; + master_isotope_ptr = master_isotope_search(isotope_ratio[j]->isotope_name); + if (master_isotope_ptr != NULL) + { + strcpy(unit, master_isotope_ptr->units); + } + return string_duplicate(unit); + } + return string_duplicate(unit); +} + +int Phreeqc:: +basic_compile(char *commands, void **lnbase, void **vbase, void **lpbase) +{ + return this->basic_interpreter->basic_compile(commands, lnbase, vbase, lpbase); +} + +int Phreeqc:: +basic_run(char *commands, void *lnbase, void *vbase, void *lpbase) +{ + return this->basic_interpreter->basic_run(commands, lnbase, vbase, lpbase); +} + +void Phreeqc:: +basic_free(void) +{ + delete this->basic_interpreter; +} + +#if defined(SWIG) || defined(SWIG_IPHREEQC) + +#include "BasicCallback.h" + +double Phreeqc:: +basic_callback(double x1, double x2, const char * str) +{ + if (this->basicCallback) + { + return this->basicCallback->Callback(x1, x2, str); + } + return 0.0; +} + +#else /* defined(SWIG) || defined(SWIG_IPHREEQC) */ + +#ifdef IPHREEQC_NO_FORTRAN_MODULE +double Phreeqc:: +basic_callback(double x1, double x2, char * str) +#else +double Phreeqc:: +basic_callback(double x1, double x2, const char * str) +#endif +{ + double local_x1 = x1; + double local_x2 = x2; + + if (basic_callback_ptr != NULL) + { + return (*basic_callback_ptr) (x1, x2, (const char *) str, basic_callback_cookie); + } + if (basic_fortran_callback_ptr != NULL) + { +#ifdef IPHREEQC_NO_FORTRAN_MODULE + return (*basic_fortran_callback_ptr) (&local_x1, &local_x2, str, (int) strlen(str)); +#else + return (*basic_fortran_callback_ptr) (&local_x1, &local_x2, str, (int) strlen(str)); +#endif + } + return 0; +} + +void +Phreeqc::register_basic_callback(double (*fcn)(double x1, double x2, const char *str, void *cookie), void *cookie1) +{ + this->basic_callback_ptr = fcn; + this->basic_callback_cookie = cookie1; +} +#ifdef IPHREEQC_NO_FORTRAN_MODULE +void +Phreeqc::register_fortran_basic_callback(double ( *fcn)(double *x1, double *x2, char *str, size_t l)) +{ + this->basic_fortran_callback_ptr = fcn; +} +#else + +void +Phreeqc::register_fortran_basic_callback(double ( *fcn)(double *x1, double *x2, const char *str, int l)) +{ + this->basic_fortran_callback_ptr = fcn; +} +#endif + +#endif /* defined(SWIG) || defined(SWIG_IPHREEQC) */ diff --git a/phreeqcpp/chart_icon.gif b/phreeqcpp/chart_icon.gif new file mode 100644 index 00000000..53bfb63a Binary files /dev/null and b/phreeqcpp/chart_icon.gif differ diff --git a/phreeqcpp/cl1.cpp b/phreeqcpp/cl1.cpp new file mode 100644 index 00000000..ce47b201 --- /dev/null +++ b/phreeqcpp/cl1.cpp @@ -0,0 +1,896 @@ +#include +#include +#include +#include +#include "Phreeqc.h" +#include "phqalloc.h" + +/* debug +#define DEBUG_CL1 +#define CHECK_ERRORS + */ + +int Phreeqc:: +cl1(int k, int l, int m, int n, + int l_nklmd, int l_n2d, + LDBLE * q, + int *l_kode, LDBLE l_toler, + int *l_iter, LDBLE * l_x, LDBLE * l_res, LDBLE * l_error, + LDBLE * l_cu, int *l_iu, int *l_s, int check) +{ + /* System generated locals */ + union double_or_int + { + int ival; + LDBLE dval; + } *q2; + + /* Local variables */ + int nklm; + LDBLE xmin, xmax; + int iout = 0; + // static i runs faster on windows + register int i, j; + register LDBLE l_z; + int maxit, n1, n2; + LDBLE pivot; + int ia, ii, kk, nk, js; + int in = 0; + LDBLE sn; + int iphase, kforce; + LDBLE zu, zv; + LDBLE tpivot; + int klm, jmn, nkl, jpn; + LDBLE cuv; + long double sum; + int klm1; + int q_dim, cu_dim; + int kode_arg; + LDBLE check_toler; +#ifdef CHECK_ERRORS + char **col_name, **row_name; + int *row_back, *col_back; +#endif +/* THIS SUBROUTINE USES A MODIFICATION OF THE SIMPLEX */ +/* METHOD OF LINEAR PROGRAMMING TO CALCULATE AN L1 SOLUTION */ +/* TO A K BY N SYSTEM OF LINEAR EQUATIONS */ +/* AX=B */ +/* SUBJECT TO L LINEAR EQUALITY CONSTRAINTS */ +/* CX=D */ +/* AND M LINEAR INEQUALITY CONSTRAINTS */ +/* EX.LE.F. */ +/* DESCRIPTION OF PARAMETERS */ +/* K NUMBER OF ROWS OF THE MATRIX A (K.GE.1). */ +/* L NUMBER OF ROWS OF THE MATRIX C (L.GE.0). */ +/* M NUMBER OF ROWS OF THE MATRIX E (M.GE.0). */ +/* N NUMBER OF COLUMNS OF THE MATRICES A,C,E (N.GE.1). */ +/* KLMD SET TO AT LEAST K+L+M FOR ADJUSTABLE DIMENSIONS. */ +/* KLM2D SET TO AT LEAST K+L+M+2 FOR ADJUSTABLE DIMENSIONS. */ +/* NKLMD SET TO AT LEAST N+K+L+M FOR ADJUSTABLE DIMENSIONS. */ +/* N2D SET TO AT LEAST N+2 FOR ADJUSTABLE DIMENSIONS */ +/* Q TWO DIMENSIONAL REAL ARRAY WITH KLM2D ROWS AND */ +/* AT LEAST N2D COLUMNS. */ +/* ON ENTRY THE MATRICES A,C AND E, AND THE VECTORS */ +/* B,D AND F MUST BE STORED IN THE FIRST K+L+M ROWS */ +/* AND N+1 COLUMNS OF Q AS FOLLOWS */ +/* A B */ +/* Q = C D */ +/* E F */ +/* THESE VALUES ARE DESTROYED BY THE SUBROUTINE. */ +/* KODE A CODE USED ON ENTRY TO, AND EXIT */ +/* FROM, THE SUBROUTINE. */ +/* ON ENTRY, THIS SHOULD NORMALLY BE SET TO 0. */ +/* HOWEVER, IF CERTAIN NONNEGATIVITY CONSTRAINTS */ +/* ARE TO BE INCLUDED IMPLICITLY, RATHER THAN */ +/* EXPLICITLY IN THE CONSTRAINTS EX.LE.F, THEN KODE */ +/* SHOULD BE SET TO 1, AND THE NONNEGATIVITY */ +/* CONSTRAINTS INCLUDED IN THE ARRAYS X AND */ +/* RES (SEE BELOW). */ +/* ON EXIT, KODE HAS ONE OF THE */ +/* FOLLOWING VALUES */ +/* 0- OPTIMAL SOLUTION FOUND, */ +/* 1- NO FEASIBLE SOLUTION TO THE */ +/* CONSTRAINTS, */ +/* 2- CALCULATIONS TERMINATED */ +/* PREMATURELY DUE TO ROUNDING ERRORS, */ +/* 3- MAXIMUM NUMBER OF ITERATIONS REACHED. */ +/* TOLER A SMALL POSITIVE TOLERANCE. EMPIRICAL */ +/* EVIDENCE SUGGESTS TOLER = 10**(-D*2/3), */ +/* WHERE D REPRESENTS THE NUMBER OF DECIMAL */ +/* DIGITS OF ACCURACY AVAILABLE. ESSENTIALLY, */ +/* THE SUBROUTINE CANNOT DISTINGUISH BETWEEN ZERO */ +/* AND ANY QUANTITY WHOSE MAGNITUDE DOES NOT EXCEED */ +/* TOLER. IN PARTICULAR, IT WILL NOT PIVOT ON ANY */ +/* NUMBER WHOSE MAGNITUDE DOES NOT EXCEED TOLER. */ +/* ITER ON ENTRY ITER MUST CONTAIN AN UPPER BOUND ON */ +/* THE MAXIMUM NUMBER OF ITERATIONS ALLOWED. */ +/* A SUGGESTED VALUE IS 10*(K+L+M). ON EXIT ITER */ +/* GIVES THE NUMBER OF SIMPLEX ITERATIONS. */ +/* X ONE DIMENSIONAL REAL ARRAY OF SIZE AT LEAST N2D. */ +/* ON EXIT THIS ARRAY CONTAINS A */ +/* SOLUTION TO THE L1 PROBLEM. IF KODE=1 */ +/* ON ENTRY, THIS ARRAY IS ALSO USED TO INCLUDE */ +/* SIMPLE NONNEGATIVITY CONSTRAINTS ON THE */ +/* VARIABLES. THE VALUES -1, 0, OR 1 */ +/* FOR X(J) INDICATE THAT THE J-TH VARIABLE */ +/* IS RESTRICTED TO BE .LE.0, UNRESTRICTED, */ +/* OR .GE.0 RESPECTIVELY. */ +/* RES ONE DIMENSIONAL REAL ARRAY OF SIZE AT LEAST KLMD. */ +/* ON EXIT THIS CONTAINS THE RESIDUALS B-AX */ +/* IN THE FIRST K COMPONENTS, D-CX IN THE */ +/* NEXT L COMPONENTS (THESE WILL BE =0),AND */ +/* F-EX IN THE NEXT M COMPONENTS. IF KODE=1 ON */ +/* ENTRY, THIS ARRAY IS ALSO USED TO INCLUDE SIMPLE */ +/* NONNEGATIVITY CONSTRAINTS ON THE RESIDUALS */ +/* B-AX. THE VALUES -1, 0, OR 1 FOR RES(I) */ +/* INDICATE THAT THE I-TH RESIDUAL (1.LE.I.LE.K) IS */ +/* RESTRICTED TO BE .LE.0, UNRESTRICTED, OR .GE.0 */ +/* RESPECTIVELY. */ +/* ERROR ON EXIT, THIS GIVES THE MINIMUM SUM OF */ +/* ABSOLUTE VALUES OF THE RESIDUALS. */ +/* CU A TWO DIMENSIONAL REAL ARRAY WITH TWO ROWS AND */ +/* AT LEAST NKLMD COLUMNS USED FOR WORKSPACE. */ +/* IU A TWO DIMENSIONAL INTEGER ARRAY WITH TWO ROWS AND */ +/* AT LEAST NKLMD COLUMNS USED FOR WORKSPACE. */ +/* S INTEGER ARRAY OF SIZE AT LEAST KLMD, USED FOR */ +/* WORKSPACE. */ +/* DOUBLE PRECISION DBLE */ +/* REAL */ + +/* INITIALIZATION. */ + + zv = 0; + kode_arg = *l_kode; + cl1_space(check, l_n2d, k + l + m, l_nklmd); + +/* Parameter adjustments */ + q_dim = l_n2d; + q2 = (union double_or_int *) q; + cu_dim = l_nklmd; + +/* Function Body */ + maxit = *l_iter; + n1 = n + 1; + n2 = n + 2; + nk = n + k; + nkl = nk + l; + klm = k + l + m; + klm1 = klm + 1; + nklm = n + klm; + kforce = 1; + *l_iter = 0; + js = 0; + ia = -1; + +/* SET UP LABELS IN Q. */ + for (j = 0; j < n; ++j) + { + q2[klm1 * q_dim + j].ival = j + 1; + } +/* L10: */ + for (i = 0; i < klm; ++i) + { + q2[i * q_dim + n1].ival = n + i + 1; + if (q2[i * q_dim + n].dval < 0.) + { + for (j = 0; j < n1; ++j) + { + q2[i * q_dim + j].dval = -q2[i * q_dim + j].dval; + } + q2[i * q_dim + n1].ival = -q2[i * q_dim + n1].ival; +/* L20: */ + } + } +/* L30: */ +/* SET UP PHASE 1 COSTS. */ + iphase = 2; +#ifdef DEBUG_CL1 + output_msg(sformatf( "Set up phase 1 costs\n")); +#endif +/* Zero first row of cu and iu */ + memcpy((void *) &(l_cu[0]), (void *) &(scratch[0]), + (size_t) nklm * sizeof(LDBLE)); + for (j = 0; j < nklm; ++j) + { + l_iu[j] = 0; + } +/* L40: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L40\n")); +#endif + if (l != 0) + { + for (j = nk; j < nkl; ++j) + { + l_cu[j] = 1.; + l_iu[j] = 1; + } +/* L50: */ + iphase = 1; + } + +/* Copy first row of cu and iu to second row */ + memcpy((void *) &(l_cu[cu_dim]), (void *) &(l_cu[0]), + (size_t) nklm * sizeof(LDBLE)); + memcpy((void *) &(l_iu[cu_dim]), (void *) &(l_iu[0]), + (size_t) nklm * sizeof(int)); + +/* L60: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L60\n")); +#endif + if (m != 0) + { + for (j = nkl; j < nklm; ++j) + { + l_cu[cu_dim + j] = 1.; + l_iu[cu_dim + j] = 1; + jmn = j - n; + if (q2[jmn * q_dim + n1].ival < 0) + { + iphase = 1; + } + } +/* L70: */ + } +/* L80: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L80\n")); +#endif + if (*l_kode != 0) + { + for (j = 0; j < n; ++j) + { + if (l_x[j] < 0.) + { +/* L90: */ + l_cu[j] = 1.; + l_iu[j] = 1; + } + else if (l_x[j] > 0.) + { + l_cu[cu_dim + j] = 1.; + l_iu[cu_dim + j] = 1; + } + } +/* L110: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L110\n")); +#endif + for (j = 0; j < k; ++j) + { + jpn = j + n; + if (l_res[j] < 0.) + { +/* L120: */ + l_cu[jpn] = 1.; + l_iu[jpn] = 1; + if (q2[j * q_dim + n1].ival > 0) + { + iphase = 1; + } + } + else if (l_res[j] > 0.) + { +/* L130: */ + l_cu[cu_dim + jpn] = 1.; + l_iu[cu_dim + jpn] = 1; + if (q2[j * q_dim + n1].ival < 0) + { + iphase = 1; + } + } + } +/* L140: */ + } +/* L150: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L150\n")); +#endif + if (iphase == 2) + { + goto L500; + } +/* COMPUTE THE MARGINAL COSTS. */ + L160: +#ifdef DEBUG_CL1 + output_msg(sformatf( "L160\n")); +#endif + for (j = js; j < n1; ++j) + { + sum = 0.; + for (i = 0; i < klm; ++i) + { + ii = q2[i * q_dim + n1].ival; + if (ii < 0) + { + l_z = l_cu[cu_dim - ii - 1]; + } + else + { + l_z = l_cu[ii - 1]; + } + sum += (long double) q2[i * q_dim + j].dval * (long double) l_z; + } + q2[klm * q_dim + j].dval = (double)sum; + } + for (j = js; j < n; ++j) + { + ii = q2[klm1 * q_dim + j].ival; + if (ii < 0) + { + l_z = l_cu[cu_dim - ii - 1]; + } + else + { + l_z = l_cu[ii - 1]; + } + q2[klm * q_dim + j].dval -= l_z; + } +/* DETERMINE THE VECTOR TO ENTER THE BASIS. */ + L240: +#ifdef DEBUG_CL1 + output_msg(sformatf( "L240, xmax %e\n", xmax)); +#endif + xmax = 0.; + if (js >= n) + { + goto L490; /* test for optimality */ + } + for (j = js; j < n; ++j) + { + zu = q2[klm * q_dim + j].dval; + ii = q2[klm1 * q_dim + j].ival; + if (ii > 0) + { + zv = -zu - l_cu[ii - 1] - l_cu[cu_dim + ii - 1]; + } + else + { + ii = -ii; + zv = zu; + zu = -zu - l_cu[ii - 1] - l_cu[cu_dim + ii - 1]; + } +/* L260 */ + if (kforce == 1 && ii > n) + { + continue; + } + if (l_iu[ii - 1] != 1 && zu > xmax) + { + xmax = zu; + in = j; + } +/* L270 */ + if (l_iu[cu_dim + ii - 1] != 1 && zv > xmax) + { + xmax = zv; + in = j; + } + } +/* L280 */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L280 xmax %e, toler %e\n", xmax, l_toler)); +#endif + if (xmax <= l_toler) + { +#ifdef DEBUG_CL1 + output_msg(sformatf( "xmax before optimality test %e\n", xmax)); +#endif + goto L490; /* test for optimality */ + } + if (q2[klm * q_dim + in].dval != xmax) + { + for (i = 0; i < klm1; ++i) + { + q2[i * q_dim + in].dval = -q2[i * q_dim + in].dval; + } + q2[klm1 * q_dim + in].ival = -q2[klm1 * q_dim + in].ival; +/* L290: */ + q2[klm * q_dim + in].dval = xmax; + } +/* DETERMINE THE VECTOR TO LEAVE THE BASIS. */ + if (iphase != 1 && ia != -1) + { + xmax = 0.; +/* find maximum absolute value in column "in" */ + for (i = 0; i <= ia; ++i) + { + l_z = fabs(q2[i * q_dim + in].dval); + if (l_z > xmax) + { + xmax = l_z; + iout = i; + } + } +/* L310: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L310, xmax %e\n", xmax)); +#endif +/* switch row ia with row iout, use memcpy */ + if (xmax > l_toler) + { + if (ia != iout) + { + memcpy((void *) &(scratch[0]), (void *) &(q2[ia * q_dim]), + (size_t) n2 * sizeof(LDBLE)); + memcpy((void *) &(q2[ia * q_dim]), (void *) &(q2[iout * q_dim]), + (size_t) n2 * sizeof(LDBLE)); + memcpy((void *) &(q2[iout * q_dim]), (void *) &(scratch[0]), + (size_t) n2 * sizeof(LDBLE)); + } +/* L320: */ +/* set pivot to row ia, column in */ + iout = ia; + --ia; + pivot = q2[iout * q_dim + in].dval; + goto L420; /* Gauss Jordan */ + } + } +/* L330: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L330, xmax %e\n", xmax)); +#endif + kk = -1; +/* divide column n1 by positive value in column "in" greater than toler */ + for (i = 0; i < klm; ++i) + { + l_z = q2[i * q_dim + in].dval; + if (l_z > l_toler) + { + ++kk; + l_res[kk] = q2[i * q_dim + n].dval / l_z; + l_s[kk] = i; + } + } +/* L340: */ +#ifdef DEBUG_CL1 + if (kk < 0) + { + output_msg(sformatf( "kode = 2 in loop 340.\n")); + } +#endif + L350: +#ifdef DEBUG_CL1 + output_msg(sformatf( "L350, xmax %e\n", xmax)); +#endif + if (kk < 0) + { +/* no positive value found in L340 or bypass intermediate verticies */ + *l_kode = 2; + goto L590; + } +/* L360: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L360, xmax %e\n", xmax)); +#endif +/* find minimum residual */ + xmin = l_res[0]; + iout = l_s[0]; + j = 0; + if (kk != 0) + { + for (i = 1; i <= kk; ++i) + { + if (l_res[i] < xmin) + { + j = i; + xmin = l_res[i]; + iout = l_s[i]; + } + } +/* L370: */ +/* put kk in position j */ + l_res[j] = l_res[kk]; + l_s[j] = l_s[kk]; + } +/* L380: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L380 iout %d, xmin %e, xmax %e\n", iout, + xmin, xmax)); +#endif + --kk; + pivot = q2[iout * q_dim + in].dval; + ii = q2[iout * q_dim + n1].ival; + if (iphase != 1) + { + if (ii < 0) + { +/* L390: */ + if (l_iu[-ii - 1] == 1) + { + goto L420; + } + } + else + { + if (l_iu[cu_dim + ii - 1] == 1) + { + goto L420; + } + } + } +/* L400: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L400\n")); +#endif + ii = abs(ii); + cuv = l_cu[ii - 1] + l_cu[cu_dim + ii - 1]; + if (q2[klm * q_dim + in].dval - pivot * cuv > l_toler) + { + +/* BYPASS INTERMEDIATE VERTICES. */ + for (j = js; j < n1; ++j) + { + l_z = q2[iout * q_dim + j].dval; + q2[klm * q_dim + j].dval -= l_z * cuv; + q2[iout * q_dim + j].dval = -l_z; + } +/* L410: */ + q2[iout * q_dim + n1].ival = -q2[iout * q_dim + n1].ival; + goto L350; + } +/* GAUSS-JORDAN ELIMINATION. */ + L420: +#ifdef DEBUG_CL1 + output_msg(sformatf( "Gauss Jordon %d\n", *l_iter)); +#endif + if (*l_iter >= maxit) + { + *l_kode = 3; + goto L590; + } +/* L430: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L430\n")); +#endif + ++(*l_iter); + for (j = js; j < n1; ++j) + { + if (j != in) + { + q2[iout * q_dim + j].dval /= pivot; + } + } +/* L440: */ + for (j = js; j < n1; ++j) + { + if (j != in) + { + l_z = -q2[iout * q_dim + j].dval; + for (i = 0; i < klm1; ++i) + { + if (i != iout) + { + q2[i * q_dim + j].dval += l_z * q2[i * q_dim + in].dval; + } + } +/* L450: */ + } + } +/* L460: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L460\n")); +#endif + tpivot = -pivot; + for (i = 0; i < klm1; ++i) + { + if (i != iout) + { + q2[i * q_dim + in].dval /= tpivot; + } + } +/* L470: */ + q2[iout * q_dim + in].dval = 1. / pivot; + ii = q2[iout * q_dim + n1].ival; + q2[iout * q_dim + n1].ival = q2[klm1 * q_dim + in].ival; + q2[klm1 * q_dim + in].ival = ii; + ii = abs(ii); + if (l_iu[ii - 1] == 0 || l_iu[cu_dim + ii - 1] == 0) + { + goto L240; + } +/* switch column */ + for (i = 0; i < klm1; ++i) + { + l_z = q2[i * q_dim + in].dval; + q2[i * q_dim + in].dval = q2[i * q_dim + js].dval; + q2[i * q_dim + js].dval = l_z; + } + i = q2[klm1 * q_dim + in].ival; + q2[klm1 * q_dim + in].ival = q2[klm1 * q_dim + js].ival; + q2[klm1 * q_dim + js].ival = i; +/* L480: */ + ++js; + goto L240; +/* TEST FOR OPTIMALITY. */ + L490: +#ifdef DEBUG_CL1 + output_msg(sformatf( "L490\n")); +#endif + if (kforce == 0) + { + if (iphase == 1) + { + if (q2[klm * q_dim + n].dval <= l_toler) + { + goto L500; + } +#ifdef DEBUG_CL1 + output_msg(sformatf( "q2[klm1-1, n1-1] > *toler. %e\n", + q2[(klm1 - 1) * q_dim + n1 - 1].dval)); +#endif + *l_kode = 1; + goto L590; + } + *l_kode = 0; + goto L590; + } + if (iphase != 1 || q2[klm * q_dim + n].dval > l_toler) + { + kforce = 0; + goto L240; + } +/* SET UP PHASE 2 COSTS. */ + L500: +#ifdef DEBUG_CL1 + output_msg(sformatf( "Set up phase 2 costs %d\n", *l_iter)); +#endif + iphase = 2; + for (j = 0; j < nklm; ++j) + { + l_cu[j] = 0.; + } +/* L510: */ + for (j = n; j < nk; ++j) + { + l_cu[j] = 1.; + } + memcpy((void *) &(l_cu[cu_dim]), (void *) &(l_cu[0]), + (size_t) nklm * sizeof(LDBLE)); +/* L520: */ + for (i = 0; i < klm; ++i) + { + ii = q2[i * q_dim + n1].ival; + if (ii <= 0) + { + if (l_iu[cu_dim - ii - 1] == 0) + { + continue; + } + l_cu[cu_dim - ii - 1] = 0.; + } + else + { +/* L530: */ + if (l_iu[ii - 1] == 0) + { + continue; + } + l_cu[ii - 1] = 0.; + } +/* L540: */ + ++ia; +/* switch row */ + if (ia != i) + { + memcpy((void *) &(scratch[0]), (void *) &(q2[ia * q_dim]), + (size_t) n2 * sizeof(LDBLE)); + memcpy((void *) &(q2[ia * q_dim]), (void *) &(q2[i * q_dim]), + (size_t) n2 * sizeof(LDBLE)); + memcpy((void *) &(q2[i * q_dim]), (void *) &(scratch[0]), + (size_t) n2 * sizeof(LDBLE)); + } +/* L550: */ + } +/* L560: */ + goto L160; + + +/* PREPARE OUTPUT. */ + L590: +#ifdef DEBUG_CL1 + output_msg(sformatf( "L590\n")); +#endif + sum = 0.; + for (j = 0; j < n; ++j) + { + l_x[j] = 0.; + } +/* L600: */ + for (i = 0; i < klm; ++i) + { + l_res[i] = 0.; + } +/* L610: */ + for (i = 0; i < klm; ++i) + { + ii = q2[i * q_dim + n1].ival; + sn = 1.; + if (ii < 0) + { + ii = -ii; + sn = -1.; + } + if (ii <= n) + { +/* L620: */ + l_x[ii - 1] = sn * q2[i * q_dim + n].dval; + } + else + { +/* L630: */ + l_res[ii - n - 1] = sn * q2[i * q_dim + n].dval; + if (ii >= n1 && ii <= nk) + { +/* * DBLE(Q(I,N1)) */ + sum += (long double) q2[i * q_dim + n].dval; + } + } + } +/* L640: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L640\n")); +#endif + *l_error = (double)sum; + /* + * Check calculation + */ + if ((check == 1) && (*l_kode == 0)) + { + check_toler = 10. * l_toler; + /* + * Check optimization constraints + */ + if (kode_arg == 1) + { + for (i = 0; i < k; ++i) + { + if (res_arg[i] < 0.0) + { + if (l_res[i] > check_toler) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1: optimization constraint not satisfied row %d, res %s, constraint %f.\n", + row_name[row_back[i]], l_res[i], res_arg[i])); +#endif + *l_kode = 1; + } + } + else if (res_arg[i] > 0.0) + { + if (l_res[i] < -check_toler) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1: optimization constraint not satisfied row %s, res %e, constraint %f.\n", + row_name[row_back[i]], l_res[i], res_arg[i])); +#endif + *l_kode = 1; + } + } + } + } + /* + * Check equalities + */ + for (i = k; i < k + l; ++i) + { + if (fabs(l_res[i]) > check_toler) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1: equality constraint not satisfied row %s, res %e, tolerance %e.\n", + row_name[row_back[i]], l_res[i], check_toler)); +#endif + *l_kode = 1; + } + } + /* + * Check inequalities + */ + for (i = k + l; i < k + l + m; ++i) + { + if (l_res[i] < -check_toler) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1: inequality constraint not satisfied row %s, res %e, tolerance %e.\n", + row_name[row_back[i]], l_res[i], check_toler)); +#endif + *l_kode = 1; + } + } + /* + * Check dissolution/precipitation constraints + */ + if (kode_arg == 1) + { + for (i = 0; i < n; ++i) + { + if (x_arg[i] < 0.0) + { + if (l_x[i] > check_toler) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1: dis/pre constraint not satisfied column %s, x %e, constraint %f.\n", + col_name[col_back[i]], l_x[i], x_arg[i])); +#endif + *l_kode = 1; + } + } + else if (x_arg[i] > 0.0) + { + if (l_x[i] < -check_toler) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1: dis/pre constraint not satisfied column %s, x %e, constraint %f.\n", + col_name[col_back[i]], l_x[i], x_arg[i])); +#endif + *l_kode = 1; + } + } + } + } + if (*l_kode == 1) + { + output_msg(sformatf( + "\n\tCL1: Roundoff errors in optimization.\n\t Try using -multiple_precision in INVERSE_MODELING\n")); + } + } + return 0; +} + +void Phreeqc:: +cl1_space(int check, int l_n2d, int klm, int l_nklmd) +{ + if (check == 1) + { + if (x_arg == NULL) + { + x_arg = (LDBLE *) PHRQ_malloc((size_t) (l_n2d * sizeof(LDBLE))); + } + else if (l_n2d > x_arg_max) + { + x_arg = + (LDBLE *) PHRQ_realloc(x_arg, (size_t) (l_n2d * sizeof(LDBLE))); + x_arg_max = l_n2d; + } + if (x_arg == NULL) + malloc_error(); + zero_double(x_arg, l_n2d); + + if (res_arg == NULL) + { + res_arg = (LDBLE *) PHRQ_malloc((size_t) ((klm) * sizeof(LDBLE))); + } + else if (klm > res_arg_max) + { + res_arg = + (LDBLE *) PHRQ_realloc(res_arg, + (size_t) ((klm) * sizeof(LDBLE))); + res_arg_max = klm; + } + if (res_arg == NULL) + malloc_error(); + zero_double(res_arg, klm); + } + +/* Make scratch space */ + if (scratch == NULL) + { + scratch = (LDBLE *) PHRQ_malloc((size_t) l_nklmd * sizeof(LDBLE)); + } + else if (l_nklmd > scratch_max) + { + scratch = + (LDBLE *) PHRQ_realloc(scratch, (size_t) l_nklmd * sizeof(LDBLE)); + scratch_max = l_nklmd; + } + if (scratch == NULL) + malloc_error(); + zero_double(scratch, l_nklmd); +} diff --git a/phreeqcpp/cl1mp.cpp b/phreeqcpp/cl1mp.cpp new file mode 100644 index 00000000..0d1d2d1d --- /dev/null +++ b/phreeqcpp/cl1mp.cpp @@ -0,0 +1,1134 @@ +#ifdef INVERSE_CL1MP +#include +#include +#include +#include +#include + +#include "Phreeqc.h" +#include "phqalloc.h" +#include "phrqtype.h" + +/* debug +#define DEBUG_CL1 +#define CHECK_ERRORS + */ + + +int Phreeqc:: +cl1mp(int k, int l, int m, int n, + int nklmd, int n2d, + LDBLE * q_arg, + int *kode_arg, LDBLE toler_arg, + int *iter, LDBLE * x_arg, LDBLE * res_arg, LDBLE * error_arg, + LDBLE * cu_arg, int *iu, int *s, int check, LDBLE censor_arg) +{ + mpf_set_default_prec(256); + /* System generated locals */ + union double_or_int + { + int ival; + mpf_t dval; + } *q2; + + /* Local variables */ + int nklm; + int iout = 0; + // static i runs faster + int i, j; + int maxit, n1; //, n2; + int ia, ii, kk, nk, js; + int in = 0; + int iphase, kforce; + int klm, jmn, nkl, jpn; + int klm1; + int *kode; + int q_dim, cu_dim; + int iswitch; + mpf_t *q; + mpf_t *x; + mpf_t *res; + mpf_t error; + mpf_t *cu; + mpf_t dummy, dummy1, sum, z, zu, zv, xmax, minus_one, toler, check_toler; + /*mpf_t *scratch; */ + mpf_t pivot, xmin, cuv, tpivot, sn; + mpf_t zero; + int censor; + mpf_t censor_tol; +/* THIS SUBROUTINE USES A MODIFICATION OF THE SIMPLEX */ +/* METHOD OF LINEAR PROGRAMMING TO CALCULATE AN L1 SOLUTION */ +/* TO A K BY N SYSTEM OF LINEAR EQUATIONS */ +/* AX=B */ +/* SUBJECT TO L LINEAR EQUALITY CONSTRAINTS */ +/* CX=D */ +/* AND M LINEAR INEQUALITY CONSTRAINTS */ +/* EX.LE.F. */ +/* DESCRIPTION OF PARAMETERS */ +/* K NUMBER OF ROWS OF THE MATRIX A (K.GE.1). */ +/* L NUMBER OF ROWS OF THE MATRIX C (L.GE.0). */ +/* M NUMBER OF ROWS OF THE MATRIX E (M.GE.0). */ +/* N NUMBER OF COLUMNS OF THE MATRICES A,C,E (N.GE.1). */ +/* KLMD SET TO AT LEAST K+L+M FOR ADJUSTABLE DIMENSIONS. */ +/* KLM2D SET TO AT LEAST K+L+M+2 FOR ADJUSTABLE DIMENSIONS. */ +/* NKLMD SET TO AT LEAST N+K+L+M FOR ADJUSTABLE DIMENSIONS. */ +/* N2D SET TO AT LEAST N+2 FOR ADJUSTABLE DIMENSIONS */ +/* Q TWO DIMENSIONAL REAL ARRAY WITH KLM2D ROWS AND */ +/* AT LEAST N2D COLUMNS. */ +/* ON ENTRY THE MATRICES A,C AND E, AND THE VECTORS */ +/* B,D AND F MUST BE STORED IN THE FIRST K+L+M ROWS */ +/* AND N+1 COLUMNS OF Q AS FOLLOWS */ +/* A B */ +/* Q = C D */ +/* E F */ +/* THESE VALUES ARE DESTROYED BY THE SUBROUTINE. */ +/* KODE A CODE USED ON ENTRY TO, AND EXIT */ +/* FROM, THE SUBROUTINE. */ +/* ON ENTRY, THIS SHOULD NORMALLY BE SET TO 0. */ +/* HOWEVER, IF CERTAIN NONNEGATIVITY CONSTRAINTS */ +/* ARE TO BE INCLUDED IMPLICITLY, RATHER THAN */ +/* EXPLICITLY IN THE CONSTRAINTS EX.LE.F, THEN KODE */ +/* SHOULD BE SET TO 1, AND THE NONNEGATIVITY */ +/* CONSTRAINTS INCLUDED IN THE ARRAYS X AND */ +/* RES (SEE BELOW). */ +/* ON EXIT, KODE HAS ONE OF THE */ +/* FOLLOWING VALUES */ +/* 0- OPTIMAL SOLUTION FOUND, */ +/* 1- NO FEASIBLE SOLUTION TO THE */ +/* CONSTRAINTS, */ +/* 2- CALCULATIONS TERMINATED */ +/* PREMATURELY DUE TO ROUNDING ERRORS, */ +/* 3- MAXIMUM NUMBER OF ITERATIONS REACHED. */ +/* TOLER A SMALL POSITIVE TOLERANCE. EMPIRICAL */ +/* EVIDENCE SUGGESTS TOLER = 10**(-D*2/3), */ +/* WHERE D REPRESENTS THE NUMBER OF DECIMAL */ +/* DIGITS OF ACCURACY AVAILABLE. ESSENTIALLY, */ +/* THE SUBROUTINE CANNOT DISTINGUISH BETWEEN ZERO */ +/* AND ANY QUANTITY WHOSE MAGNITUDE DOES NOT EXCEED */ +/* TOLER. IN PARTICULAR, IT WILL NOT PIVOT ON ANY */ +/* NUMBER WHOSE MAGNITUDE DOES NOT EXCEED TOLER. */ +/* ITER ON ENTRY ITER MUST CONTAIN AN UPPER BOUND ON */ +/* THE MAXIMUM NUMBER OF ITERATIONS ALLOWED. */ +/* A SUGGESTED VALUE IS 10*(K+L+M). ON EXIT ITER */ +/* GIVES THE NUMBER OF SIMPLEX ITERATIONS. */ +/* X ONE DIMENSIONAL REAL ARRAY OF SIZE AT LEAST N2D. */ +/* ON EXIT THIS ARRAY CONTAINS A */ +/* SOLUTION TO THE L1 PROBLEM. IF KODE=1 */ +/* ON ENTRY, THIS ARRAY IS ALSO USED TO INCLUDE */ +/* SIMPLE NONNEGATIVITY CONSTRAINTS ON THE */ +/* VARIABLES. THE VALUES -1, 0, OR 1 */ +/* FOR X(J) INDICATE THAT THE J-TH VARIABLE */ +/* IS RESTRICTED TO BE .LE.0, UNRESTRICTED, */ +/* OR .GE.0 RESPECTIVELY. */ +/* RES ONE DIMENSIONAL REAL ARRAY OF SIZE AT LEAST KLMD. */ +/* ON EXIT THIS CONTAINS THE RESIDUALS B-AX */ +/* IN THE FIRST K COMPONENTS, D-CX IN THE */ +/* NEXT L COMPONENTS (THESE WILL BE =0),AND */ +/* F-EX IN THE NEXT M COMPONENTS. IF KODE=1 ON */ +/* ENTRY, THIS ARRAY IS ALSO USED TO INCLUDE SIMPLE */ +/* NONNEGATIVITY CONSTRAINTS ON THE RESIDUALS */ +/* B-AX. THE VALUES -1, 0, OR 1 FOR RES(I) */ +/* INDICATE THAT THE I-TH RESIDUAL (1.LE.I.LE.K) IS */ +/* RESTRICTED TO BE .LE.0, UNRESTRICTED, OR .GE.0 */ +/* RESPECTIVELY. */ +/* ERROR ON EXIT, THIS GIVES THE MINIMUM SUM OF */ +/* ABSOLUTE VALUES OF THE RESIDUALS. */ +/* CU A TWO DIMENSIONAL REAL ARRAY WITH TWO ROWS AND */ +/* AT LEAST NKLMD COLUMNS USED FOR WORKSPACE. */ +/* IU A TWO DIMENSIONAL INTEGER ARRAY WITH TWO ROWS AND */ +/* AT LEAST NKLMD COLUMNS USED FOR WORKSPACE. */ +/* S INTEGER ARRAY OF SIZE AT LEAST KLMD, USED FOR */ +/* WORKSPACE. */ +/* DOUBLE PRECISION DBLE */ +/* REAL */ + +/* INITIALIZATION. */ + /* + * mp variables + */ + censor = 1; + if (censor_arg == 0.0) + censor = 0; + mpf_set_default_prec(96); + mpf_init(zero); + mpf_init(dummy); + mpf_init(dummy1); + mpf_init_set_d(censor_tol, censor_arg); + q = (mpf_t *) + PHRQ_malloc((size_t) + (max_row_count * max_column_count * sizeof(mpf_t))); + if (q == NULL) + malloc_error(); + for (i = 0; i < max_row_count * max_column_count; ++i) + { + mpf_init_set_d(q[i], q_arg[i]); + if (censor == 1) + { + if (mpf_cmp(q[i], zero) != 0) + { + mpf_abs(dummy1, q[i]); + if (mpf_cmp(dummy1, censor_tol) <= 0) + { + mpf_set_si(q[i], 0); + } + } + } + } + x = (mpf_t *) PHRQ_malloc((size_t) (n2d * sizeof(mpf_t))); + if (x == NULL) + malloc_error(); + for (i = 0; i < n2d; ++i) + { + mpf_init_set_d(x[i], x_arg[i]); + } + res = (mpf_t *) PHRQ_malloc((size_t) ((k + l + m) * sizeof(mpf_t))); + if (res == NULL) + malloc_error(); + for (i = 0; i < k + l + m; ++i) + { + mpf_init_set_d(res[i], res_arg[i]); + } + cu = (mpf_t *) PHRQ_malloc((size_t) (2 * nklmd * sizeof(mpf_t))); + if (cu == NULL) + malloc_error(); + for (i = 0; i < 2 * nklmd; ++i) + { + mpf_init_set_d(cu[i], cu_arg[i]); + } + kode = (int *) PHRQ_malloc(sizeof(int)); + if (kode == NULL) + malloc_error(); + *kode = *kode_arg; + mpf_init(sum); + mpf_init(error); + mpf_init(z); + mpf_init(zu); + mpf_init(zv); + mpf_init(xmax); + mpf_init_set_si(minus_one, -1); + mpf_init_set_d(toler, toler_arg); + mpf_init_set_d(check_toler, toler_arg); + mpf_init(pivot); + mpf_init(xmin); + mpf_init(cuv); + mpf_init(tpivot); + mpf_init(sn); +/* Parameter adjustments */ + q_dim = n2d; + q2 = (union double_or_int *) q; + cu_dim = nklmd; + +/* Function Body */ + maxit = *iter; + n1 = n + 1; + // n2 = n + 2; + nk = n + k; + nkl = nk + l; + klm = k + l + m; + klm1 = klm + 1; + nklm = n + klm; + kforce = 1; + *iter = 0; + js = 0; + ia = -1; +/* Make scratch space */ +/* + scratch = (LDBLE *) PHRQ_malloc( (size_t) nklmd * sizeof(LDBLE)); + if (scratch == NULL) malloc_error(); + for (i=0; i < nklmd; i++) { + scratch[i] = 0.0; + } +*/ +/* + scratch = (mpf_t *) PHRQ_malloc( (size_t) nklmd * sizeof(mpf_t)); + if (scratch == NULL) malloc_error(); + for (i=0; i < nklmd; i++) { + mpf_init(scratch[i]); + } +*/ +/* SET UP LABELS IN Q. */ + for (j = 0; j < n; ++j) + { + q2[klm1 * q_dim + j].ival = j + 1; + } +/* L10: */ + for (i = 0; i < klm; ++i) + { + q2[i * q_dim + n1].ival = n + i + 1; + if (mpf_cmp_d(q2[i * q_dim + n].dval, 0.0) < 0) + { + for (j = 0; j < n1; ++j) + { + /* q2[ i * q_dim + j ].dval = -q2[ i * q_dim + j ].dval; */ + mpf_neg(q2[i * q_dim + j].dval, q2[i * q_dim + j].dval); + } + q2[i * q_dim + n1].ival = -q2[i * q_dim + n1].ival; +/* L20: */ + } + } +/* L30: */ +/* SET UP PHASE 1 COSTS. */ + iphase = 2; +#ifdef DEBUG_CL1 + output_msg(sformatf( "Set up phase 1 costs\n")); +#endif +/* Zero first row of cu and iu */ + /*memcpy( (void *) &(cu[0]), (void *) &(scratch[0]), (size_t) nklm * sizeof(mpf_t) ); */ + for (j = 0; j < nklm; ++j) + { + mpf_set_si(cu[j], 0); + iu[j] = 0; + } +/* L40: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L40\n")); +#endif + if (l != 0) + { + for (j = nk; j < nkl; ++j) + { + mpf_set_si(cu[j], 1); + /*cu[ j ] = 1.; */ + iu[j] = 1; + } +/* L50: */ + iphase = 1; + } + +/* Copy first row of cu and iu to second row */ + /*memcpy( (void *) &(cu[cu_dim]), (void *) &(cu[0]), (size_t) nklm * sizeof(mpf_t) ); */ + for (i = 0; i < nklm; ++i) + { + mpf_set(cu[cu_dim + i], cu[i]); + } + memcpy((void *) &(iu[cu_dim]), (void *) &(iu[0]), + (size_t) nklm * sizeof(int)); +/* L60: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L60\n")); +#endif + if (m != 0) + { + for (j = nkl; j < nklm; ++j) + { + /* cu[ cu_dim + j ] = 1.; */ + mpf_set_si(cu[cu_dim + j], 1); + iu[cu_dim + j] = 1; + jmn = j - n; + if (q2[jmn * q_dim + n1].ival < 0) + { + iphase = 1; + } + } +/* L70: */ + } +/* L80: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L80\n")); +#endif + if (*kode != 0) + { + for (j = 0; j < n; ++j) + { + /* if ( x[j] < 0.) { */ + if (mpf_cmp_si(x[j], 0) < 0) + { +/* L90: */ + /* cu[ j ] = 1.; */ + mpf_set_si(cu[j], 1); + iu[j] = 1; + /* } else if (x[j] > 0.) { */ + } + else if (mpf_cmp_si(x[j], 0) > 0) + { + /* cu[ cu_dim + j ] = 1.; */ + mpf_set_si(cu[cu_dim + j], 1); + iu[cu_dim + j] = 1; + } + } +/* L110: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L110\n")); +#endif + for (j = 0; j < k; ++j) + { + jpn = j + n; + /* if (res[j] < 0.) { */ + if (mpf_cmp_si(res[j], 0) < 0) + { +/* L120: */ + /* cu[ jpn ] = 1.; */ + mpf_set_si(cu[jpn], 1); + iu[jpn] = 1; + if (q2[j * q_dim + n1].ival > 0) + { + iphase = 1; + } + /* } else if (res[j] > 0.) { */ + } + else if (mpf_cmp_si(res[j], 0) > 0) + { +/* L130: */ + /* cu[ cu_dim + jpn ] = 1.; */ + mpf_set_si(cu[cu_dim + jpn], 1); + iu[cu_dim + jpn] = 1; + if (q2[j * q_dim + n1].ival < 0) + { + iphase = 1; + } + } + } +/* L140: */ + } +/* L150: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L150\n")); +#endif + if (iphase == 2) + { + goto L500; + } +/* COMPUTE THE MARGINAL COSTS. */ + L160: +#ifdef DEBUG_CL1 + output_msg(sformatf( "L160\n")); +#endif + for (j = js; j < n1; ++j) + { + mpf_set_si(sum, 0); + for (i = 0; i < klm; ++i) + { + ii = q2[i * q_dim + n1].ival; + if (ii < 0) + { + /* z = cu[ cu_dim - ii - 1 ]; */ + mpf_set(z, cu[cu_dim - ii - 1]); + } + else + { + /*z = cu[ ii - 1 ]; */ + mpf_set(z, cu[ii - 1]); + } + /*sum += q2[ i * q_dim + j ].dval * z; */ + mpf_mul(dummy, q2[i * q_dim + j].dval, z); + mpf_add(sum, sum, dummy); + } + /*q2[ klm * q_dim + j ].dval = sum; */ + mpf_set(q2[klm * q_dim + j].dval, sum); + } + for (j = js; j < n; ++j) + { + ii = q2[klm1 * q_dim + j].ival; + if (ii < 0) + { + /*z = cu[ cu_dim - ii - 1 ]; */ + mpf_set(z, cu[cu_dim - ii - 1]); + } + else + { + /*z = cu[ ii - 1 ]; */ + mpf_set(z, cu[ii - 1]); + } + /*q2[ klm * q_dim + j ].dval -= z; */ + mpf_sub(q2[klm * q_dim + j].dval, q2[klm * q_dim + j].dval, z); + } +/* DETERMINE THE VECTOR TO ENTER THE BASIS. */ + L240: +#ifdef DEBUG_CL1 + output_msg(sformatf( "L240, xmax %e\n", mpf_get_d(xmax))); +#endif + /*xmax = 0.; */ + mpf_set_si(xmax, 0); + if (js >= n) + { + goto L490; /* test for optimality */ + } + for (j = js; j < n; ++j) + { + /*zu = q2[ klm * q_dim + j ].dval; */ + mpf_set(zu, q2[klm * q_dim + j].dval); + ii = q2[klm1 * q_dim + j].ival; + if (ii > 0) + { + /*zv = -zu - cu[ ii - 1 ] - cu[ cu_dim + ii - 1 ]; */ + mpf_mul(dummy, cu[cu_dim + ii - 1], minus_one); + mpf_sub(dummy, dummy, cu[ii - 1]); + mpf_sub(zv, dummy, zu); + } + else + { + ii = -ii; + /* zv = zu; */ + mpf_set(zv, zu); + /* zu = -zu - cu[ ii - 1 ] - cu[ cu_dim + ii - 1 ]; */ + mpf_mul(dummy, cu[cu_dim + ii - 1], minus_one); + mpf_sub(dummy, dummy, cu[ii - 1]); + mpf_sub(zu, dummy, zu); + } +/* L260 */ + if (kforce == 1 && ii > n) + { + continue; + } + /*if (iu[ ii - 1 ] != 1 && zu > xmax){ */ + if ((iu[ii - 1] != 1) && (mpf_cmp(zu, xmax) > 0)) + { + /*xmax = zu; */ + mpf_set(xmax, zu); + in = j; + } +/* L270 */ + /*if (iu[ cu_dim + ii - 1 ] != 1 && zv > xmax ) { */ + if ((iu[cu_dim + ii - 1] != 1) && (mpf_cmp(zv, xmax) > 0)) + { + /*xmax = zv; */ + mpf_set(xmax, zv); + in = j; + } + } +/* L280 */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L280 xmax %e, toler %e\n", mpf_get_d(xmax), + mpf_get_d(toler))); +#endif + /*if (xmax <= toler) { */ + if (mpf_cmp(xmax, toler) <= 0) + { +#ifdef DEBUG_CL1 + output_msg(sformatf( "xmax before optimality test %e\n", + mpf_get_d(xmax))); +#endif + goto L490; /* test for optimality */ + } + /*if (q2[ klm * q_dim + in ].dval != xmax) { */ + if (mpf_cmp(q2[klm * q_dim + in].dval, xmax) != 0) + { + for (i = 0; i < klm1; ++i) + { + /*q2[ i * q_dim + in ].dval = -q2[ i * q_dim + in ].dval; */ + mpf_neg(q2[i * q_dim + in].dval, q2[i * q_dim + in].dval); + } + q2[klm1 * q_dim + in].ival = -q2[klm1 * q_dim + in].ival; +/* L290: */ + /*q2[ klm * q_dim + in ].dval = xmax; */ + mpf_set(q2[klm * q_dim + in].dval, xmax); + } +/* DETERMINE THE VECTOR TO LEAVE THE BASIS. */ + if (iphase != 1 && ia != -1) + { + /*xmax = 0.; */ + mpf_set_si(xmax, 0); +/* find maximum absolute value in column "in" */ + for (i = 0; i <= ia; ++i) + { + /*z = fabs(q2[ i * q_dim + in ].dval); */ + mpf_abs(z, q2[i * q_dim + in].dval); + /*if (z > xmax) { */ + if (mpf_cmp(z, xmax) > 0) + { + /*xmax = z; */ + mpf_set(xmax, z); + iout = i; + } + } +/* L310: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L310, xmax %e\n", mpf_get_d(xmax))); +#endif +/* switch row ia with row iout, use memcpy */ + /*if (xmax > toler) { */ + if (mpf_cmp(xmax, toler) > 0) + { + /* + memcpy( (void *) &(scratch[0]), (void *) &(q2[ ia * q_dim]), + (size_t) n2 * sizeof(mpf_t) ); + memcpy( (void *) &(q2[ ia * q_dim ]), (void *) &(q2[ iout * q_dim]), + (size_t) n2 * sizeof(mpf_t) ); + memcpy( (void *) &(q2[ iout * q_dim ]), (void *) &(scratch[ 0 ]), + (size_t) n2 * sizeof(mpf_t) ); + */ + for (i = 0; i < n1; ++i) + { + mpf_set(dummy, q2[ia * q_dim + i].dval); + mpf_set(q2[ia * q_dim + i].dval, q2[iout * q_dim + i].dval); + mpf_set(q2[iout * q_dim + i].dval, dummy); + } + j = q2[ia * q_dim + n1].ival; + q2[ia * q_dim + n1].ival = q2[iout * q_dim + n1].ival; + q2[iout * q_dim + n1].ival = j; + +/* L320: */ +/* set pivot to row ia, column in */ + iout = ia; + --ia; + /*pivot = q2[ iout * q_dim + in ].dval; */ + mpf_set(pivot, q2[iout * q_dim + in].dval); + goto L420; /* Gauss Jordan */ + } + } +/* L330: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L330, xmax %e\n", mpf_get_d(xmax))); +#endif + kk = -1; +/* divide column n1 by positive value in column "in" greater than toler */ + for (i = 0; i < klm; ++i) + { + /*z = q2[ i * q_dim + in ].dval; */ + mpf_set(z, q2[i * q_dim + in].dval); + /*if (z > toler) { */ + if (mpf_cmp(z, toler) > 0) + { + ++kk; + /*res[kk] = q2[ i * q_dim + n ].dval / z; */ + mpf_div(res[kk], q2[i * q_dim + n].dval, z); + s[kk] = i; + } + } +/* L340: */ + if (kk < 0) + { + output_msg(sformatf( "kode = 2 in loop 340.\n")); + } + L350: +#ifdef DEBUG_CL1 + output_msg(sformatf( "L350, xmax %e\n", mpf_get_d(xmax))); +#endif + if (kk < 0) + { +/* no positive value found in L340 or bypass intermediate verticies */ + *kode = 2; + goto L590; + } +/* L360: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L360, xmax %e\n", mpf_get_d(xmax))); +#endif +/* find minimum residual */ + /*xmin = res[ 0 ]; */ + mpf_set(xmin, res[0]); + iout = s[0]; + j = 0; + if (kk != 0) + { + for (i = 1; i <= kk; ++i) + { + /*if (res[i] < xmin) { */ + if (mpf_cmp(res[i], xmin) < 0) + { + j = i; + /*xmin = res[i]; */ + mpf_set(xmin, res[i]); + iout = s[i]; + } + } +/* L370: */ +/* put kk in position j */ + /*res[j] = res[kk]; */ + mpf_set(res[j], res[kk]); + s[j] = s[kk]; + } +/* L380: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L380 iout %d, xmin %e, xmax %e\n", iout, + mpf_get_d(xmin), mpf_get_d(xmax))); +#endif + --kk; + /*pivot = q2[ iout * q_dim + in ].dval; */ + mpf_set(pivot, q2[iout * q_dim + in].dval); + ii = q2[iout * q_dim + n1].ival; + if (iphase != 1) + { + if (ii < 0) + { +/* L390: */ + if (iu[-ii - 1] == 1) + { + goto L420; + } + } + else + { + if (iu[cu_dim + ii - 1] == 1) + { + goto L420; + } + } + } +/* L400: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L400\n")); +#endif + ii = abs(ii); + /*cuv = cu[ ii - 1 ] + cu[ cu_dim + ii - 1]; */ + mpf_add(cuv, cu[ii - 1], cu[cu_dim + ii - 1]); + /*if (q2[ klm * q_dim + in ].dval - pivot * cuv > toler) { */ + mpf_mul(dummy, pivot, cuv); + mpf_sub(dummy, q2[klm * q_dim + in].dval, dummy); + if (mpf_cmp(dummy, toler) > 0) + { +/* BYPASS INTERMEDIATE VERTICES. */ + for (j = js; j < n1; ++j) + { + /*z = q2[ iout * q_dim + j ].dval; */ + mpf_set(z, q2[iout * q_dim + j].dval); + /*q2[ klm * q_dim + j ].dval -= z * cuv; */ + mpf_mul(dummy1, z, cuv); + mpf_sub(q2[klm * q_dim + j].dval, q2[klm * q_dim + j].dval, + dummy1); + + if (censor == 1) + { + if (mpf_cmp(q2[klm * q_dim + j].dval, zero) != 0) + { + mpf_abs(dummy1, q2[klm * q_dim + j].dval); + if (mpf_cmp(dummy1, censor_tol) <= 0) + { + mpf_set_si(q2[klm * q_dim + j].dval, 0); + } + } + } + + /*q2[ iout * q_dim + j ].dval = -z; */ + mpf_neg(q2[iout * q_dim + j].dval, z); + } +/* L410: */ + q2[iout * q_dim + n1].ival = -q2[iout * q_dim + n1].ival; + goto L350; + } +/* GAUSS-JORDAN ELIMINATION. */ + L420: +#ifdef DEBUG_CL1 + output_msg(sformatf( "Gauss Jordon %d\n", *iter)); +#endif + if (*iter >= maxit) + { + *kode = 3; + goto L590; + } +/* L430: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L430\n")); +#endif + ++(*iter); + for (j = js; j < n1; ++j) + { + if (j != in) + { + /*q2[ iout * q_dim + j ].dval /= pivot; */ + mpf_div(q2[iout * q_dim + j].dval, q2[iout * q_dim + j].dval, + pivot); + } + } +/* L440: */ + for (j = js; j < n1; ++j) + { + if (j != in) + { + /*z = -q2[ iout * q_dim + j ].dval; */ + mpf_neg(z, q2[iout * q_dim + j].dval); + for (i = 0; i < klm1; ++i) + { + if (i != iout) + { + /*q2[ i * q_dim + j ].dval += z * q2[ i * q_dim + in ].dval; */ + mpf_mul(dummy, z, q2[i * q_dim + in].dval); + mpf_add(q2[i * q_dim + j].dval, q2[i * q_dim + j].dval, + dummy); + + if (censor == 1) + { + if (mpf_cmp(q2[i * q_dim + j].dval, zero) != 0) + { + mpf_abs(dummy1, q2[i * q_dim + j].dval); + if (mpf_cmp(dummy1, censor_tol) <= 0) + { + mpf_set_si(q2[i * q_dim + j].dval, 0); + } + } + } + } + } +/* L450: */ + } + } +/* L460: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L460\n")); +#endif + /*tpivot = -pivot; */ + mpf_neg(tpivot, pivot); + for (i = 0; i < klm1; ++i) + { + if (i != iout) + { + /*q2[ i * q_dim + in ].dval /= tpivot; */ + mpf_div(q2[i * q_dim + in].dval, q2[i * q_dim + in].dval, tpivot); + } + } +/* L470: */ + /*q2[ iout * q_dim + in ].dval = 1. / pivot; */ + mpf_set_si(dummy, 1); + mpf_div(q2[iout * q_dim + in].dval, dummy, pivot); + ii = q2[iout * q_dim + n1].ival; + q2[iout * q_dim + n1].ival = q2[klm1 * q_dim + in].ival; + q2[klm1 * q_dim + in].ival = ii; + ii = abs(ii); + if (iu[ii - 1] == 0 || iu[cu_dim + ii - 1] == 0) + { + goto L240; + } +/* switch column */ + for (i = 0; i < klm1; ++i) + { + /*z = q2[ i * q_dim + in ].dval; */ + mpf_set(z, q2[i * q_dim + in].dval); + /*q2[ i * q_dim + in ].dval = q2[ i * q_dim + js ].dval; */ + mpf_set(q2[i * q_dim + in].dval, q2[i * q_dim + js].dval); + /*q2[ i * q_dim + js ].dval = z; */ + mpf_set(q2[i * q_dim + js].dval, z); + } + i = q2[klm1 * q_dim + in].ival; + q2[klm1 * q_dim + in].ival = q2[klm1 * q_dim + js].ival; + q2[klm1 * q_dim + js].ival = i; +/* L480: */ + ++js; + goto L240; +/* TEST FOR OPTIMALITY. */ + L490: +#ifdef DEBUG_CL1 + output_msg(sformatf( "L490\n")); +#endif + if (kforce == 0) + { + if (iphase == 1) + { + /*if (q2[ klm * q_dim + n ].dval <= toler) { */ + if (mpf_cmp(q2[klm * q_dim + n].dval, toler) <= 0) + { + goto L500; + } +#ifdef DEBUG_CL1 + output_msg(sformatf( "q2[klm1-1, n1-1] > *toler. %e\n", + mpf_get_d(q2[(klm1 - 1) * q_dim + n1 - 1].dval))); +#endif + *kode = 1; + goto L590; + } + *kode = 0; + goto L590; + } + /*if (iphase != 1 || q2[ klm * q_dim + n ].dval > toler) { */ + if ((iphase != 1) || (mpf_cmp(q2[klm * q_dim + n].dval, toler) > 0)) + { + kforce = 0; + goto L240; + } +/* SET UP PHASE 2 COSTS. */ + L500: +#ifdef DEBUG_CL1 + output_msg(sformatf( "Set up phase 2 costs %d\n", *iter)); +#endif + iphase = 2; + for (j = 0; j < nklm; ++j) + { + /*cu[ j ] = 0.; */ + mpf_set_si(cu[j], 0); + } +/* L510: */ + for (j = n; j < nk; ++j) + { + /*cu[ j ] = 1.; */ + mpf_set_si(cu[j], 1); + } + /* + memcpy( (void *) &(cu[cu_dim]), (void *) &(cu[0]), (size_t) nklm * sizeof(LDBLE) ); + */ + for (i = 0; i < nklm; ++i) + { + mpf_set(cu[cu_dim + i], cu[i]); + } + +/* L520: */ + for (i = 0; i < klm; ++i) + { + ii = q2[i * q_dim + n1].ival; + if (ii <= 0) + { + if (iu[cu_dim - ii - 1] == 0) + { + continue; + } + /*cu[ cu_dim - ii - 1 ] = 0.; */ + mpf_set_si(cu[cu_dim - ii - 1], 0); + } + else + { +/* L530: */ + if (iu[ii - 1] == 0) + { + continue; + } + /*cu[ ii - 1 ] = 0.; */ + mpf_set_si(cu[ii - 1], 0); + } +/* L540: */ + ++ia; +/* switch row */ + /* + memcpy( (void *) &(scratch[0]), (void *) &(q2[ ia * q_dim]), + (size_t) n2 * sizeof(LDBLE) ); + memcpy( (void *) &(q2[ ia * q_dim ]), (void *) &(q2[ i * q_dim]), + (size_t) n2 * sizeof(LDBLE) ); + memcpy( (void *) &(q2[ i * q_dim ]), (void *) &(scratch[ 0 ]), + (size_t) n2 * sizeof(LDBLE) ); + */ + for (iswitch = 0; iswitch < n1; ++iswitch) + { + mpf_set(dummy, q2[ia * q_dim + iswitch].dval); + mpf_set(q2[ia * q_dim + iswitch].dval, + q2[i * q_dim + iswitch].dval); + mpf_set(q2[i * q_dim + iswitch].dval, dummy); + } + iswitch = q2[ia * q_dim + n1].ival; + q2[ia * q_dim + n1].ival = q2[i * q_dim + n1].ival; + q2[i * q_dim + n1].ival = iswitch; +/* L550: */ + } +/* L560: */ + goto L160; + + +/* PREPARE OUTPUT. */ + L590: +#ifdef DEBUG_CL1 + output_msg(sformatf( "L590\n")); +#endif + /*sum = 0.; */ + mpf_set_si(sum, 0); + for (j = 0; j < n; ++j) + { + /*x[j] = 0.; */ + mpf_set_si(x[j], 0); + } +/* L600: */ + for (i = 0; i < klm; ++i) + { + /*res[i] = 0.; */ + mpf_set_si(res[i], 0); + } +/* L610: */ + for (i = 0; i < klm; ++i) + { + ii = q2[i * q_dim + n1].ival; + /*sn = 1.; */ + mpf_set_si(sn, 1); + if (ii < 0) + { + ii = -ii; + /*sn = -1.; */ + mpf_set_si(sn, -1); + } + if (ii <= n) + { +/* L620: */ + /*x[ii - 1] = sn * q2[ i * q_dim + n ].dval; */ + mpf_mul(x[ii - 1], sn, q2[i * q_dim + n].dval); + } + else + { +/* L630: */ + /*res[ii - n - 1] = sn * q2[ i * q_dim + n ].dval; */ + mpf_mul(res[ii - n - 1], sn, q2[i * q_dim + n].dval); + if (ii >= n1 && ii <= nk) + { +/* * DBLE(Q(I,N1)) */ + /*sum += q2[ i * q_dim + n ].dval; */ + mpf_add(sum, sum, q2[i * q_dim + n].dval); + } + } + } +/* L640: */ +#ifdef DEBUG_CL1 + output_msg(sformatf( "L640\n")); +#endif + /* + * Check calculation + */ + mpf_set_si(dummy, 100); + mpf_mul(check_toler, toler, dummy); + if (check && *kode == 0) + { + /* + * Check optimization constraints + */ + if (*kode_arg == 1) + { + for (i = 0; i < k; ++i) + { + if (res_arg[i] < 0.0) + { + mpf_sub(dummy, res[i], check_toler); + mpf_set_si(dummy1, 0); + if (mpf_cmp(dummy, dummy1) > 0) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1MP: optimization constraint not satisfied row %d, res %e, constraint %f.\n", + i, mpf_get_d(res[i]), res_arg[i])); +#endif + *kode = 1; + } + } + else if (res_arg[i] > 0.0) + { + mpf_add(dummy, res[i], check_toler); + mpf_set_si(dummy1, 0); + if (mpf_cmp(dummy, dummy1) < 0) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1MP: optimization constraint not satisfied row %d, res %e, constraint %f.\n", + i, mpf_get_d(res[i]), res_arg[i])); +#endif + *kode = 1; + } + } + } + } + /* + * Check equalities + */ + for (i = k; i < k + l; ++i) + { + mpf_abs(dummy, res[i]); + if (mpf_cmp(dummy, check_toler) > 0) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1MP: equality constraint not satisfied row %d, res %e, tolerance %e.\n", + i, mpf_get_d(res[i]), mpf_get_d(check_toler))); +#endif + + *kode = 1; + } + } + /* + * Check inequalities + */ + for (i = k + l; i < k + l + m; ++i) + { + mpf_neg(dummy, check_toler); + if (mpf_cmp(res[i], dummy) < 0) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1MP: inequality constraint not satisfied row %d, res %e, tolerance %e.\n", + i, mpf_get_d(res[i]), mpf_get_d(check_toler))); +#endif + *kode = 1; + } + } + /* + * Check dissolution/precipitation constraints + */ + if (*kode_arg == 1) + { + for (i = 0; i < n; ++i) + { + if (x_arg[i] < 0.0) + { + mpf_sub(dummy, x[i], check_toler); + mpf_set_si(dummy1, 0); + if (mpf_cmp(dummy, dummy1) > 0) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1MP: dis/pre constraint not satisfied column %d, x %e, constraint %f.\n", + i, mpf_get_d(x[i]), x_arg[i])); +#endif + *kode = 1; + } + } + else if (x_arg[i] > 0.0) + { + mpf_add(dummy, x[i], check_toler); + mpf_set_si(dummy1, 0); + if (mpf_cmp(dummy, dummy1) < 0) + { +#ifdef CHECK_ERRORS + output_msg(sformatf( + "\tCL1MP: dis/pre constraint not satisfied column %d, x %e, constraint %f.\n", + i, mpf_get_d(x[i]), x_arg[i])); +#endif + *kode = 1; + } + } + } + } + if (*kode == 1) + { + output_msg(sformatf( + "\n\tCL1MP: Roundoff errors in optimization.\n\t Deleting model.\n")); + } + } + /* + * set return variables + */ + /**error = sum;*/ + mpf_set(error, sum); + *error_arg = mpf_get_d(error); + *kode_arg = *kode; + for (i = 0; i < n2d; ++i) + { + x_arg[i] = mpf_get_d(x[i]); + } + for (i = 0; i < k + l + m; ++i) + { + res_arg[i] = mpf_get_d(res[i]); + } + + /*scratch = free_check_null (scratch); */ + + for (i = 0; i < max_row_count * max_column_count; ++i) + { + mpf_clear(q[i]); + } + q = (mpf_t *) free_check_null(q); + for (i = 0; i < n2d; ++i) + { + mpf_clear(x[i]); + } + x = (mpf_t *) free_check_null(x); + for (i = 0; i < k + l + m; ++i) + { + mpf_clear(res[i]); + } + res = (mpf_t *) free_check_null(res); + for (i = 0; i < 2 * nklmd; ++i) + { + mpf_clear(cu[i]); + } + cu = (mpf_t *) free_check_null(cu); + mpf_clear(zero); + mpf_clear(dummy); + mpf_clear(dummy1); + mpf_clear(sum); + mpf_clear(error); + mpf_clear(z); + mpf_clear(zu); + mpf_clear(zv); + mpf_clear(xmax); + mpf_clear(minus_one); + mpf_clear(toler); + mpf_clear(check_toler); + mpf_clear(pivot); + mpf_clear(xmin); + mpf_clear(cuv); + mpf_clear(tpivot); + mpf_clear(sn); + mpf_clear(censor_tol); + kode = (int *) free_check_null(kode); + return 0; +} +#endif // INVERSE_CL1MP diff --git a/phreeqcpp/class_main.cpp b/phreeqcpp/class_main.cpp new file mode 100644 index 00000000..88e19156 --- /dev/null +++ b/phreeqcpp/class_main.cpp @@ -0,0 +1,949 @@ +#include "Phreeqc.h" + +#include "NameDouble.h" +#include "Solution.h" +#include "Reaction.h" +#include "PPassemblage.h" +#include "Exchange.h" +#include "Surface.h" +#include "GasPhase.h" +#include "SSassemblage.h" +#include "cxxKinetics.h" +//#include +//#include +/* ---------------------------------------------------------------------- + * MAIN + * ---------------------------------------------------------------------- */ +int +main(int argc, char *argv[]) +/* + * Main program for PHREEQC + */ +{ + + // check for floating point exceptions on Linux + // feenableexcept(FE_DIVBYZERO|FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW); +#if defined(WIN32_MEMORY_DEBUG) + int tmpDbgFlag; + + /* + * Set the debug-heap flag to keep freed blocks in the + * heap's linked list - This will allow us to catch any + * inadvertent use of freed memory + */ +#ifdef SKIP + // Send messages (leaks) to stderr + _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR ); + _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR ); + _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE ); + _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR ); +#endif + tmpDbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + //tmpDbgFlag |= _CRTDBG_DELAY_FREE_MEM_DF; + tmpDbgFlag |= _CRTDBG_LEAK_CHECK_DF; + ///tmpDbgFlag |= _CRTDBG_CHECK_ALWAYS_DF; + _CrtSetDbgFlag(tmpDbgFlag); + //_crtBreakAlloc = 31195; +#endif +#ifdef SKIP +//Set the x86 floating-point control word according to what +//exceptions you want to trap. +_clearfp(); //Always call _clearfp before setting the control + //word +//Because the second parameter in the following call is 0, it +//only returns the floating-point control word +unsigned int cw = _controlfp(0, 0); //Get the default control + //word +//Set the exception masks off for exceptions that you want to +//trap. When a mask bit is set, the corresponding floating-point +//exception is //blocked from being generating. +cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE| + EM_DENORMAL|EM_INVALID); +//For any bit in the second parameter (mask) that is 1, the +//corresponding bit in the first parameter is used to update +//the control word. +unsigned int cwOriginal = _controlfp(cw, MCW_EM); //Set it. + //MCW_EM is defined in float.h. + //Restore the original value when done: + //_controlfp(cwOriginal, MCW_EM); +#endif + Phreeqc phreeqc_instance; + return phreeqc_instance.main_method(argc, argv); +} +//#define TEST_COPY +#ifdef TEST_COPY +int Phreeqc:: +main_method(int argc, char *argv[]) +/* + * Main program for PHREEQC + */ +{ + + int errors; + std::istream *db_cookie = NULL; + std::istream *input_cookie = NULL; +#if defined(WIN32_MEMORY_DEBUG) + int tmpDbgFlag; + + /* + * Set the debug-heap flag to keep freed blocks in the + * heap's linked list - This will allow us to catch any + * inadvertent use of freed memory + */ + tmpDbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + //tmpDbgFlag |= _CRTDBG_DELAY_FREE_MEM_DF; + tmpDbgFlag |= _CRTDBG_LEAK_CHECK_DF; + ///tmpDbgFlag |= _CRTDBG_CHECK_ALWAYS_DF; + _CrtSetDbgFlag(tmpDbgFlag); + //_crtBreakAlloc = 9482; +#endif + + phast = FALSE; +/* + * Open input/output files + */ + errors = process_file_names(argc, argv, &db_cookie, &input_cookie, TRUE); + if (errors != 0) + { + return errors; + } +#ifdef DOS + write_banner(); +#endif + +/* + * Initialize arrays + */ + errors = do_initialize(); + if (errors != 0) + { + return errors; + } +/* + * Load database into memory + */ + this->phrq_io->push_istream(db_cookie); + errors = read_database(); + this->phrq_io->clear_istream(); + + if (errors != 0) + { + return errors; + } + Phreeqc MyCopy; + MyCopy = *this; + this->clean_up(); + this->init(); + this->initialize(); +/* + * Read input data for simulation + */ + + MyCopy.phrq_io->push_istream(input_cookie); + errors = MyCopy.run_simulations(); + + //Phreeqc mycopy(*this); + MyCopy.phrq_io->clear_istream(); + + if (errors != 0) + { + return errors; + } +/* + * Display successful status + */ + pr.headings = TRUE; + errors = do_status(); + if (errors != 0) + { + return errors; + } + return 0; +} +#else +int Phreeqc:: +main_method(int argc, char *argv[]) +/* + * Main program for PHREEQC + */ +{ + + int errors; + std::istream *db_cookie = NULL; + std::istream *input_cookie = NULL; +#if defined(WIN32_MEMORY_DEBUG) + int tmpDbgFlag; + + /* + * Set the debug-heap flag to keep freed blocks in the + * heap's linked list - This will allow us to catch any + * inadvertent use of freed memory + */ + tmpDbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + //tmpDbgFlag |= _CRTDBG_DELAY_FREE_MEM_DF; + tmpDbgFlag |= _CRTDBG_LEAK_CHECK_DF; + ///tmpDbgFlag |= _CRTDBG_CHECK_ALWAYS_DF; + _CrtSetDbgFlag(tmpDbgFlag); + //_crtBreakAlloc = 9482; +#endif + try + { + phast = FALSE; + /* + * Open input/output files + */ + errors = process_file_names(argc, argv, &db_cookie, &input_cookie, TRUE); + if (errors != 0) + { + return errors; + } +#ifdef DOS + write_banner(); +#endif + + /* + * Initialize arrays + */ + errors = do_initialize(); + if (errors != 0) + { + return errors; + } + /* + * Load database into memory + */ + this->phrq_io->push_istream(db_cookie); + errors = read_database(); + this->phrq_io->clear_istream(); + + if (errors != 0) + { + return errors; + } + + /* + * Read input data for simulation + */ + + this->phrq_io->push_istream(input_cookie); + errors = run_simulations(); + + //Phreeqc mycopy(*this); + this->phrq_io->clear_istream(); + + if (errors != 0) + { + return errors; + } + /* + * Display successful status + */ + pr.headings = TRUE; + errors = do_status(); + if (errors != 0) + { + return errors; + } + } + catch (...) + { + int e = get_input_errors(); + std::cerr << "Unhandled exception in PHREEQC." << std::endl; + if (e > 0) + { + return e; + } + else + { + return 1; + } + } + return 0; +} +#endif //TEST_COPY +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +write_banner(void) +/* ---------------------------------------------------------------------- */ +{ + char buffer[80]; + int len, indent; + screen_msg( + " ÛßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßÛ\n"); + screen_msg( + " º º\n"); + + /* version */ +#ifdef NPP + //len = sprintf(buffer, "* PHREEQC-%s *", "3.4.2 AmpŠre"); + len = sprintf(buffer, "* PHREEQC-%s *", "3.4.2"); +#else + len = sprintf(buffer, "* PHREEQC-%s *", "@VERSION@"); +#endif + indent = (44 - len) / 2; + screen_msg(sformatf("%14cº%*c%s%*cº\n", ' ', indent, ' ', buffer, + 44 - indent - len, ' ')); + + screen_msg( + " º º\n"); + screen_msg( + " º A hydrogeochemical transport model º\n"); + screen_msg( + " º º\n"); + screen_msg( + " º by º\n"); + screen_msg( + " º D.L. Parkhurst and C.A.J. Appelo º\n"); + screen_msg( + " º º\n"); + + + /* date */ +#ifdef NPP + len = sprintf(buffer, "%s", "February 27, 2018"); +#else + len = sprintf(buffer, "%s", "@VER_DATE@"); +#endif + indent = (44 - len) / 2; + screen_msg(sformatf("%14cº%*c%s%*cº\n", ' ', indent, ' ', buffer, + 44 - indent - len, ' ')); + + screen_msg( + " ÛÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÛ\n\n"); + + return 0; +} +#ifdef ERROR_OSTREAM +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +process_file_names(int argc, char *argv[], std::istream **db_cookie, + std::istream **input_cookie, int log) +/* ---------------------------------------------------------------------- */ +{ + int l; + char token[2 * MAX_LENGTH], default_name[2 * MAX_LENGTH]; + char query[2 * MAX_LENGTH]; + char in_file[2 * MAX_LENGTH], out_file[2 * MAX_LENGTH], db_file[2 * MAX_LENGTH]; + char *env_ptr; + char *ptr; +/* + * Prepare error handling + */ + try { + if (phrq_io == NULL) + { + std::cerr << "No PHRQ_io output handler defined in process_file_names" << "\n"; + } +/* + * Prep for get_line + */ + max_line = MAX_LINE; + space((void **) ((void *) &line), INIT, &max_line, sizeof(char)); + space((void **) ((void *) &line_save), INIT, &max_line, sizeof(char)); +/* + * Open error ostream + */ + if (argc > 4) + { + if (!phrq_io->error_open(argv[4])) + { + error_string = sformatf( "Error opening file, %s.", argv[4]); + warning_msg(error_string); + } + } + else + { + phrq_io->error_open(NULL); + } +/* + * Open user-input file + */ + strcpy(query, "Name of input file?"); + std::ifstream * local_input_stream = NULL; + if (argc <= 1) + { + default_name[0] = '\0'; + local_input_stream = open_input_stream(query, default_name, std::ios_base::in, false); + } + else + { + strcpy(default_name, argv[1]); + local_input_stream = open_input_stream(query, default_name, std::ios_base::in, true); + } + screen_msg(sformatf("Input file: %s\n\n", default_name)); + strcpy(in_file, default_name); +/* + * Open file for output + */ + strcpy(query, "Name of output file?"); + ptr = default_name; + copy_token(token, &ptr, &l); + strcpy(token, default_name); + strcat(token, ".out"); + std::ofstream * local_output_stream = NULL; + if (argc <= 1) + { + local_output_stream = open_output_stream(query, token, std::ios_base::out, false); + } + else if (argc == 2) + { + local_output_stream = open_output_stream(query, token, std::ios_base::out, true); + } + else if (argc >= 3) + { + strcpy(token, argv[2]); + local_output_stream = open_output_stream(query, token, std::ios_base::out, true); + } + screen_msg(sformatf("Output file: %s\n\n", token)); + strcpy(out_file, token); + phrq_io->Set_output_ostream(local_output_stream); +/* + * Open log file + */ + if (log == TRUE) + { + if (!phrq_io->log_open("phreeqc.log")) + { + error_msg("Cannot open log file, phreeqc.log.", STOP); + } + } +/* + * Read input file for DATABASE keyword + */ + if (local_input_stream->is_open()) + { + phrq_io->push_istream(local_input_stream); + if (get_line() == KEYWORD) + { + ptr = line; + copy_token(token, &ptr, &l); + if (strcmp_nocase(token, "database") == 0) + { + user_database = (char *) free_check_null(user_database); +#ifdef PHREEQ98 + user_database = string_duplicate(prefix_database_dir(ptr)); +#else + user_database = string_duplicate(ptr); +#endif + if (string_trim(user_database) == EMPTY) + { + warning_msg("DATABASE file name is missing; default database will be used."); + user_database = (char *) free_check_null(user_database); + } + } + } + phrq_io->pop_istream(); + } + else + { + delete local_input_stream; + error_string = sformatf( "Error opening file, %s.", in_file); + error_msg(error_string, STOP); + } + +/* + * Open data base + */ + strcpy(query, "Name of database file?"); + env_ptr = getenv("PHREEQC_DATABASE"); + if (user_database != NULL) + { + strcpy(token, user_database); + } + else if (env_ptr != NULL) + { + strcpy(token, env_ptr); + } + else + { + strcpy(token, default_data_base); + } + + std::ifstream * local_database_file = NULL; + if (argc <= 1) + { + local_database_file = open_input_stream(query, token, std::ios_base::in, false); + } + else if (argc < 4) + { + local_database_file = open_input_stream(query, token, std::ios_base::in, true); + } + else if (argc >= 4) + { + if (user_database == NULL) + { + strcpy(token, argv[3]); + } + else + { +#ifndef PHREEQCI_GUI + warning_msg ("Database file from DATABASE keyword is used; command line argument ignored."); +#endif + } + local_database_file = open_input_stream(query, token, std::ios_base::in, true); + } + local_database_file->close(); + delete local_database_file; + + user_database = (char *) free_check_null(user_database); + user_database = string_duplicate(token); + screen_msg(sformatf("Database file: %s\n\n", token)); + strcpy(db_file, token); +#ifdef NPP + //output_msg(sformatf("Using PHREEQC: version 3.4.2 Ampère, compiled February 27, 2018\n")); +#endif + output_msg(sformatf(" Input file: %s\n", in_file)); + output_msg(sformatf(" Output file: %s\n", out_file)); +#ifdef NPP + output_msg(sformatf("Using PHREEQC: version 3.4.2, compiled February 27, 2018\n")); +#endif + output_msg(sformatf("Database file: %s\n\n", token)); +#ifdef NPP + output_flush(); +#endif + /* + * local cleanup + */ + user_database = (char *) free_check_null(user_database); + line = (char *) free_check_null(line); + line_save = (char *) free_check_null(line_save); + + *db_cookie = new std::ifstream(db_file, std::ios_base::in); + *input_cookie = new std::ifstream(in_file, std::ios_base::in); + } + catch (const PhreeqcStop&) + { + return get_input_errors(); + } + return 0; +} +#else +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +process_file_names(int argc, char *argv[], std::istream **db_cookie, + std::istream **input_cookie, int log) +/* ---------------------------------------------------------------------- */ +{ + int l; + char token[2 * MAX_LENGTH], default_name[2 * MAX_LENGTH]; + char query[2 * MAX_LENGTH]; + char in_file[2 * MAX_LENGTH], out_file[2 * MAX_LENGTH], db_file[2 * MAX_LENGTH]; + char *env_ptr; + char *ptr; +/* + * Prepare error handling + */ + try { + if (phrq_io == NULL) + { + std::cerr << "No PHRQ_io output handler defined in process_file_names" << "\n"; + } +/* + * Prep for get_line + */ + max_line = MAX_LINE; + space((void **) ((void *) &line), INIT, &max_line, sizeof(char)); + space((void **) ((void *) &line_save), INIT, &max_line, sizeof(char)); +/* + * Open error ostream + */ + if (argc > 4) + { + if (!phrq_io->error_open(argv[4])) + { + error_string = sformatf( "Error opening file, %s.", argv[4]); + warning_msg(error_string); + } + } + else + { + phrq_io->error_open(NULL); + } +/* + * Open user-input file + */ + strcpy(query, "Name of input file?"); + std::ifstream * local_input_stream = NULL; + if (argc <= 1) + { + default_name[0] = '\0'; + local_input_stream = open_input_stream(query, default_name, std::ios_base::in, false); + } + else + { + strcpy(default_name, argv[1]); + local_input_stream = open_input_stream(query, default_name, std::ios_base::in, true); + } + screen_msg(sformatf("Input file: %s\n\n", default_name)); + strcpy(in_file, default_name); +/* + * Open file for output + */ + strcpy(query, "Name of output file?"); + ptr = default_name; + copy_token(token, &ptr, &l); + strcat(token, ".out"); + std::ofstream * local_output_stream; + if (argc <= 1) + { + local_output_stream = open_output_stream(query, token, std::ios_base::out, false); + } + else if (argc == 2) + { + local_output_stream = open_output_stream(query, token, std::ios_base::out, true); + } + else if (argc >= 3) + { + strcpy(token, argv[2]); + local_output_stream = open_output_stream(query, token, std::ios_base::out, true); + } + screen_msg(sformatf("Output file: %s\n\n", token)); + strcpy(out_file, token); + phrq_io->Set_output_ostream(local_output_stream); +/* + * Open log file + */ + if (log == TRUE) + { + if (!phrq_io->log_open("phreeqc.log")) + { + error_msg("Cannot open log file, phreeqc.log.", STOP); + } + } +/* + * Read input file for DATABASE keyword + */ + if (local_input_stream->is_open()) + { + phrq_io->push_istream(local_input_stream); + if (get_line() == KEYWORD) + { + ptr = line; + copy_token(token, &ptr, &l); + if (strcmp_nocase(token, "database") == 0) + { + user_database = (char *) free_check_null(user_database); +#ifdef PHREEQ98 + user_database = string_duplicate(prefix_database_dir(ptr)); +#else + user_database = string_duplicate(ptr); +#endif + if (string_trim(user_database) == EMPTY) + { + warning_msg("DATABASE file name is missing; default database will be used."); + user_database = (char *) free_check_null(user_database); + } + } + } + phrq_io->pop_istream(); + } + else + { + delete local_input_stream; + error_string = sformatf( "Error opening file, %s.", in_file); + error_msg(error_string, STOP); + } + +/* + * Open data base + */ + strcpy(query, "Name of database file?"); + env_ptr = getenv("PHREEQC_DATABASE"); + if (user_database != NULL) + { + strcpy(token, user_database); + } + else if (env_ptr != NULL) + { + strcpy(token, env_ptr); + } + else + { + strcpy(token, default_data_base); + } + + std::ifstream * local_database_file; + if (argc <= 1) + { + local_database_file = open_input_stream(query, token, std::ios_base::in, false); + } + else if (argc < 4) + { + local_database_file = open_input_stream(query, token, std::ios_base::in, true); + } + else if (argc >= 4) + { + if (user_database == NULL) + { + strcpy(token, argv[3]); + } + else + { +#ifndef PHREEQCI_GUI + warning_msg ("Database file from DATABASE keyword is used; command line argument ignored."); +#endif + } + local_database_file = open_input_stream(query, token, std::ios_base::in, true); + } + local_database_file->close(); + delete local_database_file; + screen_msg(sformatf("Database file: %s\n\n", token)); + strcpy(db_file, token); + + output_msg(sformatf(" Input file: %s\n", in_file)); + output_msg(sformatf(" Output file: %s\n", out_file)); + output_msg(sformatf("Database file: %s\n\n", token)); + /* + * local cleanup + */ + user_database = (char *) free_check_null(user_database); + user_database = string_duplicate(token); + line = (char *) free_check_null(line); + line_save = (char *) free_check_null(line_save); + + *db_cookie = new std::ifstream(db_file, std::ios_base::in); + *input_cookie = new std::ifstream(in_file, std::ios_base::in); + } + catch (const PhreeqcStop& e) + { + return get_input_errors(); + } + return 0; +} +#endif +/* ---------------------------------------------------------------------- */ +std::ifstream * Phreeqc:: +open_input_stream(char *query, char *default_name, std::ios_base::openmode mode, bool batch) +/* ---------------------------------------------------------------------- */ +{ + char name[MAX_LENGTH]; + std::ifstream *new_stream; + int l; +#ifdef ERROR_OSTREAM + std::ostream * error_ostream_save = phrq_io->Get_error_ostream(); +#else + FILE * error_file_save = phrq_io->Get_error_file(); +#endif + + for (;;) + { +/* + * Get file name + */ + strcpy(name, default_name); + if (!batch ) + { +#ifdef ERROR_OSTREAM + phrq_io->Set_error_ostream(&std::cerr); +#else + phrq_io->Set_error_file(stderr); +#endif + screen_msg(sformatf("%s\n", query)); + if (default_name[0] != '\0') + { + screen_msg(sformatf("Default: %s\n", default_name)); + } + char *s_ptr = fgets(name, MAX_LENGTH, stdin); + if (s_ptr == NULL) + { + std::cerr << "Failed defining name." << std::endl; + } + + l = (int) strlen(name); + name[l - 1] = '\0'; + if (name[0] == '\0') + { + strcpy(name, default_name); + } + } +/* + * Open existing file to read + */ + new_stream = new std::ifstream(name, mode); + if (new_stream == NULL || !new_stream->is_open()) + { +#ifdef ERROR_OSTREAM + phrq_io->Set_error_ostream(&std::cerr); +#else + phrq_io->Set_error_file(stderr); +#endif + error_string = sformatf( "\nERROR: Cannot open file, %s.\n", name); + screen_msg(error_string); +#ifdef NPP + error_msg(sformatf( "\nERROR: Cannot open file, %s.\n Please check, and give the correct, full path + name.\n", name), STOP); + break; +#endif + error_flush(); + batch = FALSE; + continue; + } + break; + } + strncpy(default_name, name, MAX_LENGTH); + if (!batch ) + { + //phrq_io->Set_error_ostream(error_file_save); +#ifdef ERROR_OSTREAM + phrq_io->Set_error_ostream(error_ostream_save); +#else + phrq_io->Set_error_file(error_file_save); +#endif + } + return (new_stream); +} +/* ---------------------------------------------------------------------- */ +std::ofstream * Phreeqc:: +open_output_stream(char *query, char *default_name, std::ios_base::openmode mode, bool batch) +/* ---------------------------------------------------------------------- */ +{ + char name[MAX_LENGTH]; + std::ofstream *new_stream; + int l; +#ifdef ERROR_OSTREAM + std::ostream * error_ostream_save = phrq_io->Get_error_ostream(); +#else + FILE * error_file_save = phrq_io->Get_error_file(); +#endif + + for (;;) + { +/* + * Get file name + */ + strcpy(name, default_name); + if (!batch ) + { +#ifdef ERROR_OSTREAM + phrq_io->Set_error_ostream(&std::cerr); +#else + phrq_io->Set_error_file(stderr); +#endif + + screen_msg(sformatf("%s\n", query)); + if (default_name[0] != '\0') + { + screen_msg(sformatf("Default: %s\n", default_name)); + } + char *s_ptr = fgets(name, MAX_LENGTH, stdin); + if (s_ptr == NULL) + { + std::cerr << "Failed defining name." << std::endl; + } + + l = (int) strlen(name); + name[l - 1] = '\0'; + if (name[0] == '\0') + { + strcpy(name, default_name); + } + } +/* + * Open existing file to read + */ + new_stream = new std::ofstream(name, mode); + if (new_stream == NULL || !new_stream->is_open()) + { +#ifdef ERROR_OSTREAM + phrq_io->Set_error_ostream(&std::cerr); +#else + phrq_io->Set_error_file(stderr); +#endif + error_string = sformatf( "\nERROR: Cannot open file, %s.\n", name); + screen_msg(error_string); + error_flush(); + batch = FALSE; + continue; + } + break; + } + strncpy(default_name, name, MAX_LENGTH); + if (!batch ) + { +#ifdef ERROR_OSTREAM + phrq_io->Set_error_ostream(error_ostream_save); +#else + phrq_io->Set_error_file(error_file_save); +#endif + } + return (new_stream); +} +#ifdef SKIP +/* ---------------------------------------------------------------------- */ +std::ofstream * Phreeqc:: +open_output_file(char *query, char *default_name, std::ios_base::openmode mode, bool batch) +/* ---------------------------------------------------------------------- */ +{ + char name[MAX_LENGTH]; + std::ofstream *new_stream; + int l; +#ifdef ERROR_OSTREAM + std::ostream * error_ostream_save = phrq_io->Get_error_ostream(); +#else + FILE * error_file_save = phrq_io->Get_error_file(); +#endif + + + for (;;) + { +/* + * Get file name + */ + strcpy(name, default_name); + if (!batch ) + { +#ifdef ERROR_OSTREAM + phrq_io->Set_error_ostream(&std::cerr); +#else + phrq_io->Set_error_file(stderr); +#endif + screen_msg(sformatf("%s\n", query)); + if (default_name[0] != '\0') + { + screen_msg(sformatf("Default: %s\n", default_name)); + } + char *s_ptr = fgets(name, MAX_LENGTH, stdin); + if (s_ptr == NULL) + { + std::cerr << "Failed defining name." << std::endl; + } + + l = (int) strlen(name); + name[l - 1] = '\0'; + if (name[0] == '\0') + { + strcpy(name, default_name); + } + } +/* + * Open existing file to read + */ + new_stream = new std::ofstream(name, mode); + if (new_stream == NULL || !new_stream->is_open()) + { +#ifdef ERROR_OSTREAM + phrq_io->Set_error_ostream(&std::cerr); +#else + phrq_io->Set_error_file(stderr); +#endif + error_string = sformatf( "\nERROR: Cannot open file, %s.\n", name); + screen_msg(error_string); + error_flush(); + batch = FALSE; + continue; + } + break; + } + strncpy(default_name, name, MAX_LENGTH); + if (!batch ) + { +#ifdef ERROR_OSTREAM + phrq_io->Set_error_ostream(error_ostream_save); +#else + phrq_io->Set_error_file(error_file_save); +#endif + } + return (new_stream); +} +#endif diff --git a/phreeqcpp/common/PHRQ_base.cxx b/phreeqcpp/common/PHRQ_base.cxx new file mode 100644 index 00000000..0e07d1f0 --- /dev/null +++ b/phreeqcpp/common/PHRQ_base.cxx @@ -0,0 +1,108 @@ +#include "PHRQ_base.h" +#include +#include "PHRQ_io.h" +PHRQ_base:: +PHRQ_base(void) +{ + this->io = NULL; + base_error_count = 0; +} + +PHRQ_base:: +PHRQ_base(PHRQ_io * p_io) +{ + this->io = p_io; + base_error_count = 0; +} + +PHRQ_base:: +~PHRQ_base() +{ +} + +void PHRQ_base:: +error_msg(const std::string & stdstr, int stop) +{ + this->base_error_count++; + std::ostringstream msg; + msg << "ERROR: " << stdstr << "\n"; + if (this->io) + { + this->io->output_msg(msg.str().c_str()); + this->io->log_msg(msg.str().c_str()); + this->io->error_msg("\n"); + this->io->error_msg(msg.str().c_str(), stop!=0); + } + else + { +#if !defined(R_SO) + std::cerr << msg.str().c_str(); + std::cout << msg.str().c_str(); +#endif + } + if (stop != 0) + { + throw PhreeqcStop(); + } +} + +void PHRQ_base:: +warning_msg(const std::string & stdstr) +{ + if (this->io) + { + this->io->warning_msg(stdstr.c_str()); + } + else + { +#if !defined(R_SO) + std::cerr << stdstr << "\n"; + std::cout << stdstr << "\n"; +#endif + } +} + +void PHRQ_base:: +output_msg(const std::string & stdstr) +{ + if (this->io) + { + this->io->output_msg(stdstr.c_str()); + } + else + { +#if !defined(R_SO) + std::cout << stdstr << "\n"; +#endif + } +} + +void PHRQ_base:: +screen_msg(const std::string & stdstr) +{ + if (this->io) + { + this->io->screen_msg(stdstr.c_str()); + } + else + { +#if !defined(R_SO) + std::cerr << stdstr << "\n"; +#endif + } +} + +void PHRQ_base:: +echo_msg(const std::string & stdstr) +{ + if (this->io) + { + this->io->echo_msg(stdstr.c_str()); + } + else + { +#if !defined(R_SO) + std::cout << stdstr << "\n"; +#endif + } +} \ No newline at end of file diff --git a/phreeqcpp/common/PHRQ_base.h b/phreeqcpp/common/PHRQ_base.h new file mode 100644 index 00000000..037d0881 --- /dev/null +++ b/phreeqcpp/common/PHRQ_base.h @@ -0,0 +1,52 @@ +#ifndef _PHRQBASE_H +#define _PHRQBASE_H + +#include + +#if defined(_WINDLL) +#define IPQ_DLL_EXPORT __declspec(dllexport) +#else +#define IPQ_DLL_EXPORT +#endif + +class PHRQ_io; +class IPQ_DLL_EXPORT PHRQ_base +{ +public: + // constructors + PHRQ_base(void); + PHRQ_base(PHRQ_io *); + virtual ~ PHRQ_base(); + + + // methods + void output_msg(const std::string &); + void error_msg(const std::string &, int stop=0); + void warning_msg(const std::string &); + void screen_msg(const std::string &); + void echo_msg(const std::string &); + + + void Set_io(PHRQ_io * p_io) + { + this->io = p_io; + } + PHRQ_io * Get_io(void) + { + return this->io; + } + void Set_base_error_count(int i) + { + this->base_error_count = i; + } + int Get_base_error_count(void) + { + return this->base_error_count; + } + // data +protected: + PHRQ_io * io; + int base_error_count; +}; + +#endif /* _PHRQBASE_H */ diff --git a/phreeqcpp/common/PHRQ_io.cpp b/phreeqcpp/common/PHRQ_io.cpp new file mode 100644 index 00000000..ab34f8c1 --- /dev/null +++ b/phreeqcpp/common/PHRQ_io.cpp @@ -0,0 +1,904 @@ +#include +#include "PHRQ_io.h" +#include "Parser.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +PHRQ_io:: +PHRQ_io(void) +{ + output_ostream = NULL; + log_ostream = NULL; + punch_ostream = NULL; +#ifdef ERROR_OSTREAM + error_ostream = NULL; +#else + error_file = NULL; +#endif + dump_ostream = NULL; + io_error_count = 0; + + output_on = true; + log_on = false; + punch_on = true; + error_on = true; + dump_on = true; + echo_on = true; //**appt + screen_on = true; + echo_destination = ECHO_OUTPUT; + + m_next_keyword = Keywords::KEY_NONE; + accumulate = false; + m_line_type = PHRQ_io::LT_EMPTY; +} + +PHRQ_io:: +~PHRQ_io() +{ +} +// ---------------------------------------------------------------------- */ +// output ostream methods +// ---------------------------------------------------------------------- */ + +bool PHRQ_io:: +ofstream_open(std::ostream **os, const char *file_name, std::ios_base::openmode mode) +{ + std::ofstream *ofs = new std::ofstream(file_name, mode); + if (ofs && ofs->is_open()) + { + *os = ofs; + return true; + } + delete ofs; + return false; +} + +/* ---------------------------------------------------------------------- */ +bool PHRQ_io:: +output_open(const char *file_name, std::ios_base::openmode mode) +/* ---------------------------------------------------------------------- */ +{ + return ofstream_open(&output_ostream, file_name, mode); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +output_flush(void) +/* ---------------------------------------------------------------------- */ +{ + if (output_ostream) + { + output_ostream->flush(); + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +output_close(void) +/* ---------------------------------------------------------------------- */ +{ + safe_close(&output_ostream); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +output_msg(const char * str) +/* ---------------------------------------------------------------------- */ +{ + if (output_ostream != NULL && output_on) + { + (*output_ostream) << str; + } + //output_flush(); +} +// ---------------------------------------------------------------------- */ +// log ostream methods +// ---------------------------------------------------------------------- */ +bool PHRQ_io:: +log_open(const char *file_name, std::ios_base::openmode mode) +/* ---------------------------------------------------------------------- */ +{ + return ofstream_open(&log_ostream, file_name, mode); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +log_flush(void) +/* ---------------------------------------------------------------------- */ +{ + if (log_ostream) + { + log_ostream->flush(); + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +log_close(void) +/* ---------------------------------------------------------------------- */ +{ + safe_close(&log_ostream); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +log_msg(const char * str) +/* ---------------------------------------------------------------------- */ +{ + if (log_ostream != NULL && log_on) + { + (*log_ostream) << str; + } +} +// ---------------------------------------------------------------------- */ +// punch ostream methods +// ---------------------------------------------------------------------- */ +bool PHRQ_io:: +punch_open(const char *file_name, std::ios_base::openmode mode, int n_user) +/* ---------------------------------------------------------------------- */ +{ + return ofstream_open(&punch_ostream, file_name, mode); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +punch_flush(void) +/* ---------------------------------------------------------------------- */ +{ + if (punch_ostream) + { + punch_ostream->flush(); + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +punch_close(void) +/* ---------------------------------------------------------------------- */ +{ + safe_close(&punch_ostream); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +punch_msg(const char * str) +/* ---------------------------------------------------------------------- */ +{ + if (punch_ostream != NULL && punch_on) + { + (*punch_ostream) << str; + } +} +// ---------------------------------------------------------------------- */ +// error file methods +// ---------------------------------------------------------------------- */ +#ifdef ERROR_OSTREAM +/* ---------------------------------------------------------------------- */ +bool PHRQ_io:: +error_open(const char *file_name, std::ios_base::openmode mode) +/* ---------------------------------------------------------------------- */ +{ + if (file_name != NULL) + { + if (!ofstream_open(&error_ostream, file_name, mode)) + { +#if !defined(R_SO) + error_ostream = &std::cerr; +#endif + return false; + } + } + else + { +#if !defined(R_SO) + error_ostream = &std::cerr; +#endif + } + return true; +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +error_flush(void) +/* ---------------------------------------------------------------------- */ +{ + if (error_ostream) + { + error_ostream->flush(); + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +error_close(void) +/* ---------------------------------------------------------------------- */ +{ + safe_close(&error_ostream); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +error_msg(const char *err_str, bool stop) +/* ---------------------------------------------------------------------- */ +{ + + io_error_count++; + if (error_ostream != NULL && error_on) + { + //(*error_ostream) << err_str; + screen_msg(err_str); + error_flush(); + } + if (stop) + { + if (error_ostream != NULL && error_on) + { + //(*error_ostream) << "Stopping.\n"; + screen_msg("Stopping.\n"); + error_ostream->flush(); + } + output_msg("Stopping.\n"); + log_msg("Stopping.\n"); + + throw PhreeqcStop(); + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +warning_msg(const char *err_str) +/* ---------------------------------------------------------------------- */ +{ + if (error_ostream != NULL && error_on) + { + //(*error_ostream) << err_str << "\n"; + std::string err_stdstr(err_str); + err_stdstr.append("\n"); + screen_msg(err_stdstr.c_str()); + error_ostream->flush(); + } + std::ostringstream warn_str; + warn_str << err_str << "\n"; + log_msg(warn_str.str().c_str()); + log_flush(); + output_msg(warn_str.str().c_str()); + output_flush(); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +screen_msg(const char * str) +/* ---------------------------------------------------------------------- */ +{ + if (error_ostream != NULL && screen_on) + { + (*error_ostream) << str; + } +} +#else +/* ---------------------------------------------------------------------- */ +bool PHRQ_io:: +error_open(const char *file_name, const char * mode) +/* ---------------------------------------------------------------------- */ +{ + if (file_name != NULL) + { + if ((error_file = fopen(file_name, mode)) == NULL) + { + error_file = stderr; + return false; + } + } + else + { + error_file = stderr; + } + return true; +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +error_flush(void) +/* ---------------------------------------------------------------------- */ +{ + if (error_file) + { + fflush(error_file); + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +error_close(void) +/* ---------------------------------------------------------------------- */ +{ + safe_close(&error_file); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +error_msg(const char *err_str, bool stop) +/* ---------------------------------------------------------------------- */ +{ + + io_error_count++; + if (error_file != NULL && error_on) + { + //(*error_file) << err_str; + screen_msg(err_str); + error_flush(); + } + if (stop) + { + if (error_file != NULL && error_on) + { + //(*error_file) << "Stopping.\n"; + screen_msg("Stopping.\n"); + fflush(error_file); + } + output_msg("Stopping.\n"); + log_msg("Stopping.\n"); + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +warning_msg(const char *err_str) +/* ---------------------------------------------------------------------- */ +{ + if (error_file != NULL && error_on) + { + //(*error_file) << err_str << "\n"; + std::string err_stdstr(err_str); + err_stdstr.append("\n"); + screen_msg(err_stdstr.c_str()); + error_flush(); + } + std::ostringstream warn_str; + warn_str << err_str << "\n"; + log_msg(warn_str.str().c_str()); + log_flush(); + output_msg(warn_str.str().c_str()); + output_flush(); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +screen_msg(const char * str) +/* ---------------------------------------------------------------------- */ +{ + std::string stdstr(str); + if (error_file != NULL && screen_on) + { + fprintf(error_file, "%s", str); + } +} +#endif +// ---------------------------------------------------------------------- */ +// dump ostream methods +// ---------------------------------------------------------------------- */ +/* ---------------------------------------------------------------------- */ +bool PHRQ_io:: +dump_open(const char *file_name, std::ios_base::openmode mode) +/* ---------------------------------------------------------------------- */ +{ + return ofstream_open(&dump_ostream, file_name, mode); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +dump_flush(void) +/* ---------------------------------------------------------------------- */ +{ + if (dump_ostream) + { + dump_ostream->flush(); + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +dump_close(void) +/* ---------------------------------------------------------------------- */ +{ + safe_close(&dump_ostream); +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +dump_msg(const char * str) +/* ---------------------------------------------------------------------- */ +{ + if (dump_ostream != NULL && dump_on) + { + (*dump_ostream) << str; + } +} +int PHRQ_io:: +getc(void) +{ + if (std::istream* is = get_istream()) + { + int n = is->get(); + if (n == 13 && is->peek() == 10) + { + n = is->get(); + } + return n; + } + return EOF; +} + +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +fpunchf(const char *name, const char *format, double d) +/* ---------------------------------------------------------------------- */ +{ + if (punch_ostream != NULL && punch_on) + { + fpunchf_helper(punch_ostream, format, d); + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +fpunchf(const char *name, const char *format, char * s) +/* ---------------------------------------------------------------------- */ +{ + if (punch_ostream != NULL && punch_on) + { + fpunchf_helper(punch_ostream, format, s); + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +fpunchf(const char *name, const char *format, int d) +/* ---------------------------------------------------------------------- */ +{ + if (punch_ostream != NULL && punch_on) + { + fpunchf_helper(punch_ostream, format, d); + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +fpunchf_helper(std::ostream *os, const char *format, ...) +/* ---------------------------------------------------------------------- */ +{ + if (os) + { + const size_t STACK_MAX = 2048; + char stack_buffer[STACK_MAX]; + + va_list args; + va_start(args, format); + int j = ::vsnprintf(stack_buffer, STACK_MAX, format, args); + bool success = (j >= 0 && j < (int) STACK_MAX); + va_end(args); + + if (success) + { + (*os) << stack_buffer; + } + else + { + size_t alloc_buffer_size = STACK_MAX * 2; + char *alloc_buffer = new char[alloc_buffer_size]; + do + { + va_list args; + va_start(args, format); + j = ::vsnprintf(alloc_buffer, alloc_buffer_size, format, args); + success = (j >= 0 && j < (int) alloc_buffer_size); + va_end(args); + if (!success) + { + delete[] alloc_buffer; + alloc_buffer_size *= 2; + alloc_buffer = new char[alloc_buffer_size]; + } + } + while (!success); + + (*os) << alloc_buffer; + delete[] alloc_buffer; + } + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +fpunchf_helper(std::string *str, const char *format, ...) +/* ---------------------------------------------------------------------- */ +{ + if (str) + { + const size_t STACK_MAX = 2048; + char stack_buffer[STACK_MAX]; + + va_list args; + va_start(args, format); + int j = ::vsnprintf(stack_buffer, STACK_MAX, format, args); + bool success = (j >= 0 && j < (int) STACK_MAX); + va_end(args); + + if (success) + { + (*str) += stack_buffer; + } + else + { + size_t alloc_buffer_size = STACK_MAX * 2; + char *alloc_buffer = new char[alloc_buffer_size]; + do + { + va_list args; + va_start(args, format); + j = ::vsnprintf(alloc_buffer, alloc_buffer_size, format, args); + success = (j >= 0 && j < (int) alloc_buffer_size); + va_end(args); + if (!success) + { + delete[] alloc_buffer; + alloc_buffer_size *= 2; + alloc_buffer = new char[alloc_buffer_size]; + } + } + while (!success); + + (*str) += alloc_buffer; + delete[] alloc_buffer; + } + } +} + +/* ---------------------------------------------------------------------- */ +void PHRQ_io::fpunchf_end_row(const char *format) +/* ---------------------------------------------------------------------- */ +{ + //NOOP for Phreeqc +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +close_ostreams(void) +/* ---------------------------------------------------------------------- */ +{ + std::set streams; + + streams.insert(output_ostream); + streams.insert(log_ostream); +// streams.insert(punch_ostream); // Should be deleted in ~SelectedOutput +#ifdef ERROR_OSTREAM + streams.insert(error_ostream); +#else + safe_close(&error_file); +#endif + streams.insert(dump_ostream); + + std::set::iterator it = streams.begin(); + for (; it != streams.end(); it++) + { + std::ostream * x = *it; + safe_close(&x); + } + + output_ostream = NULL; + log_ostream = NULL; + punch_ostream = NULL; +#ifdef ERROR_OSTREAM + error_ostream = NULL; +#else + error_file = NULL; +#endif + dump_ostream = NULL; +} +//safe_close is static method +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +safe_close(std::ostream **stream_ptr) +/* ---------------------------------------------------------------------- */ +{ + if ( +#if !defined(R_SO) + *stream_ptr != &std::cerr && + *stream_ptr != &std::cout && + *stream_ptr != &std::clog && +#endif + *stream_ptr != NULL) + { + delete *stream_ptr; + *stream_ptr = NULL; + } +} +void PHRQ_io:: +safe_close(FILE **file_ptr) +/* ---------------------------------------------------------------------- */ +{ + if ( +#if !defined(R_SO) + *file_ptr != stderr && + *file_ptr != stdout && + *file_ptr != stdin && +#endif + *file_ptr != NULL) + { + fclose(*file_ptr); + *file_ptr = NULL; + } +} +/* ---------------------------------------------------------------------- */ +void PHRQ_io:: +echo_msg(const char * str) +/* ---------------------------------------------------------------------- */ +{ + if (echo_on) + { + switch (this->echo_destination) + { + case ECHO_LOG: + log_msg(str); + break; + case ECHO_OUTPUT: + output_msg(str); + break; + } + } +} + +std::istream * PHRQ_io:: +get_istream() +{ + if (istream_list.size() > 0) + { + return istream_list.front(); + } + else + { + return NULL; + } +} +void PHRQ_io:: +push_istream(std::istream * cookie, bool auto_delete) +{ + istream_list.push_front(cookie); + delete_istream_list.push_front(auto_delete); +} +void PHRQ_io:: +clear_istream(void) +{ + while (istream_list.size() > 0) + { + pop_istream(); + } +} +void PHRQ_io:: +pop_istream() +{ + if (istream_list.size() > 0) + { + if (delete_istream_list.front()) + { + delete istream_list.front(); + } + istream_list.pop_front(); + delete_istream_list.pop_front(); + } +} +/* ---------------------------------------------------------------------- */ +PHRQ_io::LINE_TYPE PHRQ_io:: +get_line(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read a line from input file put in "line". + * Copy of input line is stored in "line_save". + * Characters after # are discarded in line but retained in "line_save" + * + * Arguments: + * fp is file name + * Returns: + * EMPTY, + * EOF, + * KEYWORD, + * OK, + * OPTION + */ + std::string stdtoken; + bool continue_loop = true;; + + PHRQ_io::LINE_TYPE return_value; + // loop for include files + for (;;) + { + if (this->get_istream() == NULL) + { + break; + } + return_value = LT_EMPTY; + while (return_value == LT_EMPTY) + { + /* + * Eliminate all characters after # sign as a comment + */ + /* + * Get line, check for eof + */ + continue_loop = false; + + if (get_logical_line() == LT_EOF) + { + //pop next file + this->pop_istream(); + continue_loop = true; + break; + } + /* + * Get long lines + */ + bool empty = true; + m_line = m_line_save.substr(0, m_line_save.find_first_of('#')); + for (unsigned int i = 0; i < m_line.size(); ++i) + { + if (!::isspace(m_line[i])) + { + empty = false; + break; + } + } + + if (this->accumulate) + { + this->accumulated.append(m_line_save); + this->accumulated.append("\n"); + } + // + // New line character encountered + // + return_value = (empty ? LT_EMPTY : LT_OK); + + } + if (continue_loop) continue; + // + // Determine return_value + // + if (return_value == LT_OK) + { + if (check_key(m_line.begin(), m_line.end())) + { + return_value = LT_KEYWORD; + } + else + { + std::string::iterator beg = m_line.begin(); + std::string::iterator end = m_line.end(); + std::string token; + CParser::copy_token(token, beg, end); + + if (token.size() > 1 && token[0] == '-' &&::isalpha(token[1])) + { + return_value = LT_OPTION; + } + } + } + + // add new include file to stack + std::string::iterator beg = m_line.begin(); + std::string::iterator end = m_line.end(); + CParser::copy_token(stdtoken, beg, end); + std::transform(stdtoken.begin(), stdtoken.end(), stdtoken.begin(), ::tolower); + if ((strstr(stdtoken.c_str(),"include$") == stdtoken.c_str()) || + (strstr(stdtoken.c_str(),"include_file") == stdtoken.c_str())) + { + std::string file_name; + file_name.assign(beg, end); + file_name = trim(file_name); + + if (file_name.size() > 0) + { + std::ifstream *next_stream = new std::ifstream(file_name.c_str(), std::ios_base::in); + if (!next_stream->is_open()) + { + std::ostringstream errstr; + errstr << "\n*********** Could not open include file " << file_name + <<".\n Please, write the full path to this file. ***********\n\n"; + delete next_stream; +#if defined(PHREEQCI_GUI) + warning_msg(errstr.str().c_str()); + continue; +#else + output_msg(errstr.str().c_str()); + error_msg(errstr.str().c_str(), OT_STOP); +#endif + } + else + { + this->push_istream(next_stream); + std::ostringstream errstr; + errstr << "\n\tReading data from " << file_name <<" ...\n"; + output_msg(errstr.str().c_str()); // **appt + } + continue; + } + } + return return_value; + } + m_next_keyword = Keywords::KEY_END; + return LT_EOF; +} + +/** + Reads input stream until end of line, ";", or eof + stores characters in line_save + + returns: + EOF on empty line on end of file or + OK otherwise +*/ +PHRQ_io::LINE_TYPE PHRQ_io:: +get_logical_line(void) +{ + int j; + unsigned int pos; + char c; + + m_line_save.erase(m_line_save.begin(), m_line_save.end()); // m_line_save.clear(); + while ((j = getc()) != EOF) + { + c = (char) j; + if (c == '#') + { + // ignore all chars after # until newline + do + { + c = (char) j; + if (c == '\n') + { + break; + } + m_line_save += c; + } + while ((j = getc()) != EOF); + } + if (c == ';') + break; + if (c == '\n') + { + break; + } + if (c == '\\') + { + pos = (int) m_line_save.size(); + m_line_save += c; + while ((j = getc()) != EOF) + { + c = (char) j; + if (c == '\\') + { + pos = (int) m_line_save.size(); + m_line_save += c; + continue; + } + if (c == '\n') + { + // remove '\\' + m_line_save = m_line_save.substr(0,pos); + break; + } + m_line_save += c; + if (!::isspace(j)) + break; + } + } + else + { + m_line_save += c; + } + } + if (j == std::char_traits < char >::eof() && m_line_save.size() == 0) + { + return (LT_EOF); + } + return (LT_OK); +} + +bool PHRQ_io:: +check_key(std::string::iterator begin, std::string::iterator end) +{ + std::string lowercase; + CParser::copy_token(lowercase, begin, end); + std::transform(lowercase.begin(), lowercase.end(), lowercase.begin(), + tolower); + + m_next_keyword = Keywords::Keyword_search(lowercase); + if (m_next_keyword == Keywords::KEY_NONE) + { + return false; + } + return true; +} + diff --git a/phreeqcpp/common/PHRQ_io.h b/phreeqcpp/common/PHRQ_io.h new file mode 100644 index 00000000..8c469248 --- /dev/null +++ b/phreeqcpp/common/PHRQ_io.h @@ -0,0 +1,211 @@ +#ifndef _PHRQIO_H +#define _PHRQIO_H + +#if defined(_WINDLL) +#define IPQ_DLL_EXPORT __declspec(dllexport) +#else +#define IPQ_DLL_EXPORT +#endif + +#include +#include +#include +#include "Keywords.h" +#include + +#define ERROR_OSTREAM + +class PhreeqcStop : public std::exception +{ +}; + +class IPQ_DLL_EXPORT PHRQ_io +{ +public: + enum LINE_TYPE + { + LT_EOF = -1, + LT_OK = 1, + LT_EMPTY = 2, + LT_KEYWORD = 3, + LT_OPTION = 8 + }; + + enum ONERROR_TYPE + { + OT_CONTINUE = 0, + OT_STOP = 1 + }; + // constructor/destructor + PHRQ_io(void); + virtual ~ PHRQ_io(); + + // methods + static void safe_close(std::ostream **stream_ptr); + static void safe_close(FILE **file_ptr); + void close_ostreams(void); + void Set_io_error_count(int i) {this->io_error_count = i;}; + int Get_io_error_count(void) {return this->io_error_count;}; + + + // istreams + std::istream *get_istream(); + void pop_istream(); + void push_istream(std::istream * cookie, bool auto_delete = true); + void clear_istream(void); + + // helper + bool ofstream_open(std::ostream **os, const char *file_name, std::ios_base::openmode mode = std::ios_base::out); + + // output_ostream + virtual bool output_open(const char *file_name, std::ios_base::openmode mode = std::ios_base::out); + void output_flush(void); + void output_close(void); + virtual void output_msg(const char * str); + void Set_output_ostream(std::ostream * out) {this->output_ostream = out;}; + std::ostream *Get_output_ostream(void) {return this->output_ostream;}; + void Set_output_on(bool tf) {this->output_on = tf;}; + bool Get_output_on(void) {return this->output_on;}; + + // log_ostream + virtual bool log_open(const char *file_name, std::ios_base::openmode mode = std::ios_base::out); + void log_flush(void); + void log_close(void); + virtual void log_msg(const char * str); + void Set_log_ostream(std::ostream * out) {this->log_ostream = out;} + std::ostream *Get_log_ostream(void) {return this->log_ostream;} + void Set_log_on(bool tf) {this->log_on = tf;} + bool Get_log_on(void) {return this->log_on;} + + // punch_ostream + virtual bool punch_open(const char *file_name, std::ios_base::openmode mode = std::ios_base::out, int n_user = 1); + void punch_flush(void); + void punch_close(void); + virtual void punch_msg(const char * str); + void Set_punch_ostream(std::ostream * out) {this->punch_ostream = out;} + std::ostream *Get_punch_ostream(void) {return this->punch_ostream;} + void Set_punch_on(bool tf) {this->punch_on = tf;} + bool Get_punch_on(void) {return this->punch_on;} + + // error_ostream +#ifdef ERROR_OSTREAM + virtual bool error_open(const char *file_name, std::ios_base::openmode mode = std::ios_base::out); + void error_flush(void); + void error_close(void); + virtual void error_msg(const char * str, bool stop=false); + void Set_error_ostream(std::ostream * out) {this->error_ostream = out;} + std::ostream *Get_error_ostream(void) {return this->error_ostream;} + void Set_error_on(bool tf) {this->error_on = tf;} + bool Get_error_on(void) {return this->error_on;} + virtual void warning_msg(const char *err_str); +#else + virtual bool error_open(const char *file_name, const char * mode = "w"); + void error_flush(void); + void error_close(void); + virtual void error_msg(const char * str, bool stop=false); + void Set_error_file(FILE * out) {this->error_file = out;} + FILE *Get_error_file(void) {return this->error_file;} + void Set_error_on(bool tf) {this->error_on = tf;} + bool Get_error_on(void) {return this->error_on;} + virtual void warning_msg(const char *err_str); +#endif + + // dump_ostream + virtual bool dump_open(const char *file_name, std::ios_base::openmode mode = std::ios_base::out); + void dump_flush(void); + void dump_close(void); + virtual void dump_msg(const char * str); + void Set_dump_ostream(std::ostream * out) {this->dump_ostream = out;}; + std::ostream *Get_dump_ostream(void) {return this->dump_ostream;}; + void Set_dump_on(bool tf) {this->dump_on = tf;}; + bool Get_dump_on(void) {return this->dump_on;}; + + // fpunchf + virtual void fpunchf(const char *name, const char *format, double d); + virtual void fpunchf(const char *name, const char *format, char * d); + virtual void fpunchf(const char *name, const char *format, int d); + virtual void fpunchf_end_row(const char *format); + static void fpunchf_helper(std::ostream *os, const char *format, ...); + static void fpunchf_helper(std::string *str, const char *format, ...); + + virtual void screen_msg(const char * str); + void Set_screen_on(bool tf) {this->screen_on = tf;}; + bool Get_screen_on(void) {return this->screen_on;}; + + // input methods + virtual int getc(void); + virtual LINE_TYPE get_line(void); + virtual LINE_TYPE get_logical_line(void); + bool check_key(std::string::iterator begin, std::string::iterator end); + std::string & Get_m_line() {return m_line;} + std::string & Get_m_line_save() {return m_line_save;} + std::string & Get_accumulated() {return accumulated;} + LINE_TYPE Get_m_line_type() {return m_line_type;}; + void Set_accumulate(bool tf) + { + if (tf) + { + accumulated.clear(); + } + this->accumulate = tf; + } + Keywords::KEYWORDS Get_m_next_keyword() const {return m_next_keyword;} + + // echo + enum ECHO_OPTION + { + ECHO_LOG, + ECHO_OUTPUT + }; + virtual void echo_msg(const char * str); + void Set_echo_on(bool tf) {this->echo_on = tf;}; + bool Get_echo_on(void) {return this->echo_on;}; + void Set_echo_destination(ECHO_OPTION eo) {this->echo_destination = eo;}; + ECHO_OPTION Get_echo_destination(void) {return this->echo_destination;}; + + // data +protected: + std::ostream *output_ostream; + std::ostream *log_ostream; + std::ostream *punch_ostream; +#ifdef ERROR_OSTREAM + std::ostream *error_ostream; +#else + FILE * error_file; +#endif + std::ostream *dump_ostream; + int io_error_count; + + bool output_on; + bool log_on; + bool punch_on; + bool error_on; + bool dump_on; + bool echo_on; + bool screen_on; + ECHO_OPTION echo_destination; + +#if defined(_MSC_VER) +/* disable warning C4251: 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2' */ +#pragma warning(disable:4251) +#endif + + std::list istream_list; + std::list delete_istream_list; + + std::string m_line; + std::string m_line_save; + std::string accumulated; + +#if defined(_MSC_VER) +/* reset warning C4251 */ +#pragma warning(default:4251) +#endif + + // input data members + Keywords::KEYWORDS m_next_keyword; + bool accumulate; + LINE_TYPE m_line_type; +}; + +#endif /* _PHRQIO_H */ diff --git a/phreeqcpp/common/Parser.cxx b/phreeqcpp/common/Parser.cxx new file mode 100644 index 00000000..c0e13059 --- /dev/null +++ b/phreeqcpp/common/Parser.cxx @@ -0,0 +1,1317 @@ +// Parser.cpp: implementation of the CParser class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif + +#include // std::transform +#include // std::map +#include // assert +#include +#include // std::cout std::cerr +#include "Utils.h" +#include +#include "Parser.h" +#include "PHRQ_io.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// +CParser::CParser(PHRQ_io *io): +PHRQ_base(io), +m_input_stream(std::cin), +m_input_error(0), +m_next_keyword(Keywords::KEY_NONE) +{ + if (!io) + { + error_msg("This parser constructor requires non-null phrq_io", PHRQ_io::OT_STOP); + } + else + { + m_line_save = io->Get_m_line(); + m_line = io->Get_m_line(); + m_line_type = io->Get_m_line_type(); + m_line_iss.str(m_line); + m_line_iss.seekg(0, std::ios_base::beg); + m_line_iss.clear(); + echo_file = EO_ALL; + echo_stream = EO_NONE; + accumulate = false; + phrq_io_only = true; + } +} + +CParser::CParser(std::istream & input, PHRQ_io *io): +PHRQ_base(io), +m_input_stream(input), +m_input_error(0), +m_next_keyword(Keywords::KEY_NONE) +{ + m_line_save.reserve(80); + m_line.reserve(80); + echo_file = EO_ALL; + echo_stream = EO_NONE; + accumulate = false; + phrq_io_only = false; +} + +CParser::~CParser() +{ +} + +PHRQ_io::LINE_TYPE CParser::check_line(const std::string & str, + bool allow_empty, bool allow_eof, + bool allow_keyword, bool print) +{ + PHRQ_io::LINE_TYPE + i; + + // Get line + do + { + i = get_line(); + // reset iss + m_line_iss.str(m_line); + m_line_iss.seekg(0, std::ios_base::beg); + m_line_iss.clear(); + + // output for stream + switch (this->echo_stream) + { + case EO_NONE: + break; + case EO_ALL: + if (i != PHRQ_io::LT_EOF) + { + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + //get_output() << msg; + io->output_msg(msg.str().c_str()); + } + break; + case EO_KEYWORDS: + if (i == PHRQ_io::LT_KEYWORD) + { + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + //get_output() << msg; + io->output_msg(msg.str().c_str()); + } + break; + case EO_NOKEYWORDS: + if (i != PHRQ_io::LT_KEYWORD && i != PHRQ_io::LT_EOF) + { + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + //get_output() << msg; + io->output_msg(msg.str().c_str()); + } + break; + } + // output for file + switch (this->echo_file) + { + case EO_NONE: + break; + case EO_ALL: + if (i != PHRQ_io::LT_EOF) + { + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + this->echo_msg(msg.str()); + } + break; + case EO_KEYWORDS: + if (i == PHRQ_io::LT_KEYWORD) + { + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + this->echo_msg(msg.str()); + } + break; + + case EO_NOKEYWORDS: + if (i != PHRQ_io::LT_KEYWORD && i != PHRQ_io::LT_EOF) + { + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + this->echo_msg(msg.str().c_str()); + } + break; + } + } + while (i == PHRQ_io::LT_EMPTY && allow_empty == false); + + // Check eof + if (i == PHRQ_io::LT_EOF && allow_eof == false) + { + std::ostringstream msg; + msg << "Unexpected eof while reading " << str << + "\nExecution terminated.\n"; + error_msg(msg.str().c_str(), PHRQ_io::OT_STOP); + } + + // Check keyword + if (i == PHRQ_io::LT_KEYWORD && allow_keyword == false) + { + std::ostringstream msg; + msg << "Expected data for " << str << + ", but got a keyword ending data block."; + error_msg(msg.str().c_str(), PHRQ_io::OT_CONTINUE); + incr_input_error(); + } + m_line_type = i; + return i; +} +PHRQ_io::LINE_TYPE CParser::get_line_phrq_io() +{ + m_line_type = io->get_line(); + m_line_save = io->Get_m_line_save(); + m_line = io->Get_m_line(); + m_next_keyword = io->Get_m_next_keyword(); + if (accumulate) + { + this->accumulated.append(m_line_save); + this->accumulated.append("\n"); + } + return m_line_type; +} + +PHRQ_io::LINE_TYPE CParser::get_line() +{ + if (this->phrq_io_only) + { + return get_line_phrq_io(); + } + PHRQ_io::LINE_TYPE return_value = PHRQ_io::LT_EMPTY; + while (return_value == PHRQ_io::LT_EMPTY) + { + // + // Eliminate all characters after # sign as a comment + // + + // + // Get line, check for eof + // + if (get_logical_line() == PHRQ_io::LT_EOF) + { + if (!m_input_stream.eof()) + { + error_msg("Reading input file.", PHRQ_io::OT_CONTINUE); + error_msg("istream::get() returned an error.", PHRQ_io::OT_STOP); + } + else + { + //{{MOD + m_line.erase(m_line.begin(), m_line.end()); // m_line.clear(); + //}}MOD + m_next_keyword = Keywords::KEY_END; + return PHRQ_io::LT_EOF; + } + } + + // + // Get long lines + // + bool + empty = true; + m_line = m_line_save.substr(0, m_line_save.find_first_of('#')); + for (unsigned int i = 0; i < m_line.size(); ++i) + { + if (!::isspace(m_line[i])) + { + empty = false; + break; + } + } + + if (this->accumulate) + { + this->accumulated.append(m_line_save); + this->accumulated.append("\n"); + } + // + // New line character encountered + // + return_value = (empty ? PHRQ_io::LT_EMPTY : PHRQ_io::LT_OK); + } + + // + // Determine return_value + // + if (return_value == PHRQ_io::LT_OK) + { + if (check_key(m_line.begin(), m_line.end())) + { + return_value = PHRQ_io::LT_KEYWORD; + } + else + { + std::string::iterator beg = m_line.begin(); + std::string::iterator end = m_line.end(); + std::string token; + copy_token(token, beg, end); + + if (token.size() > 1 && token[0] == '-' &&::isalpha(token[1])) + { + return_value = PHRQ_io::LT_OPTION; + } + } + } + return return_value; +} + +/** + Reads input stream until end of line, ";", or eof + stores characters in line_save + + returns: + EOF on empty line on end of file or + OK otherwise +*/ +PHRQ_io::LINE_TYPE CParser::get_logical_line() +{ + int + j; + unsigned int + pos; + char + c; + + m_line_save.erase(m_line_save.begin(), m_line_save.end()); // m_line_save.clear(); + + while ((j = m_input_stream.get()) != std::char_traits < char >::eof()) + { + c = (char) j; + if (c == '#') + { + // ignore all chars after # until newline + do + { + c = (char) j; + if (c == '\n') + { + break; + } + m_line_save += c; + } + while ((j = + m_input_stream.get()) != std::char_traits < + char >::eof()); + } + if (c == ';') + break; + if (c == '\n') + { + break; + } + if (c == '\\') + { + pos = (int) m_line_save.size(); + m_line_save += c; + while ((j = + m_input_stream.get()) != std::char_traits < char >::eof()) + { + c = (char) j; + if (c == '\\') + { + pos = (int) m_line_save.size(); + m_line_save += c; + continue; + } + if (c == '\n') + { + // remove '\\' + for (; pos < m_line_save.size(); pos++) + { + m_line_save[pos] = m_line_save[pos + 1]; + } + m_line_save.erase(m_line_save.size() - 1, 1); + break; + } + m_line_save += c; + if (!::isspace(j)) + break; + } + } + else + { + m_line_save += c; + } + } + if (j == std::char_traits < char >::eof() && m_line_save.size() == 0) + { + return (PHRQ_io::LT_EOF); + } + return (PHRQ_io::LT_OK); +} + +bool +CParser::check_key(std::string::iterator begin, std::string::iterator end) +{ + std::string lowercase; + copy_token(lowercase, begin, end); + std::transform(lowercase.begin(), lowercase.end(), lowercase.begin(), + tolower); + m_next_keyword = Keywords::Keyword_search(lowercase); + if (m_next_keyword == Keywords::KEY_NONE) + { + return false; + } + return true; +} +CParser::STATUS_TYPE CParser::check_units(std::string & tot_units, + bool alkalinity, + bool check_compatibility, + const std::string & default_units, + bool print) +{ +/* + * Check if legitimate units + * Input: + * tot_units character string to check, + * alkalinity true if alkalinity, false if any other total, + * check_compatibility true check alk and default units, false otherwise + * default_units character string of default units (check /L, /kg, etc) + * print true print warning messages + * Output: + * tot_units standard form for unit + */ + using + Utilities::str_tolower; + using + Utilities::replace; + using + Utilities::squeeze_white; + + static const char * + units[] = { + "Mol/l", /* 0 */ + "mMol/l", /* 1 */ + "uMol/l", /* 2 */ + "g/l", /* 3 */ + "mg/l", /* 4 */ + "ug/l", /* 5 */ + "Mol/kgs", /* 6 */ + "mMol/kgs", /* 7 */ + "uMol/kgs", /* 8 */ + "g/kgs", /* 9 = ppt */ + "mg/kgs", /* 10 = ppm */ + "ug/kgs", /* 11 = ppb */ + "Mol/kgw", /* 12 = mol/kg H2O */ + "mMol/kgw", /* 13 = mmol/kg H2O */ + "uMol/kgw", /* 14 = umol/kg H2O */ + "g/kgw", /* 15 = mol/kg H2O */ + "mg/kgw", /* 16 = mmol/kg H2O */ + "ug/kgw", /* 17 = umol/kg H2O */ + "eq/l", /* 18 */ + "meq/l", /* 19 */ + "ueq/l", /* 20 */ + "eq/kgs", /* 21 */ + "meq/kgs", /* 22 */ + "ueq/kgs", /* 23 */ + "eq/kgw", /* 24 */ + "meq/kgw", /* 25 */ + "ueq/kgw", /* 26 */ + }; + + squeeze_white(tot_units); + str_tolower(tot_units); + replace("milli", "m", tot_units); + replace("micro", "u", tot_units); + replace("grams", "g", tot_units); + replace("gram", "g", tot_units); + replace("moles", "Mol", tot_units); + replace("mole", "Mol", tot_units); + replace("mol", "Mol", tot_units); + replace("liter", "l", tot_units); + replace("kgh", "kgw", tot_units); + replace("ppt", "g/kgs", tot_units); + replace("ppm", "mg/kgs", tot_units); + replace("ppb", "ug/kgs", tot_units); + replace("equivalents", "eq", tot_units); + replace("equivalent", "eq", tot_units); + replace("equiv", "eq", tot_units); + + std::string::size_type end; + if ((end = tot_units.find("/l")) != std::string::npos) + { + tot_units.resize(end + 2); + } + if ((end = tot_units.find("/kgs")) != std::string::npos) + { + tot_units.resize(end + 4); + } + if ((end = tot_units.find("/kgw")) != std::string::npos) + { + tot_units.resize(end + 4); + } + + // + // Check if unit in list + // + bool + found = false; + for (unsigned int i = 0; i < sizeof(units) / sizeof(char *); ++i) + { + if (tot_units.compare(units[i]) == 0) + { + found = true; + break; + } + } + if (!found) + { + if (print) + { + std::ostringstream err; + err << "Unknown unit, " << tot_units; + this->error_msg(err.str().c_str(), PHRQ_io::OT_CONTINUE); + } + return PARSER_ERROR; + } + + // + // Check if units are compatible with default_units + // + if (check_compatibility == false) + return PARSER_OK; + + // + // Special cases for alkalinity + // + if (alkalinity == true && tot_units.find("Mol") != std::string::npos) + { + if (print) + { + this->warning_msg + ("Alkalinity given in moles, assumed to be equivalents."); + } + replace("Mol", "eq", tot_units); + } + if (alkalinity == false && tot_units.find("eq") != std::string::npos) + { + if (print) + { + this->error_msg("Only alkalinity can be entered in equivalents.", + PHRQ_io::OT_CONTINUE); + } + return PARSER_ERROR; + } + + // + // See if default_units are compatible with tot_units + // + if (default_units.find("/l") != std::string::npos + && tot_units.find("/l") != std::string::npos) + return PARSER_OK; + if (default_units.find("/kgs") != std::string::npos + && tot_units.find("/kgs") != std::string::npos) + return PARSER_OK; + if (default_units.find("/kgw") != std::string::npos + && tot_units.find("/kgw") != std::string::npos) + return PARSER_OK; + + std::string str = default_units; + replace("kgs", "kg solution", str); + replace("kgs", "kg solution", tot_units); + replace("kgw", "kg water", str); + replace("kgw", "kg water", tot_units); + replace("/l", "/L", str); + replace("Mol", "mol", str); + replace("/l", "/L", tot_units); + replace("Mol", "mol", tot_units); + + if (print) + { + std::ostringstream err; + err << "Units for master species, " << tot_units << + ", are not compatible with default units, " << str << "."; + this->error_msg(err.str().c_str(), PHRQ_io::OT_CONTINUE); + } + return PARSER_ERROR; +} +CParser::TOKEN_TYPE CParser::token_type(const std::string & token) +{ + if (!token.empty()) + { + if (::isupper(token[0])) + { + return CParser::TT_UPPER; + } + else if (::islower(token[0])) + { + return CParser::TT_LOWER; + } + else if (::isdigit(token[0]) || token[0] == '.' || token[0] == '-') + { + return CParser::TT_DIGIT; + } + else + { + assert(!::isspace(token[0])); + return CParser::TT_UNKNOWN; + } + } + else + { + return CParser::TT_EMPTY; + } +} + +CParser::TOKEN_TYPE CParser::peek_token() +{ + std::istringstream::pos_type pos = m_line_iss.tellg(); + std::string token; + m_line_iss >> token; + m_line_iss.seekg(pos); + return token_type(token); +} + +CParser::TOKEN_TYPE CParser::copy_token(std::string & token, + std::string::iterator & begin, + std::string::iterator & end) +{ + if (begin != end) + { + std::string::iterator b = begin; + for (; b < end &&::isspace(*b); ++b); + + begin = b; + for (; begin < end && !::isspace(*begin); ++begin); + + token.assign(b, begin); + } + else + { + token.resize(0); + } + + return token_type(token); +} + +CParser::TOKEN_TYPE CParser::copy_token(std::string & token, + std::istream & is) +{ + is >> token; + return token_type(token); +} + +CParser::TOKEN_TYPE CParser::copy_token(std::string & token, + std::istream::pos_type & pos) +{ + m_line_iss.seekg(pos); + if (!(m_line_iss >> token)) + { + token.erase(token.begin(), token.end()); // token.clear(); + } + pos = m_line_iss.tellg(); + return token_type(token); +} + +CParser::FIND_TYPE CParser::find_option(const std::string & item, int *n, + const std::vector < std::string > + &list, bool exact) +{ + std::string token(item); + std::transform(token.begin(), token.end(), token.begin(), tolower); + for (unsigned int i = 0; i < list.size(); i++) + { + if (exact == true) + { + if (list[i].compare(token) == 0) + { + *n = i; + return FT_OK; + } + } + else + { + if (list[i].find(token) == 0) + { + *n = i; + return FT_OK; + } + } + } + + *n = -1; + return FT_ERROR; +} + +int +CParser::get_option(const std::vector < std::string > &opt_list, + std::string::iterator & next_char) +{ + // + // Read a line and check for options + // + int j; + int /* opt_l, */ opt; + std::string::iterator opt_ptr; + std::string option; + +#if !defined(R_SO) + fprintf(stderr, "Did not think this get_option was called\n"); +#endif + // + // Read line + // + PHRQ_io::LINE_TYPE lt = check_line("get_option", false, true, true, true); + if (lt == PHRQ_io::LT_EOF) + { + j = OPT_EOF; + } + else if (lt == PHRQ_io::LT_KEYWORD) + { + j = OPT_KEYWORD; + } + else if (lt == PHRQ_io::LT_OPTION) + { + opt_ptr = m_line.begin(); + std::string::iterator end = m_line.end(); + copy_token(option, opt_ptr, end); + if (find_option(option, &opt, opt_list, false) == CParser::FT_OK) + { + j = opt; + m_line_save.replace(m_line_save.find(option), option.size(), + opt_list[opt]); + m_line.replace(m_line.find(option), option.size(), opt_list[opt]); + opt_ptr = m_line.begin(); + std::string::iterator end = m_line.end(); + copy_token(option, opt_ptr, end); + next_char = opt_ptr; + if (true) // pr.echo_input == TRUE + { + if (true) // database_file == NULL + { + //get_output() << "\t" << m_line_save << "\n"; + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + io->output_msg(msg.str().c_str()); + } + } + } + else + { + if (true) // (database_file == NULL) + { + //get_output() << "\t" << m_line_save << "\n"; + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + io->output_msg(msg.str().c_str()); + } + + std::ostringstream err; + err << "Unknown option." << "\n"; + err << m_line_save << "\n"; + error_msg(err.str().c_str()); + + j = OPT_ERROR; + next_char = m_line.begin(); + } + } + else + { + opt_ptr = m_line.begin(); + std::string::iterator end = m_line.end(); + copy_token(option, opt_ptr, end); + if (find_option(option, &opt, opt_list, true) == FT_OK) + { + j = opt; + next_char = opt_ptr; + } + else + { + j = OPT_DEFAULT; + next_char = m_line.begin(); + } + if (true) // pr.echo_input == TRUE + { + if (true) // database_file == NULL + { +#if !defined(R_SO) + std::cout << "\t" << m_line_save << "\n"; +#endif + } + } + } + return (j); +} + +int +CParser::get_option(const std::vector < std::string > &opt_list, + std::istream::pos_type & next_pos) +{ + // + // Read a line and check for options + // + int j; + int opt; + std::istream::pos_type pos_ptr; + std::string option; + + // + // Read line + // + PHRQ_io::LINE_TYPE lt = check_line("get_option", false, true, true, true); + if (lt == PHRQ_io::LT_EOF) + { + j = OPT_EOF; + } + else if (lt == PHRQ_io::LT_KEYWORD) + { + j = OPT_KEYWORD; + } + else if (lt == PHRQ_io::LT_OPTION) + { + std::string::iterator opt_ptr = m_line.begin(); + std::string::iterator end = m_line.end(); + copy_token(option, opt_ptr, end); + if (find_option(option.substr(1), &opt, opt_list, false) == FT_OK) + { + // replace -option with option + j = opt; + m_line_save.replace(m_line_save.find(option), option.size(), + opt_list[opt]); + m_line.replace(m_line.find(option), option.size(), opt_list[opt]); + + // reset iss + m_line_iss.str(m_line); + m_line_iss.seekg(0, std::ios_base::beg); + m_line_iss.clear(); + + pos_ptr = 0; + copy_token(option, pos_ptr); + next_pos = pos_ptr; + } + else + { + j = OPT_ERROR; + next_pos = pos_ptr; + } + } + else + { + pos_ptr = m_line_iss.tellg(); + m_line_iss >> option; + if (find_option(option, &opt, opt_list, true) == FT_OK) + { + j = opt; + next_pos = m_line_iss.tellg(); + } + else + { + j = OPT_DEFAULT; + m_line_iss.seekg(pos_ptr); + m_line_iss.clear(); + next_pos = pos_ptr; + } + } + return (j); +} +CParser::STATUS_TYPE CParser::get_elt(std::string::iterator & begin, + const std::string::iterator end, + std::string & element) +{ + element.erase(element.begin(), element.end()); // element.clear(); + + if (begin == end) + { + error_msg("Empty string in get_elt. Expected an element name.", + PHRQ_io::OT_CONTINUE); + return PARSER_ERROR; + } + + // + // Load name into char array element + // + char + c = *begin; + ++begin; + element.insert(element.end(), c); // element.push_back(c); + if (c == '[') + { + while ((c = *begin) != ']') + { + element.insert(element.end(), c); // element.push_back(c); + ++begin; + if ((c = *begin) == ']') + { + element.insert(element.end(), c); // element.push_back(c); + ++begin; + break; + } + else if (begin == end) + { + error_msg("No ending bracket (]) for element name", + PHRQ_io::OT_CONTINUE); + incr_input_error(); + return PARSER_ERROR; + } + } + while (::islower(c = *begin) || c == '_') + { + element.insert(element.end(), c); // element.push_back(c); + ++begin; + if (begin == end) + break; + } + } + else + { + while (::islower(c = *begin) || c == '_') + { + element.insert(element.end(), c); // element.push_back(c); + ++begin; + if (begin == end) + break; + } + } + return PARSER_OK; +} + +CParser::STATUS_TYPE CParser::parse_couple(std::string & token) +{ + // Parse couple puts redox couples in standard form + // "+" is removed and couples are rewritten in sort + // order. + + if (Utilities::strcmp_nocase_arg1(token.c_str(), "pe") == 0) + { + Utilities::str_tolower(token); + return PARSER_OK; + } + + while (Utilities::replace("+", "", token)); + + std::string::iterator ptr = token.begin(); + std::string elt1; + get_elt(ptr, token.end(), elt1); + + if (*ptr != '(') + { + std::ostringstream err_msg; + err_msg << "Element name must be followed by " << + "parentheses in redox couple, " << token << "."; + error_msg(err_msg.str().c_str(), PHRQ_io::OT_CONTINUE); + incr_input_error(); + return PARSER_ERROR; + } + + int + paren_count = 1; + std::string paren1 = "("; + while (ptr != token.end()) + { + ++ptr; + if (*ptr == '/' || ptr == token.end()) + { + std::ostringstream err_msg; + err_msg << "End of line or " "/" + " encountered before end of parentheses, " << token << "."; + error_msg(err_msg.str().c_str(), PHRQ_io::OT_CONTINUE); + return PARSER_ERROR; + } + paren1.insert(paren1.end(), *ptr); // element.push_back(c); + if (*ptr == '(') + ++paren_count; + if (*ptr == ')') + --paren_count; + if (paren_count == 0) + break; + } + + ++ptr; + if (ptr == token.end() || *ptr != '/') + { + std::ostringstream err_msg; + err_msg << " " "/" " must follow parentheses " << + "ending first half of redox couple, " << token << "."; + error_msg(err_msg.str().c_str(), PHRQ_io::OT_CONTINUE); + return PARSER_ERROR; + } + ++ptr; + std::string elt2; + get_elt(ptr, token.end(), elt2); + if (elt1.compare(elt2) != 0) + { + std::ostringstream err_msg; + err_msg << "Redox couple must be two redox states " << + "of the same element, " << token << "."; + error_msg(err_msg.str().c_str(), PHRQ_io::OT_CONTINUE); + return PARSER_ERROR; + } + if (*ptr != '(') + { + std::ostringstream err_msg; + err_msg << "Element name must be followed by " + "parentheses in redox couple, " << token << "."; + error_msg(err_msg.str().c_str(), PHRQ_io::OT_CONTINUE); + incr_input_error(); + return PARSER_ERROR; + } + std::string paren2 = "("; + paren_count = 1; + + while (ptr != token.end()) + { + ++ptr; + if (*ptr == '/' || ptr == token.end()) + { + std::ostringstream err_msg; + err_msg << "End of line or " "/" + " encountered before end of parentheses, " << token << "."; + error_msg(err_msg.str().c_str(), PHRQ_io::OT_CONTINUE); + return PARSER_ERROR; + } + paren2.insert(paren2.end(), *ptr); // element.push_back(c); + if (*ptr == '(') + ++paren_count; + if (*ptr == ')') + --paren_count; + if (paren_count == 0) + break; + } + if (paren1.compare(paren2) < 0) + { + token = elt1 + paren1 + std::string("/") + elt2 + paren2; + } + else if (paren1.compare(paren2) > 0) + { + token = elt2 + paren2 + std::string("/") + elt1 + paren1; + } + else + { + std::ostringstream err_msg; + err_msg << "Both parts of redox couple are the same, " << + token << "."; + error_msg(err_msg.str().c_str(), PHRQ_io::OT_CONTINUE); + return PARSER_ERROR; + } + return PARSER_OK; +} + +template +CParser::STATUS_TYPE CParser::addPair(std::map < std::string, T >&totals, + std::istream::pos_type & pos) +{ + std::string token; + T d; + CParser::TOKEN_TYPE j; + + m_line_iss.seekg(pos); + + j = copy_token(token, pos); + + if (j == CParser::TT_EMPTY) + return PARSER_OK; + + if (!(m_line_iss >> d)) + { + return PARSER_ERROR; + } + totals[token] = d; + return PARSER_OK; +} + +int +CParser::getOptionFromLastLine(const std::vector < std::string > &opt_list, + std::string::iterator & next_char, bool flag_error) +{ + // + // Read a line and check for options + // + int j; + int /* opt_l, */ opt; + std::string::iterator opt_ptr; + std::string option; + + // + // Read line + // + PHRQ_io::LINE_TYPE lt = m_line_type; + if (lt == PHRQ_io::LT_EOF) + { + j = OPT_EOF; + } + else if (lt == PHRQ_io::LT_KEYWORD) + { + j = OPT_KEYWORD; + } + else if (lt == PHRQ_io::LT_OPTION) + { + opt_ptr = m_line.begin(); + std::string::iterator end = m_line.end(); + copy_token(option, opt_ptr, end); + if (find_option(option, &opt, opt_list, false) == CParser::FT_OK) + { + j = opt; + m_line_save.replace(m_line_save.find(option), option.size(), + opt_list[opt]); + m_line.replace(m_line.find(option), option.size(), opt_list[opt]); + opt_ptr = m_line.begin(); + std::string::iterator end = m_line.end(); + copy_token(option, opt_ptr, end); + next_char = opt_ptr; + if (true) // pr.echo_input == TRUE + { + if (true) // database_file == NULL + { + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + io->output_msg(msg.str().c_str()); + } + } + } + else + { + if (flag_error) + { + if (true) // (database_file == NULL) + { + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + io->output_msg(msg.str().c_str()); + } + std::ostringstream err; + err << "Unknown option." << "\n"; + err << m_line_save << "\n"; + error_msg(err.str().c_str()); + } + j = OPT_ERROR; + next_char = m_line.begin(); + } + } + else + { + opt_ptr = m_line.begin(); + std::string::iterator end = m_line.end(); + copy_token(option, opt_ptr, end); + if (find_option(option, &opt, opt_list, true) == FT_OK) + { + j = opt; + next_char = opt_ptr; + } + else + { + j = OPT_DEFAULT; + next_char = m_line.begin(); + } + if (true) // pr.echo_input == TRUE + { + if (true) // database_file == NULL + { +#if !defined(R_SO) + std::cout << "\t" << m_line_save << "\n"; +#endif + } + } + } + return (j); +} + +int +CParser::getOptionFromLastLine(const std::vector < std::string > &opt_list, + std::istream::pos_type & next_pos, bool flag_error) +{ + // + // Read a line and check for options + // + int j; + int opt; + std::istream::pos_type pos_ptr; + std::string option; + + // + // Read line + // + PHRQ_io::LINE_TYPE lt = m_line_type; + if (lt == PHRQ_io::LT_EOF) + { + j = OPT_EOF; + } + else if (lt == PHRQ_io::LT_KEYWORD) + { + j = OPT_KEYWORD; + } + else if (lt == PHRQ_io::LT_OPTION) + { + std::string::iterator opt_ptr = m_line.begin(); + std::string::iterator end = m_line.end(); + copy_token(option, opt_ptr, end); + if (find_option(option.substr(1), &opt, opt_list, false) == FT_OK) + { + // replace -option with option + j = opt; + m_line_save.replace(m_line_save.find(option), option.size(), + opt_list[opt]); + m_line.replace(m_line.find(option), option.size(), opt_list[opt]); + + // reset iss + m_line_iss.str(m_line); + m_line_iss.seekg(0, std::ios_base::beg); + m_line_iss.clear(); + + pos_ptr = 0; + copy_token(option, pos_ptr); + next_pos = pos_ptr; + //if (this->echo_file) // pr.echo_input == TRUE + if (false) + { + if (true) // database_file == NULL + { + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + io->output_msg(msg.str().c_str()); + } + } + } + else + { + if (flag_error) // don`t throw error + { + if (true) // (database_file == NULL) + { + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + io->output_msg(msg.str().c_str()); + } + error_msg("Unknown option.", PHRQ_io::OT_CONTINUE); + error_msg(m_line_save.c_str(), PHRQ_io::OT_CONTINUE); + incr_input_error(); + } + j = OPT_ERROR; + next_pos = pos_ptr; + } + } + else + { + pos_ptr = 0; + copy_token(option, pos_ptr); + if (find_option(option, &opt, opt_list, true) == FT_OK) + { + j = opt; + next_pos = pos_ptr; + } + else + { + j = OPT_DEFAULT; + next_pos = 0; + } + if (true) // pr.echo_input == TRUE + { + if (true) // database_file == NULL + { + std::ostringstream msg; + msg << "\t" << m_line_save << "\n"; + io->output_msg(msg.str().c_str()); + } + } + } + return (j); +} +int CParser:: +incr_input_error() +{ + return ++m_input_error; +} + +CParser::TOKEN_TYPE CParser::copy_title(std::string & token, + std::string::iterator & begin, + std::string::iterator & end) +{ + if (begin != end) + { + std::string::iterator b = begin; + std::string::iterator e = end; + for (; b < end && (::isspace(*b) || (*b == ',')); ++b); + begin = b; + if (*begin == '"') + { + begin = ++b; + for (; begin != end && !(*begin == '"'); ++begin); + e = begin; + if (begin != end && *begin == '"') + { + e = begin++; + } + } + else if (*begin == '\'') + { + begin = ++b; + for (; begin != end && !(*begin == '\''); ++begin); + e = begin; + if (begin != end && *begin == '\'') + { + e = begin++; + } + } + else + { + for (; begin < end && !(*begin == ',') && !(::isspace(*begin)); ++begin); + e = begin; + } + token.assign(b, e); + } + else + { + token.resize(0); + } + token = trim(token); + return token_type(token); +} +bool CParser::get_true_false(std::istream::pos_type & pos, bool def) +{ + std::string token; + this->copy_token(token, pos); + std::string::iterator b = token.begin(); + for (; b != token.end() && (::isspace(*b)); ++b); + if (b != token.end()) + { + if (*b == 'f' || *b == 'F') + { + return false; + } + else if (*b == 't' || *b == 'T') + { + return true; + } + } + return def; +} +CParser::TOKEN_TYPE CParser::get_rest_of_line(std::string &token) +{ + token.clear(); + int j; + while ((j = m_line_iss.get()) != std::char_traits < char >::eof()) + { + char c = (char) j; + token += c; + } + token = trim(token); + return token_type(token); +} +CParser::TOKEN_TYPE CParser::parse_delimited(std::string & source, std::string & result, + const std::string& t = " \t") +{ + + size_t pos = source.find_first_of(t); + std::string temp; + if (pos != std::string::npos) + { + result = source.substr(0, pos); + temp = source.substr(pos+1); + source = temp; + } + else + { + result = source; + source.clear(); + } + std::string str = result; + + return token_type(trim_left(str)); +} \ No newline at end of file diff --git a/phreeqcpp/common/Parser.h b/phreeqcpp/common/Parser.h new file mode 100644 index 00000000..3bf093a1 --- /dev/null +++ b/phreeqcpp/common/Parser.h @@ -0,0 +1,296 @@ +#if !defined(PARSER_H_INCLUDED) +#define PARSER_H_INCLUDED +#if defined(WIN32) +#include +#endif +#include // std::string +#include // std::map +#include // std::vector +#include // std::istringstream std::ostringstream +#include // std::ostream +#include // std::istream +#include // std::isspace +#include // std::find_if +#include // std::ptr_fun std::not1 + +#include "PHRQ_base.h" +#include "Keywords.h" +#include "PHRQ_io.h" + +#ifdef _DEBUG +#define isspace(a) isspace((a) < -1 ? (a) + 256 : (a)) +#define isupper(a) isupper((a) < -1 ? (a) + 256 : (a)) +#define islower(a) islower((a) < -1 ? (a) + 256 : (a)) +#define isdigit(a) isdigit((a) < -1 ? (a) + 256 : (a)) +#define isalpha(a) isalpha((a) < -1 ? (a) + 256 : (a)) +#endif + +class CParser: public PHRQ_base +{ + public: + CParser(PHRQ_io *io=NULL); + CParser(std::istream & input, PHRQ_io *io=NULL); + //CParser(std::istream & input, std::ostream & output, PHRQ_io *io=NULL); + //CParser(std::istream & input, std::ostream & output, + // std::ostream & error, PHRQ_io *io=NULL); + + virtual ~ CParser(); + enum TOKEN_TYPE + { + TT_EMPTY = 2, + TT_UPPER = 4, + TT_LOWER = 5, + TT_DIGIT = 6, + TT_UNKNOWN = 7 + }; + + enum FIND_TYPE + { + FT_OK = 0, + FT_ERROR = 1 + }; + + enum OPT_TYPE + { + OPT_DEFAULT = -4, + OPT_ERROR = -3, + OPT_KEYWORD = -2, + OPT_EOF = -1 + }; + enum ECHO_OPTION + { + EO_NONE = 0, + EO_ALL = 1, + EO_KEYWORDS = 2, + EO_NOKEYWORDS = 3 + }; + + enum STATUS_TYPE + { + PARSER_ERROR = 0, + PARSER_OK = 1 + }; + + + /** + Function gets a new line and checks for empty, eof, and keywords. + + Arguments: + string Input, character string used in printing error message + allow_empty Input, True or false, if a blank line is accepable + if false, another line is read + allow_eof Input, True or false, if EOF is acceptable + allow_keyword Input, True or false, if a keyword is acceptable + + Returns: + LT_EMPTY if empty line read and allow_empty == true + LT_KEYWORD if line begins with keyword + LT_EOF if eof and allow_eof == true + LT_OK otherwise + LT_OPTION if line begins with -[alpha] + + Terminates if EOF and allow_eof == false. + */ + PHRQ_io::LINE_TYPE check_line(const std::string & str, bool allow_empty, + bool allow_eof, bool allow_keyword, bool print); + + /** + Read a line from input file put in "line". + Copy of input line is stored in "line_save". + Characters after # are discarded in line but retained in "line_save" + + Arguments: + None + Returns: + LT_EMPTY, + LT_EOF, + LT_KEYWORD, + LT_OK, + LT_OPTION + */ + PHRQ_io::LINE_TYPE get_line(); + PHRQ_io::LINE_TYPE get_line_phrq_io(); + + // bool check_key(const std::string::iterator ptr); + bool check_key(std::string::iterator begin, std::string::iterator end); + + STATUS_TYPE check_units(std::string & tot_units, bool alkalinity, + bool check_compatibility, + const std::string & default_units, bool print); + + + //KEY_TYPE next_keyword() const + Keywords::KEYWORDS next_keyword() const + { + return m_next_keyword; + } + int get_option(const std::vector < std::string > &opt_list, + std::string::iterator & next_char); + int get_option(const std::vector < std::string > &opt_list, + std::istream::pos_type & next_pos); + int getOptionFromLastLine(const std::vector < std::string > &opt_list, + std::string::iterator & next_char, bool flag_error); + int getOptionFromLastLine(const std::vector < std::string > &opt_list, + std::istream::pos_type & next_pos, bool flag_error); + + + std::string & line() + { + return m_line; + } + std::string & line_save() + { + return m_line_save; + } + std::string & get_accumulated() + { + return accumulated; + } + void set_accumulate(bool tf) + { + if (tf) + { + accumulated.clear(); + } + this->accumulate = tf; + } + std::istringstream & get_iss() + { + return m_line_iss; + } + int incr_input_error(); + int get_input_error() + { + return m_input_error; + } + + /** + Copies from begin to token until first space is encountered. + + Arguments: + token output, the token + begin input, begin iterator + end input, end iterator + + Returns: + TT_EMPTY + TT_UPPER + TT_LOWER + TT_DIGIT + TT_UNKNOWN + */ + static CParser::TOKEN_TYPE copy_token(std::string & token, + std::string::iterator & begin, + std::string::iterator & end); + static CParser::TOKEN_TYPE copy_title(std::string & token, + std::string::iterator & begin, + std::string::iterator & end); + static CParser::TOKEN_TYPE token_type(const std::string & token); + static CParser::TOKEN_TYPE copy_token(std::string & token, std::istream & is); + CParser::TOKEN_TYPE copy_token(std::string & token, std::istream::pos_type & pos); + bool get_true_false(std::istream::pos_type & pos, bool def); + CParser::TOKEN_TYPE get_rest_of_line(std::string &token); + static CParser::TOKEN_TYPE parse_delimited(std::string & source, std::string & result, const std::string& t); + CParser::TOKEN_TYPE peek_token(); + PHRQ_io::LINE_TYPE get_m_line_type(void) const {return this->m_line_type;} + + /** + Function reads an element name out of the equation string. + An element name is composed of a capital letter followed by any number + of lower case characters. + + Arguments: + begin input, points to position in the equation to begin + output, points to next character of equation after + element name. + end input, points to last position in the equation + element input pointer to place to return element character string + */ + STATUS_TYPE get_elt(std::string::iterator & begin, + const std::string::iterator end, + std::string & element); + + + /** + Compares a string value to match beginning letters of a list of options + + Arguments: + item entry: pointer to string to compare + n exit: item in list that was matched + list entry: pointer to list of character values, assumed to + be lower case + count_list entry: number of character values in list + + Returns: + OK item matched + ERROR item not matched + n -1 item not matched + i position of match in list + */ + static FIND_TYPE find_option(const std::string & item, int *n, + const std::vector < std::string > &list, + bool exact); + + void set_echo_file(ECHO_OPTION opt) + { + echo_file = opt; + } + ECHO_OPTION get_echo_file() + { + return this->echo_file; + }; + + void set_echo_stream(ECHO_OPTION opt) + { + echo_stream = opt; + } + ECHO_OPTION get_echo_stream() + { + return this->echo_stream; + }; + + STATUS_TYPE parse_couple(std::string & token); + + template + STATUS_TYPE addPair(std::map < std::string, T >&totals, + std::istream::pos_type & pos); + + protected: + PHRQ_io::LINE_TYPE get_logical_line(); + + protected: + std::istream & m_input_stream; + //std::ostream & m_output_stream; + //std::ostream & m_error_stream; + int m_input_error; + //KEY_TYPE m_next_keyword; + Keywords::KEYWORDS m_next_keyword; + std::string m_line; + std::string m_line_save; + std::istringstream m_line_iss; + PHRQ_io::LINE_TYPE m_line_type; + ECHO_OPTION echo_stream; + ECHO_OPTION echo_file; + std::string accumulated; + bool accumulate; + bool phrq_io_only; + +}; + +// Global functions +static inline std::string &trim_left(std::string &s) +{ + s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(std::isspace)))); + return s; +} +static inline std::string &trim_right(std::string &s) +{ + s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); + return s; +} +static inline std::string &trim(std::string &s) +{ + return trim_left(trim_right(s)); +} + +#endif // PARSER_H_INCLUDED diff --git a/phreeqcpp/common/Utils.cxx b/phreeqcpp/common/Utils.cxx new file mode 100644 index 00000000..453218d3 --- /dev/null +++ b/phreeqcpp/common/Utils.cxx @@ -0,0 +1,186 @@ +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // ::tolower +#include // ::tolower +#include // std::transform +#include // std::cout std::cerr +#include + +#include "Utils.h" +#include "Parser.h" +#include "float.h" +#include "math.h" + +//////////////////////////////////////////////////////////////////////////// +int +Utilities::strcmp_nocase_arg1(const char *str1, const char *str2) +//////////////////////////////////////////////////////////////////////////// +{ + // + // Compare two strings disregarding case + // + int c1, c2; + while ((c1 =::tolower(*str1++)) == (c2 = *str2++)) + { + if (c1 == '\0') + return (0); + } + if (c1 < c2) + return (-1); + return (1); +} + +//////////////////////////////////////////////////////////////////////////// +int +Utilities::strcmp_nocase(const char *str1, const char *str2) +//////////////////////////////////////////////////////////////////////////// +{ + // + // Compare two strings disregarding case + // + int c1, c2; + while ((c1 =::tolower(*str1++)) == (c2 =::tolower(*str2++))) + { + if (c1 == '\0') + return (0); + } + if (c1 < c2) + return (-1); + return (1); +} + +//////////////////////////////////////////////////////////////////////////// +void +Utilities::str_tolower(std::string & str) +//////////////////////////////////////////////////////////////////////////// +{ + std::transform(str.begin(), str.end(), str.begin(), tolower); +} + +//////////////////////////////////////////////////////////////////////////// +void +Utilities::str_toupper(std::string & str) +//////////////////////////////////////////////////////////////////////////// +{ + std::transform(str.begin(), str.end(), str.begin(), toupper); +} +//////////////////////////////////////////////////////////////////////////// +std::string +Utilities::pad_right(const std::string & str, size_t l) +//////////////////////////////////////////////////////////////////////////// +{ + std::string new_str(str); + size_t length = new_str.size(); + if (length < l) + { + new_str = new_str.insert(length, l - length, ' '); + } + return new_str; +} + + +//////////////////////////////////////////////////////////////////////////// +bool +Utilities::replace(const char *str1, const char *str2, std::string & str) +//////////////////////////////////////////////////////////////////////////// +{ + std::string::size_type n = str.find(str1, 0); + if (n == std::string::npos) + return false; + + str.replace(n, ::strlen(str1), str2); + return true; +} + +//////////////////////////////////////////////////////////////////////////// +void +Utilities::squeeze_white(std::string & s_l) +//////////////////////////////////////////////////////////////////////////// +{ + std::string str; + std::string::iterator beg = s_l.begin(); + std::string::iterator end = s_l.end(); + //CParser::copy_token(str, beg, end); + std::string::iterator pos; + for (pos = beg; pos != end; pos++) + { + int c = *pos; + if (!::isspace(c)) + { + str += c; + } + } + s_l = str; +} +//////////////////////////////////////////////////////////////////////////// +double +Utilities::convert_time(double t, std::string in, std::string out) +//////////////////////////////////////////////////////////////////////////// +{ + Utilities::str_tolower(in); + + // convert t to seconds + if (in.substr(0,1) == "m") + { + t = t * 60.; + } + if (in.substr(0,1) == "h") + { + t = t * 3600.; + } + if (in.substr(0,1) == "d") + { + t = t * 3600. * 24.; + } + if (in.substr(0,1) == "y") + { + t = t * 3600. * 24. * 365.25; + } + // convert to ouput units + if (out.substr(0,1) == "m") + { + t = t / 60.; + } + if (out.substr(0,1) == "h") + { + t = t / 3600.; + } + if (out.substr(0,1) == "d") + { + t = t / ( 3600. * 24.); + } + if (out.substr(0,1) == "y") + { + t = t / (3600. * 24. * 365.25); + } + return t; + +} +LDBLE +Utilities::safe_exp(LDBLE t) +//////////////////////////////////////////////////////////////////////////// +{ + LDBLE f = 1.442695*t; // convert to exp for 2.0 + + if (f > DBL_MAX_EXP - 50.0) + { + return pow(2, DBL_MAX_EXP - 50.0); + } + else if (f < DBL_MIN_EXP + 50.0) + { + return pow(2, DBL_MIN_EXP + 50.0); + } + return exp(t); +} +//+NAN LDBLE: 7ff8000000000000 +//-NAN LDBLE: fff8000000000000 +/* +LDBLE Utilities::get_nan(void) +{ + unsigned long long raw = 0x7ff0000000000000; + LDBLE d = *( LDBLE* )&raw; + return(d); + +} +*/ diff --git a/phreeqcpp/common/Utils.h b/phreeqcpp/common/Utils.h new file mode 100644 index 00000000..74997731 --- /dev/null +++ b/phreeqcpp/common/Utils.h @@ -0,0 +1,28 @@ +#if !defined(UTILITIES_H_INCLUDED) +#define UTILITIES_H_INCLUDED + +#include +#include // std::istringstream std::ostringstream +#include // std::ostream +#include // std::istream +#include // std::map +#include "phrqtype.h" +namespace Utilities +{ + + const char INDENT[] = " "; + + int strcmp_nocase(const char *str1, const char *str2); + + int strcmp_nocase_arg1(const char *str1, const char *str2); + + void str_tolower(std::string & str); + void str_toupper(std::string & str); + std::string pad_right(const std::string & str, size_t l); + bool replace(const char *str1, const char *str2, std::string & str); + + void squeeze_white(std::string & s_l); + double convert_time(double t, std::string in, std::string out); + LDBLE safe_exp(LDBLE t); +} +#endif // UTILITIES_H_INCLUDED diff --git a/phreeqcpp/common/phrqtype.h b/phreeqcpp/common/phrqtype.h new file mode 100644 index 00000000..ab7160b5 --- /dev/null +++ b/phreeqcpp/common/phrqtype.h @@ -0,0 +1,18 @@ +#ifndef _INC_PHRQTYPE_H +#define _INC_PHRQTYPE_H +/* + * The following implements long double + * Many machines long double = double so there is no advantage + * Check float.h include file for number of digits (LDBL_DIG) + * Need to define here and in cl1.c + */ + +/*#define USE_LONG_DOUBLE*/ +#ifdef USE_LONG_DOUBLE +#define LDBLE long double +#define SCANFORMAT "%Lf" +#else +#define LDBLE double +#define SCANFORMAT "%lf" +#endif +#endif /* _INC_PHRQTYPE_H */ diff --git a/phreeqcpp/cvdense.cpp b/phreeqcpp/cvdense.cpp new file mode 100644 index 00000000..330ef02b --- /dev/null +++ b/phreeqcpp/cvdense.cpp @@ -0,0 +1,557 @@ +/************************************************************************** + * * + * File : cvdense.c * + * Programmers : Scott D. Cohen, Alan C. Hindmarsh, and * + * Radu Serban @ LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the implementation file for the CVODE dense linear * + * solver, CVDENSE. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ + + +#include +#include +#include +#include "cvdense.h" +#include "cvode.h" +#include "dense.h" +#include "sundialstypes.h" +#include "nvector.h" +#include "sundialsmath.h" + + +#include "Phreeqc.h" +#if !defined(WIN32_MEMORY_DEBUG) +#define malloc PHRQ_malloc +#endif +#define MACHENV machEnv->phreeqc_ptr-> +#define CVMEM cv_mem->cv_machenv->phreeqc_ptr-> +#define MACHENV_MALLOC MACHENV +#define CVMEM_MALLOC CVMEM + +#include "phqalloc.h" +/* WARNING don`t include any headers below here */ + +/* Error Messages */ + +#define CVDENSE "CVDense/CVReInitDense-- " + +#define MSG_CVMEM_NULL CVDENSE "CVode Memory is NULL.\n\n" + +#define MSG_MEM_FAIL CVDENSE "A memory request failed.\n\n" + +#define MSG_WRONG_NVEC CVDENSE "Incompatible NVECTOR implementation.\n\n" + +/* Other Constants */ + +#define MIN_INC_MULT RCONST(1000.0) +#define ZERO RCONST(0.0) +#define ONE RCONST(1.0) +#define TWO RCONST(2.0) + + +/****************************************************************** + * * + * Types : CVDenseMemRec, CVDenseMem * + *----------------------------------------------------------------* + * The type CVDenseMem is pointer to a CVDenseMemRec. This * + * structure contains CVDense solver-specific data. * + * * + ******************************************************************/ + +typedef struct +{ + + CVDenseJacFn d_jac; /* jac = Jacobian routine to be called */ + + DenseMat d_M; /* M = I - gamma J, gamma = h / l1 */ + + integertype *d_pivots; /* pivots = pivot array for PM = LU */ + + DenseMat d_savedJ; /* savedJ = old Jacobian */ + + long int d_nstlj; /* nstlj = nst at last Jacobian eval. */ + + long int d_nje; /* nje = no. of calls to jac */ + + void *d_J_data; /* J_data is passed to jac */ + +} CVDenseMemRec, *CVDenseMem; + + +/* CVDENSE linit, lsetup, lsolve, lfree, and DQJac routines */ + +static int CVDenseInit(CVodeMem cv_mem); + +static int CVDenseSetup(CVodeMem cv_mem, int convfail, N_Vector ypred, + N_Vector fpred, booleantype * jcurPtr, + N_Vector vtemp1, N_Vector vtemp2, N_Vector vtemp3); + +static int CVDenseSolve(CVodeMem cv_mem, N_Vector b, N_Vector ycur, + N_Vector fcur); + +static void CVDenseFree(CVodeMem cv_mem); + +static void CVDenseDQJac(integertype N, DenseMat J, RhsFn f, void *f_data, + realtype t, N_Vector y, N_Vector fy, N_Vector ewt, + realtype h, realtype uround, void *jac_data, + long int *nfePtr, N_Vector vtemp1, + N_Vector vtemp2, N_Vector vtemp3); + + +/*************** CVDenseDQJac **************************************** + + This routine generates a dense difference quotient approximation to + the Jacobian of f(t,y). It assumes that a dense matrix of type + DenseMat is stored column-wise, and that elements within each column + are contiguous. The address of the jth column of J is obtained via + the macro DENSE_COL and an N_Vector with the jth column as the + component array is created using N_VMake and N_VSetData. Finally, the + actual computation of the jth column of the Jacobian is done with a + call to N_VLinearSum. + +**********************************************************************/ + +static void +CVDenseDQJac(integertype N, DenseMat J, RhsFn f, void *f_data, + realtype tn, N_Vector y, N_Vector fy, N_Vector ewt, + realtype h, realtype uround, void *jac_data, + long int *nfePtr, N_Vector vtemp1, + N_Vector vtemp2, N_Vector vtemp3) +{ + realtype fnorm, minInc, inc, inc_inv, yjsaved, srur; + realtype *y_data, *ewt_data; + N_Vector ftemp, jthCol; + M_Env machEnv; + integertype j; + + machEnv = y->menv; /* Get machine environment */ + + ftemp = vtemp1; /* Rename work vector for use as f vector value */ + + /* Obtain pointers to the data for ewt, y */ + ewt_data = N_VGetData(ewt); + y_data = N_VGetData(y); + + /* Set minimum increment based on uround and norm of f */ + srur = RSqrt(uround); + fnorm = N_VWrmsNorm(fy, ewt); + minInc = (fnorm != ZERO) ? + (MIN_INC_MULT * ABS(h) * uround * N * fnorm) : ONE; + + jthCol = N_VMake(N, y_data, machEnv); /* j loop overwrites this data address */ + + /* This is the only for loop for 0..N-1 in CVODE */ + for (j = 0; j < N; j++) + { + + /* Generate the jth col of J(tn,y) */ + + N_VSetData(DENSE_COL(J, j), jthCol); + yjsaved = y_data[j]; + inc = MAX(srur * ABS(yjsaved), minInc / ewt_data[j]); + y_data[j] += inc; + f(N, tn, y, ftemp, f_data); + inc_inv = ONE / inc; + N_VLinearSum(inc_inv, ftemp, -inc_inv, fy, jthCol); + y_data[j] = yjsaved; + } + + N_VDispose(jthCol); + + /* Increment counter nfe = *nfePtr */ + *nfePtr += N; +} + + +/* Readability Replacements */ + +#define N (cv_mem->cv_N) +#define lmm (cv_mem->cv_lmm) +#define f (cv_mem->cv_f) +#define f_data (cv_mem->cv_f_data) +#define uround (cv_mem->cv_uround) +#define nst (cv_mem->cv_nst) +#define tn (cv_mem->cv_tn) +#define h (cv_mem->cv_h) +#define gamma (cv_mem->cv_gamma) +#define gammap (cv_mem->cv_gammap) +#define gamrat (cv_mem->cv_gamrat) +#define ewt (cv_mem->cv_ewt) +#define nfe (cv_mem->cv_nfe) +#define errfp (cv_mem->cv_errfp) +#define iopt (cv_mem->cv_iopt) +#define linit (cv_mem->cv_linit) +#define lsetup (cv_mem->cv_lsetup) +#define lsolve (cv_mem->cv_lsolve) +#define lfree (cv_mem->cv_lfree) +#define lmem (cv_mem->cv_lmem) +#define setupNonNull (cv_mem->cv_setupNonNull) +#define machenv (cv_mem->cv_machenv) + +#define jac (cvdense_mem->d_jac) +#define M (cvdense_mem->d_M) +#define pivots (cvdense_mem->d_pivots) +#define savedJ (cvdense_mem->d_savedJ) +#define nstlj (cvdense_mem->d_nstlj) +#define nje (cvdense_mem->d_nje) +#define J_data (cvdense_mem->d_J_data) + + +/*************** CVDense ********************************************* + + This routine initializes the memory record and sets various function + fields specific to the dense linear solver module. CVDense first + calls the existing lfree routine if this is not NULL. Then it sets + the cv_linit, cv_lsetup, cv_lsolve, cv_lfree fields in (*cvode_mem) + to be CVDenseInit, CVDenseSetup, CVDenseSolve, and CVDenseFree, + respectively. It allocates memory for a structure of type + CVDenseMemRec and sets the cv_lmem field in (*cvode_mem) to the + address of this structure. It sets setupNonNull in (*cvode_mem) to + TRUE, the d_J_data field in CVDenseMemRec to be the input + parameter jac_data, and the d_jac field to be: + (1) the input parameter djac if djac != NULL or + (2) CVDenseDQJac if djac == NULL. + Finally, it allocates memory for M, savedJ, and pivots. + The return value is SUCCESS = 0, or LMEM_FAIL = -1. + + NOTE: The dense linear solver assumes a serial implementation + of the NVECTOR package. Therefore, CVDense will first + test for compatible a compatible N_Vector internal + representation by checking (1) the machine environment + ID tag and (2) that the functions N_VMake, N_VDispose, + N_VGetData, and N_VSetData are implemented. + +**********************************************************************/ + +int +CVDense(void *cvode_mem, CVDenseJacFn djac, void *jac_data) +{ + CVodeMem cv_mem; + CVDenseMem cvdense_mem; + + /* Return immediately if cvode_mem is NULL */ + cv_mem = (CVodeMem) cvode_mem; + if (cv_mem == NULL) + { /* CVode reports this error */ + return (LMEM_FAIL); + } + + /* Test if the NVECTOR package is compatible with the DENSE solver */ + if ((strcmp(machenv->tag, "serial")) || + machenv->ops->nvmake == NULL || + machenv->ops->nvdispose == NULL || + machenv->ops->nvgetdata == NULL || machenv->ops->nvsetdata == NULL) + { + CVMEM warning_msg( MSG_WRONG_NVEC); + return (LMEM_FAIL); + } + + if (lfree != NULL) + lfree(cv_mem); + + /* Set four main function fields in cv_mem */ + linit = CVDenseInit; + lsetup = CVDenseSetup; + lsolve = CVDenseSolve; + lfree = CVDenseFree; + + /* Get memory for CVDenseMemRec */ +#if defined(WIN32_MEMORY_DEBUG) + lmem = cvdense_mem = (CVDenseMem) malloc(sizeof(CVDenseMemRec)); +#else + lmem = cvdense_mem = (CVDenseMem) CVMEM_MALLOC malloc(sizeof(CVDenseMemRec)); +#endif + if (cvdense_mem == NULL) + { + CVMEM warning_msg( MSG_MEM_FAIL); + return (LMEM_FAIL); + } + + /* Set Jacobian routine field, J_data, and setupNonNull */ + if (djac == NULL) + { + jac = CVDenseDQJac; + } + else + { + jac = djac; + } + J_data = jac_data; + setupNonNull = TRUE; + + /* Allocate memory for M, savedJ, and pivot array */ + + M = DenseAllocMat(N); + if (M == NULL) + { + CVMEM warning_msg( MSG_MEM_FAIL); + return (LMEM_FAIL); + } + savedJ = DenseAllocMat(N); + if (savedJ == NULL) + { + CVMEM warning_msg( MSG_MEM_FAIL); + DenseFreeMat(M); + return (LMEM_FAIL); + } + pivots = DenseAllocPiv(N); + if (pivots == NULL) + { + CVMEM warning_msg( MSG_MEM_FAIL); + DenseFreeMat(M); + DenseFreeMat(savedJ); + return (LMEM_FAIL); + } + + return (SUCCESS); +} + +/*************** CVReInitDense**************************************** + + This routine resets the link between the main CVODE module and the + dense linear solver module CVDENSE. No memory freeing or allocation + operations are done, as the existing linear solver memory is assumed + sufficient. All other initializations are the same as in CVDense. + The return value is SUCCESS = 0, or LMEM_FAIL = -1. + +**********************************************************************/ + +int +CVReInitDense(void *cvode_mem, CVDenseJacFn djac, void *jac_data) +{ + CVodeMem cv_mem; + CVDenseMem cvdense_mem; + + /* Return immediately if cvode_mem is NULL */ + cv_mem = (CVodeMem) cvode_mem; + if (cv_mem == NULL) + { /* CVode reports this error */ + //CVMEM warning_msg( MSG_CVMEM_NULL); +#if !defined(R_SO) + std::cerr << MSG_CVMEM_NULL << std::endl; +#endif + return (LMEM_FAIL); + } + + /* Test if the NVECTOR package is compatible with the DENSE solver */ + if ((strcmp(machenv->tag, "serial")) || + machenv->ops->nvmake == NULL || + machenv->ops->nvdispose == NULL || + machenv->ops->nvgetdata == NULL || machenv->ops->nvsetdata == NULL) + { + CVMEM warning_msg( MSG_WRONG_NVEC); + return (LMEM_FAIL); + } + + cvdense_mem = (CVDenseMem) lmem; /* Use existing linear solver memory pointer */ + + /* Set four main function fields in cv_mem */ + linit = CVDenseInit; + lsetup = CVDenseSetup; + lsolve = CVDenseSolve; + lfree = CVDenseFree; + + /* Set Jacobian routine field, J_data, and setupNonNull */ + if (djac == NULL) + { + jac = CVDenseDQJac; + } + else + { + jac = djac; + } + J_data = jac_data; + setupNonNull = TRUE; + + return (SUCCESS); +} + +/*************** CVDenseInit ***************************************** + + This routine does remaining initializations specific to the dense + linear solver. + +**********************************************************************/ + +static int +CVDenseInit(CVodeMem cv_mem) +{ + CVDenseMem cvdense_mem; + cvdense_mem = (CVDenseMem) lmem; + + /* Initialize nje and nstlj, and set workspace lengths */ + + nje = 0; + if (iopt != NULL) + { + iopt[DENSE_NJE] = nje; + iopt[DENSE_LRW] = 2 * N * N; + iopt[DENSE_LIW] = N; + } + nstlj = 0; + + return (LINIT_OK); +} + +/*************** CVDenseSetup **************************************** + + This routine does the setup operations for the dense linear solver. + It makes a decision whether or not to call the Jacobian evaluation + routine based on various state variables, and if not it uses the + saved copy. In any case, it constructs the Newton matrix + M = I - gamma*J, updates counters, and calls the dense LU + factorization routine. + +**********************************************************************/ + +static int +CVDenseSetup(CVodeMem cv_mem, int convfail, N_Vector ypred, + N_Vector fpred, booleantype * jcurPtr, + N_Vector vtemp1, N_Vector vtemp2, N_Vector vtemp3) +{ + booleantype jbad, jok; + realtype dgamma; + integertype ier; + CVDenseMem cvdense_mem; + + cvdense_mem = (CVDenseMem) lmem; + + /* Use nst, gamma/gammap, and convfail to set J eval. flag jok */ + + dgamma = ABS((gamma / gammap) - ONE); + jbad = (nst == 0) || (nst > nstlj + CVD_MSBJ) || + ((convfail == FAIL_BAD_J) && (dgamma < CVD_DGMAX)) || + (convfail == FAIL_OTHER); + jok = !jbad; + + if (jok) + { + /* If jok = TRUE, use saved copy of J */ + *jcurPtr = FALSE; + DenseCopy(savedJ, M); + } + else + { + /* If jok = FALSE, call jac routine for new J value */ + nje++; + if (iopt != NULL) + iopt[DENSE_NJE] = nje; + nstlj = nst; + *jcurPtr = TRUE; + DenseZero(M); + jac(N, M, f, f_data, tn, ypred, fpred, ewt, h, + uround, J_data, &nfe, vtemp1, vtemp2, vtemp3); + DenseCopy(M, savedJ); + } + + /* Scale and add I to get M = I - gamma*J */ + DenseScale(-gamma, M); + DenseAddI(M); + + /* Do LU factorization of M */ + ier = DenseFactor(M, pivots); + + /* Return 0 if the LU was complete; otherwise return 1 */ + if (ier > 0) + return (1); + return (0); +} + +/*************** CVDenseSolve **************************************** + + This routine handles the solve operation for the dense linear solver + by calling the dense backsolve routine. The returned value is 0. + +**********************************************************************/ + +static int +CVDenseSolve(CVodeMem cv_mem, N_Vector b, N_Vector ycur, N_Vector fcur) +{ + CVDenseMem cvdense_mem; + realtype *bd; + + cvdense_mem = (CVDenseMem) lmem; + + bd = N_VGetData(b); + DenseBacksolve(M, pivots, bd); + N_VSetData(bd, b); + + /* If BDF, scale the correction to account for change in gamma */ + if ((lmm == BDF) && (gamrat != ONE)) + { + N_VScale(TWO / (ONE + gamrat), b, b); + } + + return (0); +} + +/*************** CVDenseFree ***************************************** + + This routine frees memory specific to the dense linear solver. + +**********************************************************************/ + +static void +CVDenseFree(CVodeMem cv_mem) +{ + CVDenseMem cvdense_mem; + + cvdense_mem = (CVDenseMem) lmem; + + DenseFreeMat(M); + DenseFreeMat(savedJ); + DenseFreePiv(pivots); + CVMEM_MALLOC PHRQ_free(cvdense_mem); +} diff --git a/phreeqcpp/cvdense.h b/phreeqcpp/cvdense.h new file mode 100644 index 00000000..cd2bcc25 --- /dev/null +++ b/phreeqcpp/cvdense.h @@ -0,0 +1,267 @@ +#ifndef _INC_CVDENSE_H +#define _INC_CVDENSE_H +/************************************************************************** + * * + * File : cvdense.h * + * Programmers : Scott D. Cohen, Alan C. Hindmarsh, and * + * Radu Serban @ LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the header file for the CVODE dense linear solver, * + * CVDENSE. * + * * + * Note: The type integertype must be large enough to store the * + * value of the linear system size N. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ + +#ifndef _cvdense_h +#define _cvdense_h + + +#include +#include "cvode.h" +#include "sundialstypes.h" +#include "dense.h" +#include "nvector.h" + + +/****************************************************************** + * * + * CVDENSE solver statistics indices * + *----------------------------------------------------------------* + * The following enumeration gives a symbolic name to each * + * CVDENSE statistic. The symbolic names are used as indices into * + * the iopt and ropt arrays passed to CVodeMalloc. * + * The CVDENSE statistics are: * + * * + * iopt[DENSE_NJE] : number of Jacobian evaluations, i.e. of * + * calls made to the dense Jacobian routine * + * (default or user-supplied). * + * * + * iopt[DENSE_LRW] : size (in realtype words) of real workspace * + * matrices and vectors used by this solver. * + * * + * iopt[DENSE_LIW] : size (in integertype words) of integer * + * workspace vectors used by this solver. * + * * + ******************************************************************/ + + enum + { DENSE_NJE = CVODE_IOPT_SIZE, DENSE_LRW, DENSE_LIW }; + + +/****************************************************************** + * * + * CVDENSE solver constants * + *----------------------------------------------------------------* + * CVD_MSBJ : maximum number of steps between dense Jacobian * + * evaluations * + * * + * CVD_DGMAX : maximum change in gamma between dense Jacobian * + * evaluations * + * * + ******************************************************************/ + +#define CVD_MSBJ 50 + +#define CVD_DGMAX RCONST(0.2) + + +/****************************************************************** + * * + * Type : CVDenseJacFn * + *----------------------------------------------------------------* + * A dense Jacobian approximation function Jac must have the * + * prototype given below. Its parameters are: * + * * + * N is the length of all vector arguments. * + * * + * J is the dense matrix (of type DenseMat) that will be loaded * + * by a CVDenseJacFn with an approximation to the Jacobian matrix * + * J = (df_i/dy_j) at the point (t,y). * + * J is preset to zero, so only the nonzero elements need to be * + * loaded. Two efficient ways to load J are: * + * * + * (1) (with macros - no explicit data structure references) * + * for (j=0; j < N; j++) { * + * col_j = DENSE_COL(J,j); * + * for (i=0; i < N; i++) { * + * generate J_ij = the (i,j)th Jacobian element * + * col_j[i] = J_ij; * + * } * + * } * + * * + * (2) (without macros - explicit data structure references) * + * for (j=0; j < N; j++) { * + * col_j = (J->data)[j]; * + * for (i=0; i < N; i++) { * + * generate J_ij = the (i,j)th Jacobian element * + * col_j[i] = J_ij; * + * } * + * } * + * * + * The DENSE_ELEM(A,i,j) macro is appropriate for use in small * + * problems in which efficiency of access is NOT a major concern. * + * * + * f is the right hand side function for the ODE problem. * + * * + * f_data is a pointer to user data to be passed to f, the same * + * as the F_data parameter passed to CVodeMalloc. * + * * + * t is the current value of the independent variable. * + * * + * y is the current value of the dependent variable vector, * + * namely the predicted value of y(t). * + * * + * fy is the vector f(t,y). * + * * + * ewt is the error weight vector. * + * * + * h is a tentative step size in t. * + * * + * uround is the machine unit roundoff. * + * * + * jac_data is a pointer to user data - the same as the jac_data * + * parameter passed to CVDense. * + * * + * nfePtr is a pointer to the memory location containing the * + * CVODE problem data nfe = number of calls to f. The Jacobian * + * routine should update this counter by adding on the number * + * of f calls made in order to approximate the Jacobian, if any. * + * For example, if the routine calls f a total of N times, then * + * the update is *nfePtr += N. * + * * + * vtemp1, vtemp2, and vtemp3 are pointers to memory allocated * + * for vectors of length N which can be used by a CVDenseJacFn * + * as temporary storage or work space. * + * * + ******************************************************************/ + + typedef void (*CVDenseJacFn) (integertype N, DenseMat J, RhsFn f, + void *f_data, realtype t, N_Vector y, + N_Vector fy, N_Vector ewt, realtype h, + realtype uround, void *jac_data, + long int *nfePtr, N_Vector vtemp1, + N_Vector vtemp2, N_Vector vtemp3); + + +/****************************************************************** + * * + * Function : CVDense * + *----------------------------------------------------------------* + * A call to the CVDense function links the main CVODE integrator * + * with the CVDENSE linear solver. * + * * + * cvode_mem is the pointer to CVODE memory returned by * + * CVodeMalloc. * + * * + * djac is the dense Jacobian approximation routine to be used. * + * A user-supplied djac routine must be of type * + * CVDenseJacFn. Pass NULL for djac to use the default * + * difference quotient routine CVDenseDQJac supplied * + * with this solver. * + * * + * jac_data is a pointer to user data which is passed to the * + * djac routine every time it is called. * + * * + * The return values of CVDense are: * + * SUCCESS = 0 if successful * + * LMEM_FAIL = -1 if there was a memory allocation failure * + * * + * NOTE: The dense linear solver assumes a serial implementation * + * of the NVECTOR package. Therefore, CVDense will first * + * test for a compatible N_Vector internal representation * + * by checking (1) the machine environment ID tag and * + * (2) that the functions N_VMake, N_VDispose, N_VGetData, * + * and N_VSetData are implemented. * + * * + ******************************************************************/ + + int CVDense(void *cvode_mem, CVDenseJacFn djac, void *jac_data); + + +/****************************************************************** + * * + * Function : CVReInitDense * + *----------------------------------------------------------------* + * A call to the CVReInitDense function resets the link between * + * the main CVODE integrator and the CVDENSE linear solver. * + * After solving one problem using CVDENSE, call CVReInit and then* + * CVReInitDense to solve another problem of the same size, if * + * there is a change in the CVDense parameters djac or jac_data. * + * If there is no change in parameters, it is not necessary to * + * call either CVReInitDense or CVDense for the new problem. * + * * + * All arguments to CVReInitDense have the same names and meanings* + * as those of CVDense. The cvode_mem argument must be identical * + * to its value in the previous CVDense call. * + * * + * The return values of CVReInitDense are: * + * SUCCESS = 0 if successful, or * + * LMEM_FAIL = -1 if the cvode_mem argument is NULL * + * * + * NOTE: CVReInitDense performs the same compatibility tests as * + * CVDense. * + * * + ******************************************************************/ + + int CVReInitDense(void *cvode_mem, CVDenseJacFn djac, void *jac_data); + + +#endif +/* +#ifdef __cplusplus +} +#endif +*/ +#endif /* _INC_CVDENSE_H */ diff --git a/phreeqcpp/cvode.cpp b/phreeqcpp/cvode.cpp new file mode 100644 index 00000000..c03a679c --- /dev/null +++ b/phreeqcpp/cvode.cpp @@ -0,0 +1,3853 @@ +/*#define DEBUG_CVODE*/ +/************************************************************************** + * * + * File : cvode.c * + * Programmers : Scott D. Cohen, Alan C. Hindmarsh, Radu Serban, * + * and Dan Shumaker @ LLNL * + * Version of : 24 July 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the implementation file for the main CVODE integrator. * + * It is independent of the CVODE linear solver in use. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ +#include "nvector_serial.h" +#define Ith(v,i) NV_Ith_S(v,i-1) +/************************************************************/ +/******************* BEGIN Imports **************************/ +/************************************************************/ +#include +#include +#include "cvode.h" +#include "sundialstypes.h" +#include "nvector.h" +#include "sundialsmath.h" + +#include "Phreeqc.h" +#if !defined(WIN32_MEMORY_DEBUG) +#define malloc MACHENV_MALLOC PHRQ_malloc +#endif +#define MACHENV machEnv->phreeqc_ptr-> +#define CVMEM cv_mem->cv_machenv->phreeqc_ptr-> +#define MACHENV_MALLOC MACHENV +#define CVMEM_MALLOC CVMEM + +#include "phqalloc.h" +/* WARNING don`t include any headers below here */ + + +/************************************************************/ +/******************** END Imports ***************************/ +/************************************************************/ + + +/***************************************************************/ +/*********************** BEGIN Macros **************************/ +/***************************************************************/ + +/* Macro: loop */ + +#define loop for(;;) + +/***************************************************************/ +/************************ END Macros ***************************/ +/***************************************************************/ + + + +/************************************************************/ +/************** BEGIN CVODE Private Constants ***************/ +/***************************************** +*******************/ + +#define FOURTH RCONST(0.25) /* real 0.25 */ +#define THREE RCONST(3.0) /* real 3.0 */ +#define FOUR RCONST(4.0) /* real 4.0 */ +#define HUN RCONST(100.0) /* real 100.0 */ +#define TINY RCONST(1.0e-10) /* small number */ +#define HALF RCONST(0.5) /* real 0.5 */ +#define ZERO RCONST(0.0) /* real 0.0 */ +#define ONE RCONST(1.0) /* real 1.0 */ +#define TWO RCONST(2.0) /* real 2.0 */ +#define TWELVE RCONST(12.0) /* real 12.0 */ + +/***************************************************************/ +/************** BEGIN Default Constants ************************/ +/***************************************************************/ + +#define HMIN_DEFAULT ZERO /* hmin default value */ +#define HMAX_INV_DEFAULT ZERO /* hmax_inv default value */ +#define MXHNIL_DEFAULT 10 /* mxhnil default value */ +#define MXSTEP_DEFAULT 1000 /* mxstep default value */ + + +/***************************************************************/ +/*************** END Default Constants *************************/ +/***************************************************************/ + + +/***************************************************************/ +/************ BEGIN Routine-Specific Constants *****************/ +/***************************************************************/ + +/* CVodeDky */ + +#define FUZZ_FACTOR RCONST(100.0) + +/* CVHin */ + +#define HLB_FACTOR RCONST(100.0) +#define HUB_FACTOR RCONST(0.1) +#define H_BIAS HALF +#define MAX_ITERS 40 + +/* CVSet */ + +#define CORTES RCONST(0.1) + +/* CVStep return values */ + +#define SUCCESS_STEP 0 +#define REP_ERR_FAIL -1 +#define REP_CONV_FAIL -2 +#define SETUP_FAILED -3 +#define SOLVE_FAILED -4 + +/* CVStep control constants */ + +#define PREDICT_AGAIN -5 +#define DO_ERROR_TEST 1 + +/* CVStep */ + +#define THRESH RCONST(1.5) +#define ETAMX1 RCONST(10000.0) +#define ETAMX2 RCONST(10.0) +#define ETAMX3 RCONST(10.0) +#define ETAMXF RCONST(0.2) +#define ETAMIN RCONST(0.1) +#define ETACF RCONST(0.25) +#define ADDON RCONST(0.000001) +#define BIAS1 RCONST(6.0) +#define BIAS2 RCONST(6.0) +#define BIAS3 RCONST(10.0) +#define ONEPSM RCONST(1.000001) + +#define SMALL_NST 10 /* nst > SMALL_NST => use ETAMX3 */ +#define MXNCF 10 /* max no. of convergence failures during */ + /* one step try */ +#define MXNEF 7 /* max no. of error test failures during */ + /* one step try */ +#define MXNEF1 3 /* max no. of error test failures before */ + /* forcing a reduction of order */ +#define SMALL_NEF 2 /* if an error failure occurs and */ + /* SMALL_NEF <= nef <= MXNEF1, then */ + /* reset eta = MIN(eta, ETAMXF) */ +#define LONG_WAIT 10 /* number of steps to wait before */ + /* considering an order change when */ + /* q==1 and MXNEF1 error test failures */ + /* have occurred */ + +/* CVnls return values */ + +#define SOLVED 0 +#define CONV_FAIL -1 +#define SETUP_FAIL_UNREC -2 +#define SOLVE_FAIL_UNREC -3 + +/* CVnls input flags */ + +#define FIRST_CALL 0 +#define PREV_CONV_FAIL -1 +#define PREV_ERR_FAIL -2 + +/* CVnls other constants */ + +#define FUNC_MAXCOR 3 /* maximum no. of corrector iterations */ + /* for iter == FUNCTIONAL */ +#define NEWT_MAXCOR 3 /* maximum no. of corrector iterations */ + /* for iter == NEWTON */ + +#define CRDOWN RCONST(0.3) /* constant used in the estimation of the */ + /* convergence rate (crate) of the */ + /* iterates for the nonlinear equation */ +#define DGMAX RCONST(0.3) /* iter == NEWTON, |gamma/gammap-1| > DGMAX */ + /* => call lsetup */ + +#define RDIV TWO /* declare divergence if ratio del/delp > RDIV */ +#define MSBP 20 /* max no. of steps between lsetup calls */ + +#define TRY_AGAIN_CVODE 99 /* control constant for CVnlsNewton - should be */ + /* distinct from CVnls return values */ + + +/***************************************************************/ +/*************** END Routine-Specific Constants ***************/ +/***************************************************************/ + + +/***************************************************************/ +/***************** BEGIN Error Messages ************************/ +/***************************************************************/ + +/* CVodeMalloc/CVReInit Error Messages */ + +#define CVM "CVodeMalloc/CVReInit-- " + +#define MSG_Y0_NULL CVM "y0=NULL illegal.\n\n" + +#define MSG_BAD_N CVM "N=%ld < 1 illegal.\n\n" + +#define MSG_BAD_LMM_1 CVM "lmm=%d illegal.\n" +#define MSG_BAD_LMM_2 "The legal values are ADAMS=%d and BDF=%d.\n\n" +#define MSG_BAD_LMM MSG_BAD_LMM_1 MSG_BAD_LMM_2 + +#define MSG_BAD_ITER_1 CVM "iter=%d illegal.\n" +#define MSG_BAD_ITER_2 "The legal values are FUNCTIONAL=%d " +#define MSG_BAD_ITER_3 "and NEWTON=%d.\n\n" +#define MSG_BAD_ITER MSG_BAD_ITER_1 MSG_BAD_ITER_2 MSG_BAD_ITER_3 + +#define MSG_BAD_ITOL_1 CVM "itol=%d illegal.\n" +#define MSG_BAD_ITOL_2 "The legal values are SS=%d and SV=%d.\n\n" +#define MSG_BAD_ITOL MSG_BAD_ITOL_1 MSG_BAD_ITOL_2 + +#define MSG_F_NULL CVM "f=NULL illegal.\n\n" + +#define MSG_RELTOL_NULL CVM "reltol=NULL illegal.\n\n" + +#define MSG_BAD_RELTOL CVM "*reltol=%g < 0 illegal.\n\n" + +#define MSG_ABSTOL_NULL CVM "abstol=NULL illegal.\n\n" + +#define MSG_BAD_ABSTOL CVM "Some abstol component < 0.0 illegal.\n\n" + +#define MSG_BAD_OPTIN_1 CVM "optIn=%d illegal.\n" +#define MSG_BAD_OPTIN_2 "The legal values are FALSE=%d and TRUE=%d.\n\n" +#define MSG_BAD_OPTIN MSG_BAD_OPTIN_1 MSG_BAD_OPTIN_2 + +#define MSG_BAD_OPT CVM "optIn=TRUE, but iopt=ropt=NULL.\n\n" + +#define MSG_MEM_FAIL CVM "A memory request failed.\n\n" + +#define MSG_BAD_EWT CVM "Some initial ewt component = 0.0 illegal.\n\n" + +#define MSG_REI_NO_MEM "CVReInit-- cvode_mem = NULL illegal.\n\n" + +#define MSG_REI_MAXORD1 "CVReInit-- Illegal attempt to increase " +#define MSG_REI_MAXORD2 "maximum method order from %d to %d.\n\n" +#define MSG_REI_MAXORD MSG_REI_MAXORD1 MSG_REI_MAXORD2 + + +/* CVode error messages */ + +#define CVODE "CVode-- " + +#define NO_MEM "cvode_mem=NULL illegal.\n\n" + +#define MSG_CVODE_NO_MEM CVODE NO_MEM + +#define MSG_LINIT_NULL CVODE "The linear solver's init routine is NULL.\n\n" + +#define MSG_LSETUP_NULL CVODE "The linear solver's setup routine is NULL.\n\n" + +#define MSG_LSOLVE_NULL CVODE "The linear solver's solve routine is NULL.\n\n" + +#define MSG_LFREE_NULL CVODE "The linear solver's free routine is NULL.\n\n" + +#define MSG_LINIT_FAIL CVODE "The linear solver's init routine failed.\n\n" + +#define MSG_YOUT_NULL CVODE "yout=NULL illegal.\n\n" + +#define MSG_T_NULL CVODE "t=NULL illegal.\n\n" + +#define MSG_BAD_ITASK_1 CVODE "itask=%d illegal.\nThe legal values are" +#define MSG_BAD_ITASK_2 " NORMAL=%d and ONE_STEP=%d.\n\n" +#define MSG_BAD_ITASK MSG_BAD_ITASK_1 MSG_BAD_ITASK_2 + +#define MSG_BAD_HMIN_HMAX_1 CVODE "Inconsistent step size limits:\n" +#define MSG_BAD_HMIN_HMAX_2 "ropt[HMIN]=%g > ropt[HMAX]=%g.\n\n" +#define MSG_BAD_HMIN_HMAX MSG_BAD_HMIN_HMAX_1 MSG_BAD_HMIN_HMAX_2 + +#define MSG_BAD_H0 CVODE "h0=%g and tout-t0=%g inconsistent.\n\n" + +#define MSG_BAD_TOUT_1 CVODE "Trouble interpolating at tout = %g.\n" +#define MSG_BAD_TOUT_2 "tout too far back in direction of integration.\n\n" +#define MSG_BAD_TOUT MSG_BAD_TOUT_1 MSG_BAD_TOUT_2 + +#define MSG_MAX_STEPS_1 CVODE "At t=%g, mxstep=%d steps taken on " +#define MSG_MAX_STEPS_2 "this call before\nreaching tout=%g.\n\n" +#define MSG_MAX_STEPS MSG_MAX_STEPS_1 MSG_MAX_STEPS_2 + +#define MSG_EWT_NOW_BAD_1 CVODE "At t=%g, " +#define MSG_EWT_NOW_BAD_2 "some ewt component has become <= 0.0.\n\n" +#define MSG_EWT_NOW_BAD MSG_EWT_NOW_BAD_1 MSG_EWT_NOW_BAD_2 + +#define MSG_TOO_MUCH_ACC CVODE "At t=%g, too much accuracy requested.\n\n" + +#define MSG_HNIL_1 CVODE "Warning.. internal t=%g and step size h=%g\n" +#define MSG_HNIL_2 "are such that t + h == t on the next step.\n" +#define MSG_HNIL_3 "The solver will continue anyway.\n\n" +#define MSG_HNIL MSG_HNIL_1 MSG_HNIL_2 MSG_HNIL_3 + +#define MSG_HNIL_DONE_1 CVODE "The above warning has been issued %d times " +#define MSG_HNIL_DONE_2 "and will not be\nissued again for this problem.\n\n" +#define MSG_HNIL_DONE MSG_HNIL_DONE_1 MSG_HNIL_DONE_2 + +#define MSG_ERR_FAILS_1 CVODE "At t=%g and step size h=%g, the error test\n" +#define MSG_ERR_FAILS_2 "failed repeatedly or with |h| = hmin.\n\n" +#define MSG_ERR_FAILS MSG_ERR_FAILS_1 MSG_ERR_FAILS_2 + +#define MSG_CONV_FAILS_1 CVODE "At t=%g and step size h=%g, the corrector\n" +#define MSG_CONV_FAILS_2 "convergence failed repeatedly or " +#define MSG_CONV_FAILS_3 "with |h| = hmin.\n\n" +#define MSG_CONV_FAILS MSG_CONV_FAILS_1 MSG_CONV_FAILS_2 MSG_CONV_FAILS_3 + +#define MSG_SETUP_FAILED_1 CVODE "At t=%g, the setup routine failed in an " +#define MSG_SETUP_FAILED_2 "unrecoverable manner.\n\n" +#define MSG_SETUP_FAILED MSG_SETUP_FAILED_1 MSG_SETUP_FAILED_2 + +#define MSG_SOLVE_FAILED_1 CVODE "At t=%g, the solve routine failed in an " +#define MSG_SOLVE_FAILED_2 "unrecoverable manner.\n\n" +#define MSG_SOLVE_FAILED MSG_SOLVE_FAILED_1 MSG_SOLVE_FAILED_2 + +#define MSG_TOO_CLOSE_1 CVODE "tout=%g too close to t0=%g to start" +#define MSG_TOO_CLOSE_2 " integration.\n\n" +#define MSG_TOO_CLOSE MSG_TOO_CLOSE_1 MSG_TOO_CLOSE_2 + + +/* CVodeDky Error Messages */ + +#define DKY "CVodeDky-- " + +#define MSG_DKY_NO_MEM DKY NO_MEM + +#define MSG_BAD_K DKY "k=%d illegal.\n\n" + +#define MSG_BAD_T_1 DKY "t=%g illegal.\n" +#define MSG_BAD_T_2 "t not in interval tcur-hu=%g to tcur=%g.\n\n" +#define MSG_BAD_T MSG_BAD_T_1 MSG_BAD_T_2 + +#define MSG_BAD_DKY DKY "dky=NULL illegal.\n\n" + +/***************************************************************/ +/****************** END Error Messages *************************/ +/***************************************************************/ + + +/************************************************************/ +/*************** END CVODE Private Constants ****************/ +/************************************************************/ + + +/**************************************************************/ +/********* BEGIN Private Helper Functions Prototypes **********/ +/**************************************************************/ + +static booleantype CVAllocVectors(CVodeMem cv_mem, integertype neq, + int maxord, M_Env machEnv); +static void CVFreeVectors(CVodeMem cv_mem, int maxord); + +static booleantype CVEwtSet(CVodeMem cv_mem, N_Vector ycur); +static booleantype CVEwtSetSS(CVodeMem cv_mem, N_Vector ycur); +static booleantype CVEwtSetSV(CVodeMem cv_mem, N_Vector ycur); + +static booleantype CVHin(CVodeMem cv_mem, realtype tout); +static realtype CVUpperBoundH0(CVodeMem cv_mem, realtype tdist); +static realtype CVYddNorm(CVodeMem cv_mem, realtype hg); + +static int CVStep(CVodeMem cv_mem); + +static int CVsldet(CVodeMem cv_mem); + +static void CVAdjustParams(CVodeMem cv_mem); +static void CVAdjustOrder(CVodeMem cv_mem, int deltaq); +static void CVAdjustAdams(CVodeMem cv_mem, int deltaq); +static void CVAdjustBDF(CVodeMem cv_mem, int deltaq); +static void CVIncreaseBDF(CVodeMem cv_mem); +static void CVDecreaseBDF(CVodeMem cv_mem); + +static void CVRescale(CVodeMem cv_mem); + +static void CVPredict(CVodeMem cv_mem); + +static void CVSet(CVodeMem cv_mem); +static void CVSetAdams(CVodeMem cv_mem); +static realtype CVAdamsStart(CVodeMem cv_mem, realtype m[]); +static void CVAdamsFinish(CVodeMem cv_mem, realtype m[], realtype M[], + realtype hsum); +static realtype CVAltSum(int iend, realtype a[], int k); +static void CVSetBDF(CVodeMem cv_mem); +static void CVSetTqBDF(CVodeMem cv_mem, realtype hsum, realtype alpha0, + realtype alpha0_hat, realtype xi_inv, + realtype xistar_inv); + +static int CVnls(CVodeMem cv_mem, int nflag); +static int CVnlsFunctional(CVodeMem cv_mem); +static int CVnlsNewton(CVodeMem cv_mem, int nflag); +static int CVNewtonIteration(CVodeMem cv_mem); + +static int CVHandleNFlag(CVodeMem cv_mem, int *nflagPtr, realtype saved_t, + int *ncfPtr); + +static void CVRestore(CVodeMem cv_mem, realtype saved_t); + +static booleantype CVDoErrorTest(CVodeMem cv_mem, int *nflagPtr, + int *kflagPtr, realtype saved_t, + int *nefPtr, realtype * dsmPtr); + +static void CVCompleteStep(CVodeMem cv_mem); + +static void CVPrepareNextStep(CVodeMem cv_mem, realtype dsm); +static void CVSetEta(CVodeMem cv_mem); +static realtype CVComputeEtaqm1(CVodeMem cv_mem); +static realtype CVComputeEtaqp1(CVodeMem cv_mem); +static void CVChooseEta(CVodeMem cv_mem); +static void CVBDFStab(CVodeMem cv_mem); + +static int CVHandleFailure(CVodeMem cv_mem, int kflag); + + +/**************************************************************/ +/********** END Private Helper Functions Prototypes ***********/ +/**************************************************************/ + + +/**************************************************************/ +/**************** BEGIN Readability Constants *****************/ +/**************************************************************/ + + +#define uround (cv_mem->cv_uround) +#define zn (cv_mem->cv_zn) +#define ewt (cv_mem->cv_ewt) +#define y (cv_mem->cv_y) +#define acor (cv_mem->cv_acor) +#define tempv (cv_mem->cv_tempv) +#define ftemp (cv_mem->cv_ftemp) +#define q (cv_mem->cv_q) +#define qprime (cv_mem->cv_qprime) +#define qwait (cv_mem->cv_qwait) +#define L (cv_mem->cv_L) +#define h (cv_mem->cv_h) +#define hprime (cv_mem->cv_hprime) +#define eta (cv_mem-> cv_eta) +#define etaqm1 (cv_mem-> cv_etaqm1) +#define etaq (cv_mem-> cv_etaq) +#define etaqp1 (cv_mem-> cv_etaqp1) +#define nscon (cv_mem->cv_nscon) +#define ssdat (cv_mem->cv_ssdat) +#define hscale (cv_mem->cv_hscale) +#define tn (cv_mem->cv_tn) +#define tau (cv_mem->cv_tau) +#define tq (cv_mem->cv_tq) +#define l (cv_mem->cv_l) +#define rl1 (cv_mem->cv_rl1) +#define gamma (cv_mem->cv_gamma) +#define gammap (cv_mem->cv_gammap) +#define gamrat (cv_mem->cv_gamrat) +#define crate (cv_mem->cv_crate) +#define acnrm (cv_mem->cv_acnrm) +#define mnewt (cv_mem->cv_mnewt) +#define qmax (cv_mem->cv_qmax) +#define mxstep (cv_mem->cv_mxstep) +#define maxcor (cv_mem->cv_maxcor) +#define mxhnil (cv_mem->cv_mxhnil) +#define hmin (cv_mem->cv_hmin) +#define hmax_inv (cv_mem->cv_hmax_inv) +#define etamax (cv_mem->cv_etamax) +#define nst (cv_mem->cv_nst) +#define nfe (cv_mem->cv_nfe) +#define ncfn (cv_mem->cv_ncfn) +#define netf (cv_mem->cv_netf) +#define nni (cv_mem-> cv_nni) +#define nsetups (cv_mem->cv_nsetups) +#define nhnil (cv_mem->cv_nhnil) +#define lrw (cv_mem->cv_lrw) +#define liw (cv_mem->cv_liw) +#define linit (cv_mem->cv_linit) +#define lsetup (cv_mem->cv_lsetup) +#define lsolve (cv_mem->cv_lsolve) +#define lfree (cv_mem->cv_lfree) +#define lmem (cv_mem->cv_lmem) +#define qu (cv_mem->cv_qu) +#define nstlp (cv_mem->cv_nstlp) +#define hu (cv_mem->cv_hu) +#define saved_tq5 (cv_mem->cv_saved_tq5) +#define jcur (cv_mem->cv_jcur) +#define tolsf (cv_mem->cv_tolsf) +#define setupNonNull (cv_mem->cv_setupNonNull) +#define machenv (cv_mem->cv_machenv) +#define sldeton (cv_mem->cv_sldeton) + +/**************************************************************/ +/***************** END Readability Constants ******************/ +/**************************************************************/ + + +/***************************************************************/ +/************* BEGIN CVODE Implementation **********************/ +/***************************************************************/ + + +/***************************************************************/ +/********* BEGIN Exported Functions Implementation *************/ +/***************************************************************/ + + +/******************** CVodeMalloc ******************************* + + CVodeMalloc allocates and initializes memory for a problem. All + problem specification inputs are checked for errors. If any + error occurs during initialization, it is reported to the file + whose file pointer is errfp and NULL is returned. Otherwise, the + pointer to successfully initialized problem memory is returned. + +*****************************************************************/ + +void * +CVodeMalloc(integertype N, RhsFn f, realtype t0, N_Vector y0, + int lmm, int iter, int itol, + realtype * reltol, void *abstol, + void *f_data, FILE * errfp, booleantype optIn, + long int iopt[], realtype ropt[], M_Env machEnv) +{ + booleantype allocOK, ioptExists, roptExists, neg_abstol, ewtsetOK; + int maxord; + CVodeMem cv_mem; +#if !defined(R_SO) + FILE *fp; +#endif + int i, k; + + /* Check for legal input parameters */ + +#if !defined(R_SO) + fp = (errfp == NULL) ? stdout : errfp; +#endif + + if (y0 == NULL) + { + MACHENV warning_msg( MSG_Y0_NULL); + return (NULL); + } + + if (N <= 0) + { + MACHENV warning_msg(MACHENV sformatf(MSG_BAD_N, N)); + return (NULL); + } + + if ((lmm != ADAMS) && (lmm != BDF)) + { + MACHENV warning_msg(MACHENV sformatf(MSG_BAD_LMM, lmm, ADAMS, BDF)); + return (NULL); + } + + if ((iter != FUNCTIONAL) && (iter != NEWTON)) + { + MACHENV warning_msg(MACHENV sformatf(MSG_BAD_ITER, iter, FUNCTIONAL, NEWTON)); + return (NULL); + } + + if ((itol != SS) && (itol != SV)) + { + MACHENV warning_msg(MACHENV sformatf(MSG_BAD_ITOL, itol, SS, SV)); + return (NULL); + } + + if (f == NULL) + { + MACHENV warning_msg(MSG_F_NULL); + return (NULL); + } + + if (reltol == NULL) + { + MACHENV warning_msg(MSG_RELTOL_NULL); + return (NULL); + } + + if (*reltol < ZERO) + { + MACHENV warning_msg(MACHENV sformatf(MSG_BAD_RELTOL, (double) *reltol)); + return (NULL); + } + + if (abstol == NULL) + { + MACHENV warning_msg(MSG_ABSTOL_NULL); + return (NULL); + } + + if (itol == SS) + { + neg_abstol = (*((realtype *) abstol) < ZERO); + } + else + { + neg_abstol = (N_VMin((N_Vector) abstol) < ZERO); + } + if (neg_abstol) + { + MACHENV warning_msg(MSG_BAD_ABSTOL); + return (NULL); + } + + if ((optIn != FALSE) && (optIn != TRUE)) + { + MACHENV warning_msg(MACHENV sformatf(MSG_BAD_OPTIN, optIn, FALSE, TRUE)); + return (NULL); + } + + if ((optIn) && (iopt == NULL) && (ropt == NULL)) + { + MACHENV warning_msg(MSG_BAD_OPT); + return (NULL); + } + + ioptExists = (iopt != NULL); + roptExists = (ropt != NULL); + + /* Compute maxord */ + + maxord = (lmm == ADAMS) ? ADAMS_Q_MAX : BDF_Q_MAX; + + if (optIn && ioptExists) + { + if (iopt[MAXORD] > 0) + maxord = MIN(maxord, iopt[MAXORD]); + } + + cv_mem = (CVodeMem) malloc(sizeof(struct CVodeMemRec)); + if (cv_mem == NULL) + { + MACHENV warning_msg(MSG_MEM_FAIL); + return (NULL); + } + + /* Allocate the vectors */ + + allocOK = CVAllocVectors(cv_mem, N, maxord, machEnv); + if (!allocOK) + { + MACHENV warning_msg(MSG_MEM_FAIL); + MACHENV_MALLOC PHRQ_free(cv_mem); + return (NULL); + } + + /* Copy tolerances into memory, and set the ewt vector */ + + cv_mem->cv_itol = itol; + cv_mem->cv_reltol = reltol; + cv_mem->cv_abstol = abstol; + ewtsetOK = CVEwtSet(cv_mem, y0); + if (!ewtsetOK) + { + MACHENV warning_msg(MSG_BAD_EWT); + CVFreeVectors(cv_mem, maxord); + MACHENV_MALLOC PHRQ_free(cv_mem); + return (NULL); + } + + /* All error checking is complete at this point */ + + /* Copy the remaining input parameters into CVODE memory */ + + cv_mem->cv_N = N; + cv_mem->cv_f = f; + cv_mem->cv_f_data = f_data; + cv_mem->cv_lmm = lmm; + cv_mem->cv_iter = iter; + cv_mem->cv_optIn = optIn; + cv_mem->cv_iopt = iopt; + cv_mem->cv_ropt = ropt; +#if !defined(R_SO) + cv_mem->cv_errfp = fp; +#endif + tn = t0; + machenv = machEnv; + + /* Set step parameters */ + + q = 1; + L = 2; + qwait = L; + qmax = maxord; + etamax = ETAMX1; + + /* Set uround */ + + uround = UnitRoundoff(); + + /* Set the linear solver addresses to NULL. + (We check != NULL later, in CVode, if using NEWTON.) */ + + linit = NULL; + lsetup = NULL; + lsolve = NULL; + lfree = NULL; + lmem = NULL; + + /* Initialize zn[0] in the history array */ + + N_VScale(ONE, y0, zn[0]); + + /* Handle the remaining optional inputs (CVode checks ropt[HMAX]) */ + + hmax_inv = HMAX_INV_DEFAULT; + hmin = HMIN_DEFAULT; + if (optIn && roptExists) + { + if (ropt[HMIN] > ZERO) + hmin = ropt[HMIN]; + } + + mxhnil = MXHNIL_DEFAULT; + mxstep = MXSTEP_DEFAULT; + if (optIn && ioptExists) + { + if (iopt[MXHNIL] != 0) + mxhnil = iopt[MXHNIL]; + if (iopt[MXSTEP] > 0) + mxstep = iopt[MXSTEP]; + } + + if ((!optIn) && roptExists) + ropt[H0] = ZERO; + + /* Set maxcor */ + + maxcor = (iter == NEWTON) ? NEWT_MAXCOR : FUNC_MAXCOR; + + /* Initialize all the counters */ + + nst = nfe = ncfn = netf = nni = nsetups = nhnil = nstlp = 0; + + /* Initialize all other variables corresponding to optional outputs */ + + qu = 0; + hu = ZERO; + tolsf = ONE; + + /* Initialize optional output locations in iopt, ropt */ + /* and Stablilty Limit Detection data. */ + + nscon = 0; + sldeton = FALSE; + if (ioptExists) + { + iopt[NST] = iopt[NFE] = iopt[NSETUPS] = iopt[NNI] = 0; + iopt[NCFN] = iopt[NETF] = 0; + iopt[QU] = qu; + iopt[QCUR] = 0; + iopt[LENRW] = lrw; + iopt[LENIW] = liw; + if (optIn && iopt[SLDET] && (lmm == BDF)) + { + sldeton = TRUE; + iopt[NOR] = 0; + for (i = 1; i <= 5; i++) + { + for (k = 1; k <= 3; k++) + ssdat[i - 1][k - 1] = ZERO; + } + } + } + + if (roptExists) + { + ropt[HU] = hu; + ropt[HCUR] = ZERO; + ropt[TCUR] = t0; + ropt[TOLSF] = tolsf; + } + + + /* Problem has been successfully initialized */ + + return ((void *) cv_mem); +} + + +/******************** CVReInit ********************************** + + CVReInit re-initializes CVODE's memory for a problem, assuming + it has already been allocated in a prior CVodeMalloc call. + All problem specification inputs are checked for errors. + The problem size N is assumed to be unchanged since the call to + CVodeMalloc, and the maximum order maxord must not be larger. + If any error occurs during initialization, it is reported to the + file whose file pointer is errfp. + The return value is SUCCESS = 0 if no errors occurred, or + a negative value otherwise. + +*****************************************************************/ + +int +CVReInit(void *cvode_mem, RhsFn f, realtype t0, N_Vector y0, + int lmm, int iter, int itol, + realtype * reltol, void *abstol, + void *f_data, FILE * errfp, booleantype optIn, + long int iopt[], realtype ropt[], M_Env machEnv) +{ + booleantype ioptExists, roptExists, neg_abstol, ewtsetOK; + int maxord, i, k; + CVodeMem cv_mem; +#if !defined(R_SO) + FILE *fp; +#endif + /* Check for legal input parameters */ + +#if !defined(R_SO) + fp = (errfp == NULL) ? stdout : errfp; +#endif + + if (cvode_mem == NULL) + { + MACHENV warning_msg(MSG_REI_NO_MEM); + return (CVREI_NO_MEM); + } + cv_mem = (CVodeMem) cvode_mem; + + if (y0 == NULL) + { + MACHENV warning_msg(MSG_Y0_NULL); + return (CVREI_ILL_INPUT); + } + + if ((lmm != ADAMS) && (lmm != BDF)) + { + MACHENV warning_msg(MACHENV sformatf(MSG_BAD_LMM, lmm, ADAMS, BDF)); + return (CVREI_ILL_INPUT); + } + + if ((iter != FUNCTIONAL) && (iter != NEWTON)) + { + MACHENV warning_msg(MACHENV sformatf(MSG_BAD_ITER, iter, FUNCTIONAL, NEWTON)); + return (CVREI_ILL_INPUT); + } + + if ((itol != SS) && (itol != SV)) + { + MACHENV warning_msg(MACHENV sformatf(MSG_BAD_ITOL, itol, SS, SV)); + return (CVREI_ILL_INPUT); + } + + if (f == NULL) + { + MACHENV warning_msg(MSG_F_NULL); + return (CVREI_ILL_INPUT); + } + + if (reltol == NULL) + { + MACHENV warning_msg(MSG_RELTOL_NULL); + return (CVREI_ILL_INPUT); + } + + if (*reltol < ZERO) + { + MACHENV warning_msg(MACHENV sformatf(MSG_BAD_RELTOL, (double) *reltol)); + return (CVREI_ILL_INPUT); + } + + if (abstol == NULL) + { + MACHENV warning_msg(MSG_ABSTOL_NULL); + return (CVREI_ILL_INPUT); + } + + if (itol == SS) + { + neg_abstol = (*((realtype *) abstol) < ZERO); + } + else + { + neg_abstol = (N_VMin((N_Vector) abstol) < ZERO); + } + if (neg_abstol) + { + MACHENV warning_msg(MSG_BAD_ABSTOL); + return (CVREI_ILL_INPUT); + } + + if ((optIn != FALSE) && (optIn != TRUE)) + { + MACHENV warning_msg(MACHENV sformatf(MSG_BAD_OPTIN, optIn, FALSE, TRUE)); + return (CVREI_ILL_INPUT); + } + + if ((optIn) && (iopt == NULL) && (ropt == NULL)) + { + MACHENV warning_msg(MSG_BAD_OPT); + return (CVREI_ILL_INPUT); + } + + ioptExists = (iopt != NULL); + roptExists = (ropt != NULL); + + /* Compute new maxord and check against old value */ + + maxord = (lmm == ADAMS) ? ADAMS_Q_MAX : BDF_Q_MAX; + if (optIn && ioptExists) + { + if (iopt[MAXORD] > 0) + maxord = MIN(maxord, iopt[MAXORD]); + } + if (maxord > qmax) + { + MACHENV warning_msg(MACHENV sformatf(MSG_REI_MAXORD, qmax, maxord)); + return (CVREI_ILL_INPUT); + } + + /* Copy tolerances into memory, and set the ewt vector */ + + cv_mem->cv_itol = itol; + cv_mem->cv_reltol = reltol; + cv_mem->cv_abstol = abstol; + ewtsetOK = CVEwtSet(cv_mem, y0); + if (!ewtsetOK) + { + MACHENV warning_msg(MSG_BAD_EWT); + return (CVREI_ILL_INPUT); + } + + /* All error checking is complete at this point */ + + /* Copy the remaining input parameters into CVODE memory */ + + cv_mem->cv_f = f; + cv_mem->cv_f_data = f_data; + cv_mem->cv_lmm = lmm; + cv_mem->cv_iter = iter; + cv_mem->cv_optIn = optIn; + cv_mem->cv_iopt = iopt; + cv_mem->cv_ropt = ropt; +#if !defined(R_SO) + cv_mem->cv_errfp = fp; +#endif + tn = t0; + machenv = machEnv; + + /* Set step parameters */ + + q = 1; + L = 2; + qwait = L; + qmax = maxord; + etamax = ETAMX1; + + /* Set uround */ + + uround = UnitRoundoff(); + + /* Initialize zn[0] in the history array */ + + N_VScale(ONE, y0, zn[0]); + + /* Handle the remaining optional inputs (CVode checks ropt[HMAX]) */ + + hmax_inv = HMAX_INV_DEFAULT; + hmin = HMIN_DEFAULT; + if (optIn && roptExists) + { + if (ropt[HMIN] > ZERO) + hmin = ropt[HMIN]; + } + + mxhnil = MXHNIL_DEFAULT; + mxstep = MXSTEP_DEFAULT; + if (optIn && ioptExists) + { + if (iopt[MXHNIL] != 0) + mxhnil = iopt[MXHNIL]; + if (iopt[MXSTEP] > 0) + mxstep = iopt[MXSTEP]; + } + + if ((!optIn) && roptExists) + ropt[H0] = ZERO; + + /* Set maxcor */ + + maxcor = (iter == NEWTON) ? NEWT_MAXCOR : FUNC_MAXCOR; + + /* Initialize all the counters */ + + nst = nfe = ncfn = netf = nni = nsetups = nhnil = nstlp = 0; + + /* Initialize all other vars corresponding to optional outputs */ + + qu = 0; + hu = ZERO; + tolsf = ONE; + + /* Initialize optional output locations in iopt, ropt */ + /* and Stablilty Limit Detection data. */ + + nscon = 0; + sldeton = FALSE; + if (ioptExists) + { + iopt[NST] = iopt[NFE] = iopt[NSETUPS] = iopt[NNI] = 0; + iopt[NCFN] = iopt[NETF] = 0; + iopt[QU] = qu; + iopt[QCUR] = 0; + iopt[LENRW] = lrw; + iopt[LENIW] = liw; + if (optIn && iopt[SLDET] && (lmm == BDF)) + { + sldeton = TRUE; + iopt[NOR] = 0; + for (i = 1; i <= 5; i++) + { + for (k = 1; k <= 3; k++) + ssdat[i - 1][k - 1] = ZERO; + } + } + } + + if (roptExists) + { + ropt[HU] = hu; + ropt[HCUR] = ZERO; + ropt[TCUR] = t0; + ropt[TOLSF] = tolsf; + } + + /* Problem has been successfully re-initialized */ + + return (SUCCESS); +} + + +/**************************************************************/ +/************** BEGIN More Readability Constants **************/ +/**************************************************************/ + +#define N (cv_mem->cv_N) +#define f (cv_mem->cv_f) +#define f_data (cv_mem->cv_f_data) +#define lmm (cv_mem->cv_lmm) +#define iter (cv_mem->cv_iter) +#define itol (cv_mem->cv_itol) +#define reltol (cv_mem->cv_reltol) +#define abstol (cv_mem->cv_abstol) +#define optIn (cv_mem->cv_optIn) +#define iopt (cv_mem->cv_iopt) +#define ropt (cv_mem->cv_ropt) +#define errfp (cv_mem->cv_errfp) + +/**************************************************************/ +/*************** END More Readability Constants ***************/ +/**************************************************************/ + + +/********************* CVode **************************************** + + This routine is the main driver of the CVODE package. + + It integrates over a time interval defined by the user, by calling + CVStep to do internal time steps. + + The first time that CVode is called for a successfully initialized + problem, it computes a tentative initial step size h. + + CVode supports two modes, specified by itask: NORMAL and ONE_STEP. + In the NORMAL mode, the solver steps until it reaches or passes tout + and then interpolates to obtain y(tout). + In the ONE_STEP mode, it takes one internal step and returns. + +********************************************************************/ + +int +CVode(void *cvode_mem, realtype tout, N_Vector yout, realtype * t, int itask) +{ + int nstloc, kflag, istate, next_q, ier; + realtype rh, next_h; + booleantype hOK, ewtsetOK; + CVodeMem cv_mem; + realtype t0; + + /* Check for legal inputs in all cases */ + + cv_mem = (CVodeMem) cvode_mem; + if (cvode_mem == NULL) + { + CVMEM warning_msg(MSG_CVODE_NO_MEM); + return (CVODE_NO_MEM); + } + + if ((y = yout) == NULL) + { + CVMEM warning_msg(MSG_YOUT_NULL); + return (ILL_INPUT); + } + + if (t == NULL) + { + CVMEM warning_msg(MSG_T_NULL); + return (ILL_INPUT); + } + t0 = tn; + *t = tn; + + if ((itask != NORMAL) && (itask != ONE_STEP)) + { + CVMEM warning_msg(CVMEM sformatf(MSG_BAD_ITASK, itask, NORMAL, ONE_STEP)); + return (ILL_INPUT); + } + + /* Set hmax_inv from ropt[HMAX] and test for hmin > hmax */ + + if (optIn && ropt != NULL) + { + if (ropt[HMAX] > ZERO) + hmax_inv = ONE / ropt[HMAX]; + if (hmin * hmax_inv > ONE) + { + CVMEM warning_msg(CVMEM sformatf(MSG_BAD_HMIN_HMAX, (double) hmin, + (double) ropt[HMAX])); + return (ILL_INPUT); + } + } + + /* On first call, check solver functions and call linit function */ + + if (nst == 0) + { + if (iter == NEWTON) + { + if (linit == NULL) + { + CVMEM warning_msg(MSG_LINIT_NULL); + return (ILL_INPUT); + } + if (lsetup == NULL) + { + CVMEM warning_msg(MSG_LSETUP_NULL); + return (ILL_INPUT); + } + if (lsolve == NULL) + { + CVMEM warning_msg(MSG_LSOLVE_NULL); + return (ILL_INPUT); + } + if (lfree == NULL) + { + CVMEM warning_msg(MSG_LFREE_NULL); + return (ILL_INPUT); + } + ier = linit(cv_mem); + if (ier != LINIT_OK) + { + CVMEM warning_msg(MSG_LINIT_FAIL); + return (ILL_INPUT); + } + } + + /* On the first call, call f at (t0,y0), set zn[1] = y'(t0), + set initial h (from H0 or CVHin), and scale zn[1] by h */ + CVMEM cvode_rate_sim_time = CVMEM cvode_rate_sim_time_start + tn; + CVMEM cvode_step_fraction = 0; + + f(N, tn, zn[0], zn[1], f_data); + nfe = 1; + h = ZERO; + if (ropt != NULL) + h = ropt[H0]; + if ((h != ZERO) && ((tout - tn) * h < ZERO)) + { + CVMEM warning_msg(CVMEM sformatf(MSG_BAD_H0, (double) h, + (double) (tout - tn))); + return (ILL_INPUT); + } + if (h == ZERO) + { + hOK = CVHin(cv_mem, tout); + if (!hOK) + { + CVMEM warning_msg(CVMEM sformatf(MSG_TOO_CLOSE, (double) tout, + (double) tn)); + return (ILL_INPUT); + } + } + rh = ABS(h) * hmax_inv; + if (rh > ONE) + h /= rh; + if (ABS(h) < hmin) + h *= hmin / ABS(h); + hscale = h; + N_VScale(h, zn[1], zn[1]); + + } /* end of first call block */ + + /* If not the first call, check if tout already reached */ + + if ((itask == NORMAL) && (nst > 0) && ((tn - tout) * h >= ZERO)) + { + *t = tout; + ier = CVodeDky(cv_mem, tout, 0, yout); + if (ier != OKAY) + { /* ier must be == BAD_T */ + CVMEM warning_msg(CVMEM sformatf(MSG_BAD_TOUT, (double) tout)); + return (ILL_INPUT); + } + return (SUCCESS); + } + + /* Looping point for internal steps */ + + nstloc = 0; + loop + { + + next_h = h; + next_q = q; + + /* Reset and check ewt */ + + if (nst > 0) + { + ewtsetOK = CVEwtSet(cv_mem, zn[0]); + if (!ewtsetOK) + { + CVMEM warning_msg(CVMEM sformatf(MSG_EWT_NOW_BAD, (double) tn)); + istate = ILL_INPUT; + *t = tn; + N_VScale(ONE, zn[0], yout); + break; + } + } + + /* Check for too many steps */ + + if (nstloc >= mxstep) + { + istate = TOO_MUCH_WORK; + *t = tn; + N_VScale(ONE, zn[0], yout); + break; + } + + /* Check for too much accuracy requested */ + + if ((tolsf = uround * N_VWrmsNorm(zn[0], ewt)) > ONE) + { + CVMEM warning_msg(CVMEM sformatf(MSG_TOO_MUCH_ACC, (double) tn)); + istate = TOO_MUCH_ACC; + *t = tn; + N_VScale(ONE, zn[0], yout); + tolsf *= TWO; + break; + } + + /* Check for h below roundoff level in tn */ + + if (tn + h == tn) + { + nhnil++; + if (nhnil <= mxhnil) + CVMEM warning_msg(CVMEM sformatf(MSG_HNIL, (double) tn, (double) h)); + if (nhnil == mxhnil) + CVMEM warning_msg(CVMEM sformatf(MSG_HNIL_DONE, mxhnil)); + } + + /* Call CVStep to take a step */ + + kflag = CVStep(cv_mem); +#ifdef DEBUG_CVODE + cvode_test = TRUE; + f(N, tn, y, ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("After CVStep, y Fail\n"); + } + else + { + CVMEM warning_msg("After CVStep, y OK\n"); + } + cvode_test = TRUE; + f(N, tn, zn[0], ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CCVMEM warning_msg("After CVStep, zn Fail\n"); + } + else + { + CVMEM warning_msg("After CVStep, zn OK\n"); + } +#endif + /* Process failed step cases, and exit loop */ + + if (kflag != SUCCESS_STEP) + { + istate = CVHandleFailure(cv_mem, kflag); + *t = tn; + N_VScale(ONE, zn[0], yout); + break; + } + + nstloc++; + + /* Check if in one-step mode, and if so copy y and exit loop */ + + if (itask == ONE_STEP) + { + istate = SUCCESS; + *t = tn; + N_VScale(ONE, zn[0], yout); + next_q = qprime; + next_h = hprime; + break; + } + CVMEM cvode_rate_sim_time = CVMEM cvode_rate_sim_time_start + tn; + CVMEM cvode_step_fraction = (tn - t0) / (tout - t0); + /* + CVMEM warning_msg(CVMEM sformatf("ODE: tn %e, t0 %e, tout %e, step_frac %e\n", (double) tn, (double) t0, (double) tout, (double) cvode_step_fraction)); + */ + /* Check if tout reached, and if so interpolate and exit loop */ + + if ((tn - tout) * h >= ZERO) + { + /* + CVMEM warning_msg("*tn %e, t0 %e, tout %e, h %e\n", tn, t0, tout,h).c_str()); + */ + CVMEM cvode_rate_sim_time = CVMEM cvode_rate_sim_time_start + tout; + CVMEM cvode_step_fraction = 1.0; + istate = SUCCESS; + *t = tout; + (void) CVodeDky(cv_mem, tout, 0, yout); + next_q = qprime; + next_h = hprime; + break; + } + } + + /* End of step loop; load optional outputs and return */ + + if (iopt != NULL) + { + iopt[NST] = nst; + iopt[NFE] = nfe; + iopt[NSETUPS] = nsetups; + iopt[NNI] = nni; + iopt[NCFN] = ncfn; + iopt[NETF] = netf; + iopt[QU] = q; + iopt[QCUR] = next_q; + } + + if (ropt != NULL) + { + ropt[HU] = h; + ropt[HCUR] = next_h; + ropt[TCUR] = tn; + ropt[TOLSF] = tolsf; + } +#ifdef DEBUG_CVODE + /* + * check interpolation + */ + CVMEM cvode_test = TRUE; + f(N, tn, y, ftemp, f_data); + CVMEM cvode_test = FALSE; + if (CVMEM cvode_error == TRUE) + { + //CVMEM warning_msg("End of cvode, Interpolated y Fail\n"); + fprintf(stderr, "End of cvode, Interpolated y Fail\n"); + return (-1); + } + else + { + //CVMEM warning_msg("End of cvode, Interpolated y OK\n"); + //fprintf(stderr, "End of cvode, Interpolated y OK\n"); + //for (int i = 0; i <= N; i++) + //{ + // fprintf(stderr, "%d %e\n", i, Ith(y, i)); + //} + } +#endif + return (istate); +} + +/*************** CVodeDky ******************************************** + + This routine computes the k-th derivative of the interpolating + polynomial at the time t and stores the result in the vector dky. + The formula is: + q + dky = SUM c(j,k) * (t - tn)^(j-k) * h^(-j) * zn[j] , + j=k + where c(j,k) = j*(j-1)*...*(j-k+1), q is the current order, and + zn[j] is the j-th column of the Nordsieck history array. + + This function is called by CVode with k = 0 and t = tout, but + may also be called directly by the user. + +**********************************************************************/ + +int +CVodeDky(void *cvode_mem, realtype t, int k, N_Vector dky) +{ + realtype s, c, r; + realtype tfuzz, tp, tn1; + int i, j; + CVodeMem cv_mem; + + cv_mem = (CVodeMem) cvode_mem; + + /* Check all inputs for legality */ + + if (cvode_mem == NULL) + { + /* + CVMEM warning_msg(MSG_DKY_NO_MEM); + */ + return (DKY_NO_MEM); + } + + if (dky == NULL) + { + CVMEM warning_msg(MSG_BAD_DKY); + return (BAD_DKY); + } + + if ((k < 0) || (k > q)) + { + CVMEM warning_msg(CVMEM sformatf(MSG_BAD_K, k)); + return (BAD_K); + } + + tfuzz = FUZZ_FACTOR * uround * (ABS(tn) + ABS(hu)); + if (hu < ZERO) + tfuzz = -tfuzz; + tp = tn - hu - tfuzz; + tn1 = tn + tfuzz; + if ((t - tp) * (t - tn1) > ZERO) + { + CVMEM warning_msg(CVMEM sformatf(MSG_BAD_T, (double) t, (double) (tn - hu), + (double) tn)); + return (BAD_T); + } + + /* Sum the differentiated interpolating polynomial */ + + s = (t - tn) / h; + for (j = q; j >= k; j--) + { + c = ONE; + for (i = j; i >= j - k + 1; i--) + c *= i; + if (j == q) + { + N_VScale(c, zn[q], dky); + } + else + { + N_VLinearSum(c, zn[j], s, dky, dky); + } + } + if (k == 0) + return (OKAY); + r = RPowerI(h, -k); + N_VScale(r, dky, dky); + return (OKAY); +} + +/********************* CVodeFree ********************************** + + This routine frees the problem memory allocated by CVodeMalloc. + Such memory includes all the vectors allocated by CVAllocVectors, + and the memory lmem for the linear solver (deallocated by a call + to lfree). + +*******************************************************************/ + +void +CVodeFree(void *cvode_mem) +{ + CVodeMem cv_mem; + + cv_mem = (CVodeMem) cvode_mem; + + if (cvode_mem == NULL) + return; + + CVFreeVectors(cv_mem, qmax); + if (iter == NEWTON) + lfree(cv_mem); + CVMEM_MALLOC PHRQ_free(cv_mem); +} + + +/***************************************************************/ +/********** END Exported Functions Implementation **************/ +/***************************************************************/ + + +/*******************************************************************/ +/******** BEGIN Private Helper Functions Implementation ************/ +/*******************************************************************/ + +/****************** CVAllocVectors *********************************** + + This routine allocates the CVODE vectors ewt, acor, tempv, ftemp, and + zn[0], ..., zn[maxord]. The length of the vectors is the input + parameter neq and the maximum order (needed to allocate zn) is the + input parameter maxord. If all memory allocations are successful, + CVAllocVectors returns TRUE. Otherwise all allocated memory is freed + and CVAllocVectors returns FALSE. + This routine also sets the optional outputs lrw and liw, which are + (respectively) the lengths of the real and integer work spaces + allocated here. + +**********************************************************************/ + +static booleantype +CVAllocVectors(CVodeMem cv_mem, integertype neq, int maxord, M_Env machEnv) +{ + int i, j; + + /* Allocate ewt, acor, tempv, ftemp */ + + ewt = N_VNew(neq, machEnv); + if (ewt == NULL) + return (FALSE); + acor = N_VNew(neq, machEnv); + if (acor == NULL) + { + N_VFree(ewt); + return (FALSE); + } + tempv = N_VNew(neq, machEnv); + if (tempv == NULL) + { + N_VFree(ewt); + N_VFree(acor); + return (FALSE); + } + ftemp = N_VNew(neq, machEnv); + if (ftemp == NULL) + { + N_VFree(tempv); + N_VFree(ewt); + N_VFree(acor); + return (FALSE); + } + + /* Allocate zn[0] ... zn[maxord] */ + + for (j = 0; j <= maxord; j++) + { + zn[j] = N_VNew(neq, machEnv); + if (zn[j] == NULL) + { + N_VFree(ewt); + N_VFree(acor); + N_VFree(tempv); + N_VFree(ftemp); + for (i = 0; i < j; i++) + N_VFree(zn[i]); + return (FALSE); + } + } + + /* Set solver workspace lengths */ + + lrw = (maxord + 5) * neq; + liw = 0; + + return (TRUE); +} + +/***************** CVFreeVectors ********************************* + + This routine frees the CVODE vectors allocated in CVAllocVectors. + +******************************************************************/ + +static void +CVFreeVectors(CVodeMem cv_mem, int maxord) +{ + int j; + + N_VFree(ewt); + N_VFree(acor); + N_VFree(tempv); + N_VFree(ftemp); + for (j = 0; j <= maxord; j++) + N_VFree(zn[j]); +} + +/*********************** CVEwtSet ************************************** + + This routine is responsible for setting the error weight vector ewt, + according to tol_type, as follows: + + (1) ewt[i] = 1 / (*reltol * ABS(ycur[i]) + *abstol), i=0,...,neq-1 + if tol_type = SS + (2) ewt[i] = 1 / (*reltol * ABS(ycur[i]) + abstol[i]), i=0,...,neq-1 + if tol_type = SV + + CVEwtSet returns TRUE if ewt is successfully set as above to a + positive vector and FALSE otherwise. In the latter case, ewt is + considered undefined after the FALSE return from CVEwtSet. + + All the real work is done in the routines CVEwtSetSS, CVEwtSetSV. + +***********************************************************************/ + +static booleantype +CVEwtSet(CVodeMem cv_mem, N_Vector ycur) +{ + switch (itol) + { + case SS: + return (CVEwtSetSS(cv_mem, ycur)); + case SV: + return (CVEwtSetSV(cv_mem, ycur)); + } + return (-99); +} + +/*********************** CVEwtSetSS ********************************* + + This routine sets ewt as decribed above in the case tol_type = SS. + It tests for non-positive components before inverting. CVEwtSetSS + returns TRUE if ewt is successfully set to a positive vector + and FALSE otherwise. In the latter case, ewt is considered + undefined after the FALSE return from CVEwtSetSS. + +********************************************************************/ + +static booleantype +CVEwtSetSS(CVodeMem cv_mem, N_Vector ycur) +{ + realtype rtoli, atoli; + + rtoli = *reltol; + atoli = *((realtype *) abstol); + N_VAbs(ycur, tempv); + N_VScale(rtoli, tempv, tempv); + N_VAddConst(tempv, atoli, tempv); + if (N_VMin(tempv) <= ZERO) + return (FALSE); + N_VInv(tempv, ewt); + return (TRUE); +} + +/*********************** CVEwtSetSV ********************************* + + This routine sets ewt as decribed above in the case tol_type = SV. + It tests for non-positive components before inverting. CVEwtSetSV + returns TRUE if ewt is successfully set to a positive vector + and FALSE otherwise. In the latter case, ewt is considered + undefined after the FALSE return from CVEwtSetSV. + +********************************************************************/ + +static booleantype +CVEwtSetSV(CVodeMem cv_mem, N_Vector ycur) +{ + realtype rtoli; + rtoli = *reltol; + N_VAbs(ycur, tempv); + N_VLinearSum(rtoli, tempv, ONE, (N_Vector) abstol, tempv); + if (N_VMin(tempv) <= ZERO) + return (FALSE); + N_VInv(tempv, ewt); + return (TRUE); +} + +/******************* CVHin *************************************** + + This routine computes a tentative initial step size h0. + If tout is too close to tn (= t0), then CVHin returns FALSE and + h remains uninitialized. Otherwise, CVHin sets h to the chosen + value h0 and returns TRUE. + + The algorithm used seeks to find h0 as a solution of + (WRMS norm of (h0^2 ydd / 2)) = 1, + where ydd = estimated second derivative of y. + +*****************************************************************/ + +static booleantype +CVHin(CVodeMem cv_mem, realtype tout) +{ + int sign, count; + realtype tdiff, tdist, tround, hlb, hub; + realtype hg, hgs, hnew, hrat, h0, yddnrm; + + /* Test for tout too close to tn */ + + if ((tdiff = tout - tn) == ZERO) + return (FALSE); + + sign = (tdiff > ZERO) ? 1 : -1; + tdist = ABS(tdiff); + tround = uround * MAX(ABS(tn), ABS(tout)); + if (tdist < TWO * tround) + return (FALSE); + + /* Set lower and upper bounds on h0, and take geometric mean + Exit with this value if the bounds cross each other */ + + hlb = HLB_FACTOR * tround; + hub = CVUpperBoundH0(cv_mem, tdist); + hg = RSqrt(hlb * hub); + hnew = hg; + if (hub < hlb) + { + if (sign == -1) + hg = -hg; + h = hg; + return (TRUE); + } + + /* Loop up to MAX_ITERS times to find h0. + Stop if new and previous values differ by a factor < 2. + Stop if hnew/hg > 2 after one iteration, as this probably means + that the ydd value is bad because of cancellation error. */ + + count = 0; + loop + { + count++; + if (count >= MAX_ITERS) + break; + hgs = hg * sign; + yddnrm = CVYddNorm(cv_mem, hgs); + if (CVMEM cvode_error == TRUE) + { + hg /= 2.; +#ifdef DEBUG_CVODE + CVMEM warning_msg("halving step in CVHin\n"); +#endif + continue; + } + + hnew = + (yddnrm * hub * hub > + TWO) ? RSqrt(TWO / yddnrm) : RSqrt(hg * hub); + + hrat = hnew / hg; + if ((hrat > HALF) && (hrat < TWO)) + break; + if ((count >= 2) && (hrat > TWO)) + { + hnew = hg; + break; + } + hg = hnew; + } + + /* Apply bounds, bias factor, and attach sign */ + + h0 = H_BIAS * hnew; + if (h0 < hlb) + h0 = hlb; + if (h0 > hub) + h0 = hub; + if (sign == -1) + h0 = -h0; + h = h0; + return (TRUE); +} + +/******************** CVUpperBoundH0 ****************************** + + This routine sets an upper bound on abs(h0) based on + tdist = abs(tout - t0) and the values of y[i]/y'[i]. + +******************************************************************/ + +static realtype +CVUpperBoundH0(CVodeMem cv_mem, realtype tdist) +{ + realtype atoli, hub_inv, hub; + booleantype vectorAtol; + N_Vector temp1, temp2; + + atoli = 0; + vectorAtol = (itol == SV); + if (!vectorAtol) + atoli = *((realtype *) abstol); + temp1 = tempv; + temp2 = acor; + N_VAbs(zn[0], temp1); + N_VAbs(zn[1], temp2); + if (vectorAtol) + { + N_VLinearSum(HUB_FACTOR, temp1, ONE, (N_Vector) abstol, temp1); + } + else + { + N_VScale(HUB_FACTOR, temp1, temp1); + N_VAddConst(temp1, atoli, temp1); + } + N_VDiv(temp2, temp1, temp1); + hub_inv = N_VMaxNorm(temp1); + hub = HUB_FACTOR * tdist; + if (hub * hub_inv > ONE) + hub = ONE / hub_inv; + return (hub); +} + +/****************** CVYddNorm ************************************* + + This routine computes an estimate of the second derivative of y + using a difference quotient, and returns its WRMS norm. + +******************************************************************/ + +static realtype +CVYddNorm(CVodeMem cv_mem, realtype hg) +{ + realtype yddnrm; + + N_VLinearSum(hg, zn[1], ONE, zn[0], y); + f(N, tn + hg, y, tempv, f_data); +#ifdef DEBUG_CVODE + if (cvode_error == TRUE) + { + CVMEM warning_msg("CVYddNorm error\n"); + } +#endif + nfe++; + N_VLinearSum(ONE, tempv, -ONE, zn[1], tempv); + N_VScale(ONE / hg, tempv, tempv); + + yddnrm = N_VWrmsNorm(tempv, ewt); + return (yddnrm); +} + +/********************* CVStep ************************************** + + This routine performs one internal cvode step, from tn to tn + h. + It calls other routines to do all the work. + + The main operations done here are as follows: + * preliminary adjustments if a new step size was chosen; + * prediction of the Nordsieck history array zn at tn + h; + * setting of multistep method coefficients and test quantities; + * solution of the nonlinear system; + * testing the local error; + * updating zn and other state data if successful; + * resetting stepsize and order for the next step. + * if SLDET is on, check for stability, reduce order if necessary. + On a failure in the nonlinear system solution or error test, the + step may be reattempted, depending on the nature of the failure. + +********************************************************************/ + +static int +CVStep(CVodeMem cv_mem) +{ + realtype saved_t, dsm; + int ncf, nef, nflag; + booleantype passed; + + int kflag; + + saved_t = tn; + ncf = nef = 0; + nflag = FIRST_CALL; + + + if ((nst > 0) && (hprime != h)) + CVAdjustParams(cv_mem); + + /* Looping point for attempts to take a step */ + loop + { + bool predict_fail = false; + CVMEM cvode_test = TRUE; + f(N, tn, y, ftemp, f_data); + CVMEM cvode_test = FALSE; + if (CVMEM cvode_error == TRUE) + { + predict_fail = true; +#ifdef DEBUG_CVODE + CVMEM warning_msg("Before predict, y Fail, time %e\n", tn); +#endif + } + else + { + CVMEM cvode_prev_good_time = CVMEM cvode_last_good_time; + N_VScale(1.0, CVMEM cvode_last_good_y, CVMEM cvode_prev_good_y); + CVMEM cvode_last_good_time = tn; + N_VScale(1.0, y, CVMEM cvode_last_good_y); +#ifdef DEBUG_CVODE + CVMEM warning_msg("Before predict, y OK, time %e\n", tn); +#endif + } +#ifdef DEBUG_CVODE + cvode_test = TRUE; + f(N, tn, zn[0], ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("Before predict, zn Fail\n"); + } + else + { + CVMEM warning_msg("Before predict, zn OK\n"); + } + saved_t = tn; +#endif + CVPredict(cv_mem); +#ifdef DEBUG_CVODE + cvode_test = TRUE; + f(N, tn, y, ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("After predict, y Fail\n"); + } + else + { + CVMEM warning_msg("After predict, y OK\n"); + } + cvode_test = TRUE; + f(N, tn, zn[0], ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("After predict, zn Fail\n"); + + } + else + { + CVMEM warning_msg("After predict, zn OK\n"); + } +#endif + CVSet(cv_mem); + + nflag = CVnls(cv_mem, nflag); + if (CVMEM cvode_error == TRUE || predict_fail) + { + nflag = -1; + } +#ifdef DEBUG_CVODE + cvode_test = TRUE; + f(N, tn, y, ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("After CVnls, y Fail\n"); + } + else + { + CVMEM warning_msg("After CVnls, y OK\n"); + } + cvode_test = TRUE; + f(N, tn, zn[0], ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("After CVnls, zn Fail\n"); + } + else + { + CVMEM warning_msg("After CVnls, zn OK\n"); + } +#endif + //fprintf(stderr, "\nTime %e,\th %e\n", tn, h); + kflag = CVHandleNFlag(cv_mem, &nflag, saved_t, &ncf); + if (kflag == PREDICT_AGAIN) + continue; + if (kflag != DO_ERROR_TEST) + return (kflag); + /* Return if nonlinear solve failed and recovery not possible. */ +#ifdef DEBUG_CVODE + cvode_test = TRUE; + f(N, tn, y, ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("Before error test, y Fail\n"); + } + else + { + CVMEM warning_msg("Before error test, y OK\n"); + } + cvode_test = TRUE; + f(N, tn, zn[0], ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("Before error test, zn Fail\n"); + } + else + { + CVMEM warning_msg("Before error test, zn OK\n"); + } +#endif + passed = CVDoErrorTest(cv_mem, &nflag, &kflag, saved_t, &nef, &dsm); +#ifdef DEBUG_CVODE + cvode_test = TRUE; + f(N, tn, y, ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg(CVMEM sformatf("After error test, y Fail, passed %d\n", + passed)); + } + else + { + CVMEM warning_msg(CVMEM sformatf("After error test, y OK, passed %d\n", + passed)); + } + cvode_test = TRUE; + f(N, tn, zn[0], ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("After error test, zn Fail\n"); + } + else + { + CVMEM warning_msg("After error test, zn OK\n"); + } +#endif + /* Return if error test failed and recovery not possible. */ + if ((!passed) && (kflag == REP_ERR_FAIL)) + return (kflag); + if (passed) + break; + /* Retry step if error test failed, nflag == PREV_ERR_FAIL */ + } +#ifdef DEBUG_CVODE + CVMEM warning_msg("Finished step in CVStep\n"); +#endif + /* Nonlinear system solve and error test were both successful. + Update data, and consider change of step and/or order. */ + + + CVCompleteStep(cv_mem); +#ifdef DEBUG_CVODE + CVMEM cvode_test = TRUE; + f(N, tn, y, ftemp, f_data); + CVMEM cvode_test = FALSE; + if (CVMEM cvode_error == TRUE) + { + CVMEM warning_msg("After complete step, y Fail\n"); + } + else + { + CVMEM warning_msg("After complete step, y OK\n"); + } + CVMEM cvode_test = TRUE; + f(N, tn, zn[0], ftemp, f_data); + CVMEM cvode_test = FALSE; + if (CVMEM cvode_error == TRUE) + { + CVMEM warning_msg("After complete step, zn Fail\n"); + } + else + { + CVMEM warning_msg("After complete step, zn OK\n"); + } +#endif + CVPrepareNextStep(cv_mem, dsm); + + /* If Stablilty Limit Detection is turned on, call stability limit + detection routine for possible order reduction. */ + + if (sldeton) + CVBDFStab(cv_mem); +#ifdef DEBUG_CVODE + cvode_test = TRUE; + f(N, tn, y, ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("After cvbfdstab, y Fail\n"); + } + else + { + CVMEM warning_msg("After cvbfdstab, y OK\n"); + } + cvode_test = TRUE; + f(N, tn, zn[0], ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("After cvbfdstab, zn Fail\n"); + } + else + { + CVMEM warning_msg("After cvbfdstab, zn OK\n"); + } +#endif + etamax = (nst <= SMALL_NST) ? ETAMX2 : ETAMX3; + + /* Finally, we rescale the acor array to be the + estimated local error vector. */ + + N_VScale(ONE / tq[2], acor, acor); + return (SUCCESS_STEP); + +} + + +/********************* CVAdjustParams ******************************** + + This routine is called when a change in step size was decided upon, + and it handles the required adjustments to the history array zn. + If there is to be a change in order, we call CVAdjustOrder and reset + q, L = q+1, and qwait. Then in any case, we call CVRescale, which + resets h and rescales the Nordsieck array. + +**********************************************************************/ + +static void +CVAdjustParams(CVodeMem cv_mem) +{ + if (qprime != q) + { + CVAdjustOrder(cv_mem, qprime - q); + q = qprime; + L = q + 1; + qwait = L; + } + CVRescale(cv_mem); +} + +/********************* CVAdjustOrder ***************************** + + This routine is a high level routine which handles an order + change by an amount deltaq (= +1 or -1). If a decrease in order + is requested and q==2, then the routine returns immediately. + Otherwise CVAdjustAdams or CVAdjustBDF is called to handle the + order change (depending on the value of lmm). + +******************************************************************/ + +static void +CVAdjustOrder(CVodeMem cv_mem, int deltaq) +{ + if ((q == 2) && (deltaq != 1)) + return; + + switch (lmm) + { + case ADAMS: + CVAdjustAdams(cv_mem, deltaq); + break; + case BDF: + CVAdjustBDF(cv_mem, deltaq); + break; + } +} + +/*************** CVAdjustAdams *********************************** + + This routine adjusts the history array on a change of order q by + deltaq, in the case that lmm == ADAMS. + +*****************************************************************/ + +static void +CVAdjustAdams(CVodeMem cv_mem, int deltaq) +{ + int i, j; + realtype xi, hsum; + + /* On an order increase, set new column of zn to zero and return */ + + if (deltaq == 1) + { + N_VConst(ZERO, zn[L]); + return; + } + + /* On an order decrease, each zn[j] is adjusted by a multiple + of zn[q]. The coefficients in the adjustment are the + coefficients of the polynomial x*x*(x+xi_1)*...*(x+xi_j), + integrated, where xi_j = [t_n - t_(n-j)]/h. */ + + for (i = 0; i <= qmax; i++) + l[i] = ZERO; + l[1] = ONE; + hsum = ZERO; + for (j = 1; j <= q - 2; j++) + { + hsum += tau[j]; + xi = hsum / hscale; + for (i = j + 1; i >= 1; i--) + l[i] = l[i] * xi + l[i - 1]; + } + + for (j = 1; j <= q - 2; j++) + l[j + 1] = q * (l[j] / (j + 1)); + + for (j = 2; j < q; j++) + N_VLinearSum(-l[j], zn[q], ONE, zn[j], zn[j]); +} + +/********************** CVAdjustBDF ******************************* + + This is a high level routine which handles adjustments to the + history array on a change of order by deltaq in the case that + lmm == BDF. CVAdjustBDF calls CVIncreaseBDF if deltaq = +1 and + CVDecreaseBDF if deltaq = -1 to do the actual work. + +******************************************************************/ + +static void +CVAdjustBDF(CVodeMem cv_mem, int deltaq) +{ + switch (deltaq) + { + case 1: + CVIncreaseBDF(cv_mem); + return; + case -1: + CVDecreaseBDF(cv_mem); + return; + } +} + +/******************** CVIncreaseBDF ********************************** + + This routine adjusts the history array on an increase in the + order q in the case that lmm == BDF. + A new column zn[q+1] is set equal to a multiple of the saved + vector (= acor) in zn[qmax]. Then each zn[j] is adjusted by + a multiple of zn[q+1]. The coefficients in the adjustment are the + coefficients of the polynomial x*x*(x+xi_1)*...*(x+xi_(q-1)), + where xi_j = [t_n - t_(n-j)]/h. + +*********************************************************************/ + +static void +CVIncreaseBDF(CVodeMem cv_mem) +{ + realtype alpha0, alpha1, prod, xi, xiold, hsum, A1; + int i, j; + + for (i = 0; i <= qmax; i++) + l[i] = ZERO; + l[2] = alpha1 = prod = xiold = ONE; + alpha0 = -ONE; + hsum = hscale; + if (q > 1) + { + for (j = 1; j < q; j++) + { + hsum += tau[j + 1]; + xi = hsum / hscale; + prod *= xi; + alpha0 -= ONE / (j + 1); + alpha1 += ONE / xi; + for (i = j + 2; i >= 2; i--) + l[i] = l[i] * xiold + l[i - 1]; + xiold = xi; + } + } + A1 = (-alpha0 - alpha1) / prod; + if (L >= 0 && L <= qmax) + { + N_VScale(A1, zn[qmax], zn[L]); + for (j = 2; j <= q; j++) + { + N_VLinearSum(l[j], zn[L], ONE, zn[j], zn[j]); + } + } +} + +/********************* CVDecreaseBDF ****************************** + + This routine adjusts the history array on a decrease in the + order q in the case that lmm == BDF. + Each zn[j] is adjusted by a multiple of zn[q]. The coefficients + in the adjustment are the coefficients of the polynomial + x*x*(x+xi_1)*...*(x+xi_(q-2)), where xi_j = [t_n - t_(n-j)]/h. + +******************************************************************/ + +static void +CVDecreaseBDF(CVodeMem cv_mem) +{ + realtype hsum, xi; + int i, j; + + for (i = 0; i <= qmax; i++) + l[i] = ZERO; + l[2] = ONE; + hsum = ZERO; + for (j = 1; j <= q - 2; j++) + { + hsum += tau[j]; + xi = hsum / hscale; + for (i = j + 2; i >= 2; i--) + l[i] = l[i] * xi + l[i - 1]; + } + + for (j = 2; j < q; j++) + N_VLinearSum(-l[j], zn[q], ONE, zn[j], zn[j]); +} + +/**************** CVRescale *********************************** + + This routine rescales the Nordsieck array by multiplying the + jth column zn[j] by eta^j, j = 1, ..., q. Then the value of + h is rescaled by eta, and hscale is reset to h. + +***************************************************************/ + +static void +CVRescale(CVodeMem cv_mem) +{ + int j; + realtype factor; + + factor = eta; + for (j = 1; j <= q; j++) + { + N_VScale(factor, zn[j], zn[j]); + factor *= eta; + } + h = hscale * eta; + hscale = h; + nscon = 0; +} + +/********************* CVPredict ************************************* + + This routine advances tn by the tentative step size h, and computes + the predicted array z_n(0), which is overwritten on zn. The + prediction of zn is done by repeated additions. + +*********************************************************************/ + +static void +CVPredict(CVodeMem cv_mem) +{ + int j, k; + + tn += h; + for (k = 1; k <= q; k++) + for (j = q; j >= k; j--) + N_VLinearSum(ONE, zn[j - 1], ONE, zn[j], zn[j - 1]); +} + +/************************** CVSet ********************************* + + This routine is a high level routine which calls CVSetAdams or + CVSetBDF to set the polynomial l, the test quantity array tq, + and the related variables rl1, gamma, and gamrat. + +******************************************************************/ + +static void +CVSet(CVodeMem cv_mem) +{ + switch (lmm) + { + case ADAMS: + CVSetAdams(cv_mem); + break; + case BDF: + CVSetBDF(cv_mem); + break; + } + rl1 = ONE / l[1]; + gamma = h * rl1; + if (nst == 0) + gammap = gamma; + gamrat = (nst > 0) ? gamma / gammap : ONE; /* protect x / x != 1.0 */ +} + +/******************** CVSetAdams ********************************* + + This routine handles the computation of l and tq for the + case lmm == ADAMS. + + The components of the array l are the coefficients of a + polynomial Lambda(x) = l_0 + l_1 x + ... + l_q x^q, given by + q-1 + (d/dx) Lambda(x) = c * PRODUCT (1 + x / xi_i) , where + i=1 + Lambda(-1) = 0, Lambda(0) = 1, and c is a normalization factor. + Here xi_i = [t_n - t_(n-i)] / h. + + The array tq is set to test quantities used in the convergence + test, the error test, and the selection of h at a new order. + +*****************************************************************/ + +static void +CVSetAdams(CVodeMem cv_mem) +{ + realtype m[L_MAX], M[3], hsum; + + if (q == 1) + { + l[0] = l[1] = tq[1] = tq[5] = ONE; + tq[2] = TWO; + tq[3] = TWELVE; + tq[4] = CORTES * tq[2]; /* = 0.1 * tq[2] */ + return; + } + + hsum = CVAdamsStart(cv_mem, m); + + M[0] = CVAltSum(q - 1, m, 1); + M[1] = CVAltSum(q - 1, m, 2); + + CVAdamsFinish(cv_mem, m, M, hsum); +} + +/****************** CVAdamsStart ******************************** + + This routine generates in m[] the coefficients of the product + polynomial needed for the Adams l and tq coefficients for q > 1. + +******************************************************************/ + +static realtype +CVAdamsStart(CVodeMem cv_mem, realtype m[]) +{ + realtype hsum, xi_inv, sum; + int i, j; + + hsum = h; + m[0] = ONE; + for (i = 1; i <= q; i++) + m[i] = ZERO; + for (j = 1; j < q; j++) + { + if ((j == q - 1) && (qwait == 1)) + { + sum = CVAltSum(q - 2, m, 2); + tq[1] = m[q - 2] / (q * sum); + } + xi_inv = h / hsum; + for (i = j; i >= 1; i--) + m[i] += m[i - 1] * xi_inv; + hsum += tau[j]; + /* The m[i] are coefficients of product(1 to j) (1 + x/xi_i) */ + } + return (hsum); +} + +/****************** CVAdamsFinish ******************************* + + This routine completes the calculation of the Adams l and tq. + +******************************************************************/ + +static void +CVAdamsFinish(CVodeMem cv_mem, realtype m[], realtype M[], realtype hsum) +{ + int i; + realtype M0_inv, xi, xi_inv; + + M0_inv = ONE / M[0]; + + l[0] = ONE; + for (i = 1; i <= q; i++) + l[i] = M0_inv * (m[i - 1] / i); + xi = hsum / h; + xi_inv = ONE / xi; + + tq[2] = xi * M[0] / M[1]; + tq[5] = xi / l[q]; + + if (qwait == 1) + { + for (i = q; i >= 1; i--) + m[i] += m[i - 1] * xi_inv; + M[2] = CVAltSum(q, m, 2); + tq[3] = L * M[0] / M[2]; + } + + tq[4] = CORTES * tq[2]; +} + +/****************** CVAltSum ************************************** + + CVAltSum returns the value of the alternating sum + sum (i= 0 ... iend) [ (-1)^i * (a[i] / (i + k)) ]. + If iend < 0 then CVAltSum returns 0. + This operation is needed to compute the integral, from -1 to 0, + of a polynomial x^(k-1) M(x) given the coefficients of M(x). + +******************************************************************/ + +static realtype +CVAltSum(int iend, realtype a[], int k) +{ + int i, sign; + realtype sum; + + if (iend < 0) + return (ZERO); + + sum = ZERO; + sign = 1; + for (i = 0; i <= iend; i++) + { + sum += sign * (a[i] / (i + k)); + sign = -sign; + } + return (sum); +} + +/***************** CVSetBDF ************************************** + + This routine computes the coefficients l and tq in the case + lmm == BDF. CVSetBDF calls CVSetTqBDF to set the test + quantity array tq. + + The components of the array l are the coefficients of a + polynomial Lambda(x) = l_0 + l_1 x + ... + l_q x^q, given by + q-1 + Lambda(x) = (1 + x / xi*_q) * PRODUCT (1 + x / xi_i) , where + i=1 + xi_i = [t_n - t_(n-i)] / h. + + The array tq is set to test quantities used in the convergence + test, the error test, and the selection of h at a new order. + + +*****************************************************************/ + +static void +CVSetBDF(CVodeMem cv_mem) +{ + realtype alpha0, alpha0_hat, xi_inv, xistar_inv, hsum; + int i, j; + + l[0] = l[1] = xi_inv = xistar_inv = ONE; + for (i = 2; i <= q; i++) + l[i] = ZERO; + alpha0 = alpha0_hat = -ONE; + hsum = h; + if (q > 1) + { + for (j = 2; j < q; j++) + { + hsum += tau[j - 1]; + xi_inv = h / hsum; + alpha0 -= ONE / j; + for (i = j; i >= 1; i--) + l[i] += l[i - 1] * xi_inv; + /* The l[i] are coefficients of product(1 to j) (1 + x/xi_i) */ + } + + /* j = q */ + alpha0 -= ONE / q; + xistar_inv = -l[1] - alpha0; + hsum += tau[q - 1]; + xi_inv = h / hsum; + alpha0_hat = -l[1] - xi_inv; + for (i = q; i >= 1; i--) + l[i] += l[i - 1] * xistar_inv; + } + + CVSetTqBDF(cv_mem, hsum, alpha0, alpha0_hat, xi_inv, xistar_inv); +} + +/****************** CVSetTqBDF ************************************ + + This routine sets the test quantity array tq when lmm == BDF. + +******************************************************************/ + +static void +CVSetTqBDF(CVodeMem cv_mem, realtype hsum, realtype alpha0, + realtype alpha0_hat, realtype xi_inv, realtype xistar_inv) +{ + realtype A1, A2, A3, A4, A5, A6; + realtype C, CPrime, CPrimePrime; + + A1 = ONE - alpha0_hat + alpha0; + A2 = ONE + q * A1; + tq[2] = ABS(alpha0 * (A2 / A1)); + tq[5] = ABS((A2) / (l[q] * xi_inv / xistar_inv)); + if (qwait == 1) + { + C = xistar_inv / l[q]; + A3 = alpha0 + ONE / q; + A4 = alpha0_hat + xi_inv; + CPrime = A3 / (ONE - A4 + A3); + tq[1] = ABS(CPrime / C); + hsum += tau[q]; + xi_inv = h / hsum; + A5 = alpha0 - (ONE / (q + 1)); + A6 = alpha0_hat - xi_inv; + CPrimePrime = A2 / (ONE - A6 + A5); + tq[3] = ABS(CPrimePrime * xi_inv * (q + 2) * A5); + } + tq[4] = CORTES * tq[2]; +} + +/****************** CVnls ***************************************** + + This routine attempts to solve the nonlinear system associated + with a single implicit step of the linear multistep method. + Depending on iter, it calls CVnlsFunctional or CVnlsNewton + to do the work. + +******************************************************************/ + +static int +CVnls(CVodeMem cv_mem, int nflag) +{ + switch (iter) + { + case FUNCTIONAL: + return (CVnlsFunctional(cv_mem)); + case NEWTON: + return (CVnlsNewton(cv_mem, nflag)); + } + return (-99); +} + +/***************** CVnlsFunctional ******************************** + + This routine attempts to solve the nonlinear system using + functional iteration (no matrices involved). + +******************************************************************/ + +static int +CVnlsFunctional(CVodeMem cv_mem) +{ + int m; + realtype del, delp, dcon; + + /* Initialize counter and evaluate f at predicted y */ + + delp = 0; + crate = ONE; + m = 0; + f(N, tn, zn[0], tempv, f_data); + nfe++; + if (CVMEM cvode_error == TRUE) + { +#ifdef DEBUG_CVODE + CVMEM warning_msg("CVnlsFunctional, Fail at beginning\n"); +#endif + return (CONV_FAIL); + } + else + { +#ifdef DEBUG_CVODE + CVMEM warning_msg("CVnlsFunctional, OK at beginning\n"); +#endif + } + N_VConst(ZERO, acor); + + /* Loop until convergence; accumulate corrections in acor */ + + loop + { + /* Correct y directly from the last f value */ + N_VLinearSum(h, tempv, -ONE, zn[1], tempv); + N_VScale(rl1, tempv, tempv); + N_VLinearSum(ONE, zn[0], ONE, tempv, y); + /* Get WRMS norm of current correction to use in convergence test */ + N_VLinearSum(ONE, tempv, -ONE, acor, acor); + del = N_VWrmsNorm(acor, ewt); + N_VScale(ONE, tempv, acor); + + /* Test for convergence. If m > 0, an estimate of the convergence + rate constant is stored in crate, and used in the test. */ + if (m > 0) + crate = MAX(CRDOWN * crate, del / delp); + dcon = del * MIN(ONE, crate) / tq[4]; + if (dcon <= ONE) + { + acnrm = (m == 0) ? del : N_VWrmsNorm(acor, ewt); + return (SOLVED); /* Convergence achieved */ + } + + /* Stop at maxcor iterations or if iter. seems to be diverging */ + m++; + if ((m == maxcor) || ((m >= 2) && (del > RDIV * delp))) + return (CONV_FAIL); + /* Save norm of correction, evaluate f, and loop again */ + delp = del; + f(N, tn, y, tempv, f_data); + if (CVMEM cvode_error == TRUE) + { +#ifdef DEBUG_CVODE + CVMEM warning_msg("CVnlsFunctional, Fail at end\n"); +#endif + return (CONV_FAIL); + } + else + { +#ifdef DEBUG_CVODE + CVMEM warning_msg("CVnlsFunctional, OK at end\n"); +#endif + } + nfe++; + } +} + +/*********************** CVnlsNewton ********************************** + + This routine handles the Newton iteration. It calls lsetup if + indicated, calls CVNewtonIteration to perform the iteration, and + retries a failed attempt at Newton iteration if that is indicated. + See return values at top of this file. + +**********************************************************************/ + +static int +CVnlsNewton(CVodeMem cv_mem, int nflag) +{ + N_Vector vtemp1, vtemp2, vtemp3; + int convfail, ier; + booleantype callSetup; + + vtemp1 = acor; /* rename acor as vtemp1 for readability */ + vtemp2 = y; /* rename y as vtemp2 for readability */ + vtemp3 = tempv; /* rename tempv as vtemp3 for readability */ + + /* Set flag convfail, input to lsetup for its evaluation decision */ + convfail = ((nflag == FIRST_CALL) || (nflag == PREV_ERR_FAIL)) ? + NO_FAILURES : FAIL_OTHER; + + /* Decide whether or not to call setup routine (if one exists) */ + if (setupNonNull) + { + callSetup = (nflag == PREV_CONV_FAIL) || (nflag == PREV_ERR_FAIL) || + (nst == 0) || (nst >= nstlp + MSBP) + || (ABS(gamrat - ONE) > DGMAX); + } + else + { + crate = ONE; + callSetup = FALSE; + } + + /* Looping point for the solution of the nonlinear system. + Evaluate f at the predicted y, call lsetup if indicated, and + call CVNewtonIteration for the Newton iteration itself. */ + + loop + { + + f(N, tn, zn[0], ftemp, f_data); + + nfe++; + if (CVMEM cvode_error == TRUE) + { +#ifdef DEBUG_CVODE + CVMEM warning_msg(CVMEM sformatf("CVnlsNewton, start of loop, time %e\n", + tn)); +#endif + return (CONV_FAIL); + } + else + { +#ifdef DEBUG_CVODE + CVMEM warning_msg(CVMEM sformatf( + "CVnlsNewton, OK, start of loop, time %e\n", tn)); +#endif + } + + if (callSetup) + { + ier = lsetup(cv_mem, convfail, zn[0], ftemp, &jcur, + vtemp1, vtemp2, vtemp3); + nsetups++; + callSetup = FALSE; + gamrat = crate = ONE; + gammap = gamma; + nstlp = nst; + /* Return if lsetup failed */ + if (ier < 0) + return (SETUP_FAIL_UNREC); + if (ier > 0) + return (CONV_FAIL); + } + + /* Set acor to zero and load prediction into y vector */ + N_VConst(ZERO, acor); + N_VScale(ONE, zn[0], y); + + /* Do the Newton iteration */ + ier = CVNewtonIteration(cv_mem); + if (CVMEM cvode_error == TRUE) + { + return (CONV_FAIL); + } + CVMEM cvode_test = TRUE; + f(N, tn, y, ftemp, f_data); + CVMEM cvode_test = FALSE; + if (CVMEM cvode_error == TRUE) + { +#ifdef DEBUG_CVODE + CVMEM warning_msg(CVMEM sformatf( + "After CVNewtonIteration, Fail, ier %d\n", ier)); +#endif + return (CONV_FAIL); + } + else + { +#ifdef DEBUG_CVODE + CVMEM warning_msg(CVMEM sformatf("After CVNewtonIteration, OK, ier %d\n", + ier)); +#endif + } + /* If there is a convergence failure and the Jacobian-related + data appears not to be current, loop again with a call to lsetup + in which convfail=FAIL_BAD_J. Otherwise return. */ + if (ier != TRY_AGAIN_CVODE) + return (ier); + callSetup = TRUE; + convfail = FAIL_BAD_J; + } +} + +/********************** CVNewtonIteration **************************** + + This routine performs the Newton iteration. If the iteration succeeds, + it returns the value SOLVED. If not, it may signal the CVnlsNewton + routine to call lsetup again and reattempt the iteration, by + returning the value TRY_AGAIN_CVODE. (In this case, CVnlsNewton must set + convfail to FAIL_BAD_J before calling setup again). + Otherwise, this routine returns one of the appropriate values + SOLVE_FAIL_UNREC or CONV_FAIL back to CVnlsNewton. + +*********************************************************************/ + +static int +CVNewtonIteration(CVodeMem cv_mem) +{ + int m, ret; + realtype del, delp, dcon; + N_Vector b; + + + mnewt = m = 0; + delp = 0; + + /* Looping point for Newton iteration */ + loop + { + + /* Evaluate the residual of the nonlinear system */ + N_VLinearSum(rl1, zn[1], ONE, acor, tempv); + N_VLinearSum(gamma, ftemp, -ONE, tempv, tempv); + + /* Call the lsolve function */ + b = tempv; + ret = lsolve(cv_mem, b, y, ftemp); + nni++; + + if (ret < 0) + return (SOLVE_FAIL_UNREC); + + /* If lsolve had a recoverable failure and Jacobian data is + not current, signal to try the solution again */ + if (ret > 0) + { + if ((!jcur) && (setupNonNull)) + return (TRY_AGAIN_CVODE); + return (CONV_FAIL); + } + /* Get WRMS norm of correction; add correction to acor and y */ + del = N_VWrmsNorm(b, ewt); + N_VLinearSum(ONE, acor, ONE, b, acor); + N_VLinearSum(ONE, zn[0], ONE, acor, y); + + /* Test for convergence. If m > 0, an estimate of the convergence + rate constant is stored in crate, and used in the test. */ + if (m > 0) + { + crate = MAX(CRDOWN * crate, del / delp); + } + dcon = del * MIN(ONE, crate) / tq[4]; + +#ifdef DEBUG_CVODE + /* added before SOLVED */ + cvode_test = TRUE; + f(N, tn, y, ftemp, f_data); + cvode_test = FALSE; + if (cvode_error == TRUE) + { + CVMEM warning_msg("CVnlsNewton, Fail at SOLVED\n"); + return (CONV_FAIL); + } + else + { + CVMEM warning_msg("CVnlsNewton, OK at SOLVED\n"); + } +#endif + if (dcon <= ONE) + { + acnrm = (m == 0) ? del : N_VWrmsNorm(acor, ewt); + jcur = FALSE; + return (SOLVED); /* Nonlinear system was solved successfully */ + } + + mnewt = ++m; + + /* Stop at maxcor iterations or if iter. seems to be diverging. + If still not converged and Jacobian data is not current, + signal to try the solution again */ + if ((m == maxcor) || ((m >= 2) && (del > RDIV * delp))) + { + if ((!jcur) && (setupNonNull)) + return (TRY_AGAIN_CVODE); + return (CONV_FAIL); + } + + /* Save norm of correction, evaluate f, and loop again */ + delp = del; + f(N, tn, y, ftemp, f_data); + + if (CVMEM cvode_error == TRUE) + { +#ifdef DEBUG_CVODE + CVMEM warning_msg("CVnlsNewton, Fail at end\n"); +#endif + return (CONV_FAIL); + } + else + { +#ifdef DEBUG_CVODE + CVMEM warning_msg("CVnlsNewton, OK at end\n"); +#endif + } + nfe++; + } +} + +/********************** CVHandleNFlag ******************************* + + This routine takes action on the return value nflag = *nflagPtr + returned by CVnls, as follows: + + If CVnls succeeded in solving the nonlinear system, then + CVHandleNFlag returns the constant DO_ERROR_TEST, which tells CVStep + to perform the error test. + + If the nonlinear system was not solved successfully, then ncfn and + ncf = *ncfPtr are incremented and Nordsieck array zn is restored. + + If the solution of the nonlinear system failed due to an + unrecoverable failure by setup, we return the value SETUP_FAILED. + + If it failed due to an unrecoverable failure in solve, then we return + the value SOLVE_FAILED. + + Otherwise, a recoverable failure occurred when solving the + nonlinear system (CVnls returned nflag == CONV_FAIL). + In this case, we return the value REP_CONV_FAIL if ncf is now + equal to MXNCF or |h| = hmin. + If not, we set *nflagPtr = PREV_CONV_FAIL and return the value + PREDICT_AGAIN, telling CVStep to reattempt the step. + +*********************************************************************/ + +static int +CVHandleNFlag(CVodeMem cv_mem, int *nflagPtr, realtype saved_t, int *ncfPtr) +{ + int nflag; + + nflag = *nflagPtr; + + if (nflag == SOLVED) + return (DO_ERROR_TEST); + + /* The nonlinear soln. failed; increment ncfn and restore zn */ + ncfn++; + CVRestore(cv_mem, saved_t); + + /* Return if lsetup or lsolve failed unrecoverably */ + if (nflag == SETUP_FAIL_UNREC) + return (SETUP_FAILED); + if (nflag == SOLVE_FAIL_UNREC) + return (SOLVE_FAILED); + + /* At this point, nflag == CONV_FAIL; increment ncf */ + + (*ncfPtr)++; + etamax = ONE; + /* If we had MXNCF failures or |h| = hmin, return REP_CONV_FAIL */ + if ((ABS(h) <= hmin * ONEPSM) || (*ncfPtr == MXNCF)) + return (REP_CONV_FAIL); + + /* Reduce step size; return to reattempt the step */ + eta = MAX(ETACF, hmin / ABS(h)); + *nflagPtr = PREV_CONV_FAIL; + CVRescale(cv_mem); + return (PREDICT_AGAIN); +} + +/********************** CVRestore ************************************ + + This routine restores the value of tn to saved_t and undoes the + prediction. After execution of CVRestore, the Nordsieck array zn has + the same values as before the call to CVPredict. + +********************************************************************/ + +static void +CVRestore(CVodeMem cv_mem, realtype saved_t) +{ + int j, k; + + tn = saved_t; + for (k = 1; k <= q; k++) + for (j = q; j >= k; j--) + N_VLinearSum(ONE, zn[j - 1], -ONE, zn[j], zn[j - 1]); +} + +/******************* CVDoErrorTest ******************************** + + This routine performs the local error test. + The weighted local error norm dsm is loaded into *dsmPtr, and + the test dsm ?<= 1 is made. + + If the test passes, CVDoErrorTest returns TRUE. + + If the test fails, we undo the step just taken (call CVRestore), + set *nflagPtr to PREV_ERR_FAIL, and return FALSE. + + If MXNEF error test failures have occurred or if ABS(h) = hmin, + we set *kflagPtr = REP_ERR_FAIL. (Otherwise *kflagPtr has the + value last returned by CVHandleNflag.) + + If more than MXNEF1 error test failures have occurred, an order + reduction is forced. + +******************************************************************/ + +static booleantype +CVDoErrorTest(CVodeMem cv_mem, int *nflagPtr, int *kflagPtr, + realtype saved_t, int *nefPtr, realtype * dsmPtr) +{ + realtype dsm; + + dsm = acnrm / tq[2]; + + /* If est. local error norm dsm passes test, return TRUE */ + *dsmPtr = dsm; + if (dsm <= ONE) + return (TRUE); + + /* Test failed; increment counters, set nflag, and restore zn array */ + (*nefPtr)++; + netf++; + *nflagPtr = PREV_ERR_FAIL; + CVRestore(cv_mem, saved_t); + + /* At MXNEF failures or |h| = hmin, return with kflag = REP_ERR_FAIL */ + if ((ABS(h) <= hmin * ONEPSM) || (*nefPtr == MXNEF)) + { + *kflagPtr = REP_ERR_FAIL; + return (FALSE); + } + + /* Set etamax = 1 to prevent step size increase at end of this step */ + etamax = ONE; + + /* Set h ratio eta from dsm, rescale, and return for retry of step */ + if (*nefPtr <= MXNEF1) + { + eta = ONE / (RPowerR(BIAS2 * dsm, ONE / L) + ADDON); + eta = MAX(ETAMIN, MAX(eta, hmin / ABS(h))); + if (*nefPtr >= SMALL_NEF) + eta = MIN(eta, ETAMXF); + CVRescale(cv_mem); + return (FALSE); + } + + /* After MXNEF1 failures, force an order reduction and retry step */ + if (q > 1) + { + eta = MAX(ETAMIN, hmin / ABS(h)); + CVAdjustOrder(cv_mem, -1); + L = q; + q--; + qwait = L; + CVRescale(cv_mem); + return (FALSE); + } + + /* If already at order 1, restart: reload zn from scratch */ + eta = MAX(ETAMIN, hmin / ABS(h)); + h *= eta; + hscale = h; + qwait = LONG_WAIT; + nscon = 0; + f(N, tn, zn[0], tempv, f_data); + if (CVMEM cvode_error == TRUE) + { + CVMEM warning_msg("CVDoErrorTest"); + /*exit(8); */ + CVMEM error_msg("CVDoErrorTest", 1 /* STOP */ ); +#if !defined(R_SO) + exit(4); +#endif + } + nfe++; + N_VScale(h, tempv, zn[1]); + return (FALSE); +} + +/*************** CVCompleteStep ********************************** + + This routine performs various update operations when the solution + to the nonlinear system has passed the local error test. + We increment the step counter nst, record the values hu and qu, + update the tau array, and apply the corrections to the zn array. + The tau[i] are the last q values of h, with tau[1] the most recent. + The counter qwait is decremented, and if qwait == 1 (and q < qmax) + we save acor and tq[5] for a possible order increase. + +******************************************************************/ + +static void +CVCompleteStep(CVodeMem cv_mem) +{ + int i, j; + + nst++; + nscon++; + hu = h; + qu = q; + + for (i = q; i >= 2; i--) + tau[i] = tau[i - 1]; + if ((q == 1) && (nst > 1)) + tau[2] = tau[1]; + tau[1] = h; + + for (j = 0; j <= q; j++) + N_VLinearSum(l[j], acor, ONE, zn[j], zn[j]); + qwait--; + if ((qwait == 1) && (q != qmax)) + { + N_VScale(ONE, acor, zn[qmax]); + saved_tq5 = tq[5]; + } +} + +/************* CVPrepareNextStep ********************************** + + This routine handles the setting of stepsize and order for the + next step -- hprime and qprime. Along with hprime, it sets the + ratio eta = hprime/h. It also updates other state variables + related to a change of step size or order. + +******************************************************************/ + +static void +CVPrepareNextStep(CVodeMem cv_mem, realtype dsm) +{ + /* If etamax = 1, defer step size or order changes */ + if (etamax == ONE) + { + qwait = MAX(qwait, 2); + qprime = q; + hprime = h; + eta = ONE; + return; + } + + /* etaq is the ratio of new to old h at the current order */ + etaq = ONE / (RPowerR(BIAS2 * dsm, ONE / L) + ADDON); + + /* If no order change, adjust eta and acor in CVSetEta and return */ + if (qwait != 0) + { + eta = etaq; + qprime = q; + CVSetEta(cv_mem); + return; + } + + /* If qwait = 0, consider an order change. etaqm1 and etaqp1 are + the ratios of new to old h at orders q-1 and q+1, respectively. + CVChooseEta selects the largest; CVSetEta adjusts eta and acor */ + qwait = 2; + etaqm1 = CVComputeEtaqm1(cv_mem); + etaqp1 = CVComputeEtaqp1(cv_mem); + CVChooseEta(cv_mem); + CVSetEta(cv_mem); +} + +/***************** CVSetEta *************************************** + + This routine adjusts the value of eta according to the various + heuristic limits and the optional input hmax. It also resets + etamax to be the estimated local error vector. + +*******************************************************************/ + +static void +CVSetEta(CVodeMem cv_mem) +{ + + /* If eta below the threshhold THRESH, reject a change of step size */ + if (eta < THRESH) + { + eta = ONE; + hprime = h; + } + else + { + /* Limit eta by etamax and hmax, then set hprime */ + eta = MIN(eta, etamax); + eta /= MAX(ONE, ABS(h) * hmax_inv * eta); + hprime = h * eta; + if (qprime < q) + nscon = 0; + } + + /* Reset etamax for the next step size change, and scale acor */ +} + +/*************** CVComputeEtaqm1 ********************************** + + This routine computes and returns the value of etaqm1 for a + possible decrease in order by 1. + +******************************************************************/ + +static realtype +CVComputeEtaqm1(CVodeMem cv_mem) +{ + realtype ddn; + + etaqm1 = ZERO; + if (q > 1) + { + ddn = N_VWrmsNorm(zn[q], ewt) / tq[1]; + etaqm1 = ONE / (RPowerR(BIAS1 * ddn, ONE / q) + ADDON); + } + return (etaqm1); +} + +/*************** CVComputeEtaqp1 ********************************** + + This routine computes and returns the value of etaqp1 for a + possible increase in order by 1. + +******************************************************************/ + +static realtype +CVComputeEtaqp1(CVodeMem cv_mem) +{ + realtype dup, cquot; + + etaqp1 = ZERO; + if (q != qmax) + { + cquot = (tq[5] / saved_tq5) * RPowerI(h / tau[2], L); + N_VLinearSum(-cquot, zn[qmax], ONE, acor, tempv); + dup = N_VWrmsNorm(tempv, ewt) / tq[3]; + etaqp1 = ONE / (RPowerR(BIAS3 * dup, ONE / (L + 1)) + ADDON); + } + return (etaqp1); +} + +/******************* CVChooseEta ********************************** + + Given etaqm1, etaq, etaqp1 (the values of eta for qprime = + q - 1, q, or q + 1, respectively), this routine chooses the + maximum eta value, sets eta to that value, and sets qprime to the + corresponding value of q. If there is a tie, the preference + order is to (1) keep the same order, then (2) decrease the order, + and finally (3) increase the order. If the maximum eta value + is below the threshhold THRESH, the order is kept unchanged and + eta is set to 1. + +******************************************************************/ + +static void +CVChooseEta(CVodeMem cv_mem) +{ + realtype etam; + + etam = MAX(etaqm1, MAX(etaq, etaqp1)); + + if (etam < THRESH) + { + eta = ONE; + qprime = q; + return; + } + + if (etam == etaq) + { + eta = etaq; + qprime = q; + } + else if (etam == etaqm1) + { + eta = etaqm1; + qprime = q - 1; + } + else + { + eta = etaqp1; + qprime = q + 1; + if (lmm == BDF) + N_VScale(ONE, acor, zn[qmax]); + } +} + +/****************** CVHandleFailure ****************************** + + This routine prints error messages for all cases of failure by + CVStep. It returns to CVode the value that CVode is to return to + the user. + +*****************************************************************/ + +static int +CVHandleFailure(CVodeMem cv_mem, int kflag) +{ + /* Set vector of absolute weighted local errors */ + N_VProd(acor, ewt, tempv); + N_VAbs(tempv, tempv); + + /* Depending on kflag, print error message and return error flag */ + switch (kflag) + { + case REP_ERR_FAIL: + { + char * error_string = CVMEM sformatf(MSG_ERR_FAILS, (double) tn, (double) h); + CVMEM warning_msg(error_string); + } + return (ERR_FAILURE); + case REP_CONV_FAIL: + { + char * error_string = CVMEM sformatf(MSG_CONV_FAILS, (double) tn, (double) h); + CVMEM warning_msg(error_string); + } + + return (CONV_FAILURE); + case SETUP_FAILED: + { + char * error_string = CVMEM sformatf(MSG_SETUP_FAILED, (double) tn); + CVMEM warning_msg(error_string); + } + return (SETUP_FAILURE); + case SOLVE_FAILED: + { + char * error_string = CVMEM sformatf(MSG_SOLVE_FAILED, (double) tn); + CVMEM warning_msg(error_string); + } + return (SOLVE_FAILURE); + } + return (-99); +} + +/****************** CVBDFStab *********************************** + This routine handles the BDF Stability Limit Detection Algorithm + STALD. It is called if lmm = BDF and the SLDET option is on. + If the order is 3 or more, the required norm data is saved. + If a decision to reduce order has not already been made, and + enough data has been saved, CVsldet is called. If it signals + a stability limit violation, the order is reduced, and the step + size is reset accordingly. + +*****************************************************************/ + +void +CVBDFStab(CVodeMem cv_mem) +{ + int i, k, ldflag, factorial; + realtype sq, sqm1, sqm2; + + /* If order is 3 or greater, then save scaled derivative data, + push old data down in i, then add current values to top. */ + + if (q >= 3) + { + for (k = 1; k <= 3; k++) + { + for (i = 5; i >= 2; i--) + ssdat[i][k] = ssdat[i - 1][k]; + } + factorial = 1; + for (i = 1; i <= q - 1; i++) + factorial *= i; + sq = factorial * q * (q + 1) * acnrm / tq[5]; + sqm1 = factorial * q * N_VWrmsNorm(zn[q], ewt); + sqm2 = factorial * N_VWrmsNorm(zn[q - 1], ewt); + ssdat[1][1] = sqm2 * sqm2; + ssdat[1][2] = sqm1 * sqm1; + ssdat[1][3] = sq * sq; + } + + if (qprime >= q) + { + + /* If order is 3 or greater, and enough ssdat has been saved, + nscon >= q+5, then call stability limit detection routine. */ + + if ((q >= 3) && (nscon >= q + 5)) + { + ldflag = CVsldet(cv_mem); + if (ldflag > 3) + { + /* A stability limit violation is indicated by + a return flag of 4, 5, or 6. + Reduce new order. */ + qprime = q - 1; + eta = etaqm1; + eta = MIN(eta, etamax); + eta = eta / MAX(ONE, ABS(h) * hmax_inv * eta); + hprime = h * eta; + iopt[NOR] = iopt[NOR] + 1; + /* CVMEM warning_msg(CVMEM sformatf( + " Order reduced to %d by CVBDFStab at nst = %d,\n h = %e hnew = %e\n", + qprime,nst,h,h*eta)); */ + } + } + } + else + { + /* Otherwise, let order increase happen, and + reset stability limit counter, nscon. */ + nscon = 0; + } +} + +/********************* CVsldet ************************************ + This routine detects stability limitation using stored scaled + derivatives data. CVsldet returns the magnitude of the + dominate characteristic root, rr. The presents of a stability + limit is indicated by rr > "something a little less then 1.0", + and a positive kflag. This routine should only be called if + order is greater than or equal to 3, and data has been collected + for 5 time steps. + + Returned values: + kflag = 1 -> Found stable characteristic root, normal matrix case + kflag = 2 -> Found stable characteristic root, quartic solution + kflag = 3 -> Found stable characteristic root, quartic solution, + with Newton correction + kflag = 4 -> Found stability violation, normal matrix case + kflag = 5 -> Found stability violation, quartic solution + kflag = 6 -> Found stability violation, quartic solution, + with Newton correction + + kflag < 0 -> No stability limitation, + or could not compute limitation. + + kflag = -1 -> Min/max ratio of ssdat too small. + kflag = -2 -> For normal matrix case, vmax > vrrt2*vrrt2 + kflag = -3 -> For normal matrix case, The three ratios + are inconsistent. + kflag = -4 -> Small coefficient prevents elimination of quartics. + kflag = -5 -> R value from quartics not consistent. + kflag = -6 -> No corrected root passes test on qk values + kflag = -7 -> Trouble solving for sigsq. + kflag = -8 -> Trouble solving for B, or R via B. + kflag = -9 -> R via sigsq[k] disagrees with R from data. + +********************************************************************/ + +static int +CVsldet(CVodeMem cv_mem) +{ + integertype i, k, j, it, kmin, kflag = 0; + realtype rat[5][4], rav[4], qkr[4], sigsq[4], smax[4], ssmax[4]; + realtype drr[4], rrc[4], sqmx[4], qjk[4][4], vrat[5], qc[6][4], qco[6][4]; + realtype rr, rrcut, vrrtol, vrrt2, sqtol, rrtol; + realtype smink, smaxk, sumrat, sumrsq, vmin, vmax, drrmax, adrr; + realtype /*small_cvode,*/ tem, sqmax, saqk, qp, s, sqmaxk, saqj, sqmin; + realtype rsa, rsb, rsc, rsd, rse, rd1a, rd1b, rd1c, rd1d; + realtype rd2a, rd2b, rd2c, rd3a, rd3b, cest1, corr1; + realtype ratp, ratm, qfac1, qfac2, bb, rrb; + + /* The following are cutoffs and tolerances used by this routine */ + + rrcut = 0.98; + vrrtol = 1.0e-4; + vrrt2 = 5.0e-4; + sqtol = 1.0e-3; + rrtol = 1.0e-2; + + rr = ZERO; + + /* Index k corresponds to the degree of the interpolating polynomial. */ + /* k = 1 -> q-1 */ + /* k = 2 -> q */ + /* k = 3 -> q+1 */ + + /* Index i is a backward-in-time index, i = 1 -> current time, */ + /* i = 2 -> previous step, etc */ + + /* get maxima, minima, and variances, and form quartic coefficients */ + + for (k = 1; k <= 3; k++) + { + smink = ssdat[1][k]; + smaxk = ZERO; + + for (i = 1; i <= 5; i++) + { + smink = MIN(smink, ssdat[i][k]); + smaxk = MAX(smaxk, ssdat[i][k]); + } + + if (smink < TINY * smaxk) + { + kflag = -1; + return (kflag); + } + smax[k] = smaxk; + ssmax[k] = smaxk * smaxk; + + sumrat = ZERO; + sumrsq = ZERO; + for (i = 1; i <= 4; i++) + { + rat[i][k] = ssdat[i][k] / ssdat[i + 1][k]; + sumrat = sumrat + rat[i][k]; + sumrsq = sumrsq + rat[i][k] * rat[i][k]; + } + rav[k] = FOURTH * sumrat; + vrat[k] = ABS(FOURTH * sumrsq - rav[k] * rav[k]); + + qc[5][k] = ssdat[1][k] * ssdat[3][k] - ssdat[2][k] * ssdat[2][k]; + qc[4][k] = ssdat[2][k] * ssdat[3][k] - ssdat[1][k] * ssdat[4][k]; + qc[3][k] = ZERO; + qc[2][k] = ssdat[2][k] * ssdat[5][k] - ssdat[3][k] * ssdat[4][k]; + qc[1][k] = ssdat[4][k] * ssdat[4][k] - ssdat[3][k] * ssdat[5][k]; + + for (i = 1; i <= 5; i++) + { + qco[i][k] = qc[i][k]; + } + } /* End of k loop */ + + /* Isolate normal or nearly-normal matrix case. Three quartic will + have common or nearly-common roots in this case. + Return a kflag = 1 if this procedure works. If three root + differ more than vrrt2, return error kflag = -3. */ + + vmin = MIN(vrat[1], MIN(vrat[2], vrat[3])); + vmax = MAX(vrat[1], MAX(vrat[2], vrat[3])); + + if (vmin < vrrtol * vrrtol) + { + if (vmax > vrrt2 * vrrt2) + { + kflag = -2; + return (kflag); + } + else + { + rr = (rav[1] + rav[2] + rav[3]) / THREE; + + drrmax = ZERO; + for (k = 1; k <= 3; k++) + { + adrr = ABS(rav[k] - rr); + drrmax = MAX(drrmax, adrr); + } + if (drrmax > vrrt2) + { + kflag = -3; + } + + kflag = 1; + + /* can compute charactistic root, drop to next section */ + + } + } + else + { + + /* use the quartics to get rr. */ + + if (ABS(qco[1][1]) < TINY * ssmax[1]) + { + //small_cvode = qco[1][1]; + kflag = -4; + return (kflag); + } + + tem = qco[1][2] / qco[1][1]; + for (i = 2; i <= 5; i++) + { + qco[i][2] = qco[i][2] - tem * qco[i][1]; + } + + qco[1][2] = ZERO; + tem = qco[1][3] / qco[1][1]; + for (i = 2; i <= 5; i++) + { + qco[i][3] = qco[i][3] - tem * qco[i][1]; + } + qco[1][3] = ZERO; + + if (ABS(qco[2][2]) < TINY * ssmax[2]) + { + //small_cvode = qco[2][2]; + kflag = -4; + return (kflag); + } + + tem = qco[2][3] / qco[2][2]; + for (i = 3; i <= 5; i++) + { + qco[i][3] = qco[i][3] - tem * qco[i][2]; + } + + if (ABS(qco[4][3]) < TINY * ssmax[3]) + { + //small_cvode = qco[4][3]; + kflag = -4; + return (kflag); + } + + rr = -qco[5][3] / qco[4][3]; + + if (rr < TINY || rr > HUN) + { + kflag = -5; + return (kflag); + } + + for (k = 1; k <= 3; k++) + { + qkr[k] = + qc[5][k] + rr * (qc[4][k] + + rr * rr * (qc[2][k] + rr * qc[1][k])); + } + + sqmax = ZERO; + for (k = 1; k <= 3; k++) + { + saqk = ABS(qkr[k]) / ssmax[k]; + if (saqk > sqmax) + sqmax = saqk; + } + sqmin = sqmax; + if (sqmax < sqtol) + { + kflag = 2; + + /* can compute charactistic root, drop to "given rr,etc" */ + + } + else + { + + /* do Newton corrections to improve rr. */ + + for (it = 1; it <= 3; it++) + { + for (k = 1; k <= 3; k++) + { + qp = qc[4][k] + rr * rr * (THREE * qc[2][k] + + rr * FOUR * qc[1][k]); + drr[k] = ZERO; + if (ABS(qp) > TINY * ssmax[k]) + drr[k] = -qkr[k] / qp; + rrc[k] = rr + drr[k]; + } + + for (k = 1; k <= 3; k++) + { + s = rrc[k]; + sqmaxk = ZERO; + for (j = 1; j <= 3; j++) + { + qjk[j][k] = qc[5][j] + s * (qc[4][j] + + s * s * (qc[2][j] + + s * qc[1][j])); + saqj = ABS(qjk[j][k]) / ssmax[j]; + if (saqj > sqmaxk) + sqmaxk = saqj; + } + sqmx[k] = sqmaxk; + } + + sqmin = sqmx[1]; + kmin = 1; + for (k = 2; k <= 3; k++) + { + if (sqmx[k] < sqmin) + { + kmin = k; + sqmin = sqmx[k]; + } + } + rr = rrc[kmin]; + + if (sqmin < sqtol) + { + kflag = 3; + /* can compute charactistic root */ + /* break out of Newton correction loop and drop to "given rr,etc" */ + break; + } + else + { + for (j = 1; j <= 3; j++) + { + qkr[j] = qjk[j][kmin]; + } + } + } /* end of Newton correction loop */ + + if (sqmin > sqtol) + { + kflag = -6; + return (kflag); + } + } /* end of if (sqmax < sqtol) else */ + } /* end of if(vmin < vrrtol*vrrtol) else, quartics to get rr. */ + + /* given rr, find sigsq[k] and verify rr. */ + /* All positive kflag drop to this section */ + + for (k = 1; k <= 3; k++) + { + rsa = ssdat[1][k]; + rsb = ssdat[2][k] * rr; + rsc = ssdat[3][k] * rr * rr; + rsd = ssdat[4][k] * rr * rr * rr; + rse = ssdat[5][k] * rr * rr * rr * rr; + rd1a = rsa - rsb; + rd1b = rsb - rsc; + rd1c = rsc - rsd; + rd1d = rsd - rse; + rd2a = rd1a - rd1b; + rd2b = rd1b - rd1c; + rd2c = rd1c - rd1d; + rd3a = rd2a - rd2b; + rd3b = rd2b - rd2c; + rd3b = rd3b; + + if (ABS(rd1b) < TINY * smax[k]) + { + kflag = -7; + return (kflag); + } + + cest1 = -rd3a / rd1b; + if (cest1 < TINY || cest1 > FOUR) + { + kflag = -7; + return (kflag); + } + corr1 = (rd2b / cest1) / (rr * rr); + sigsq[k] = ssdat[3][k] + corr1; + } + + if (sigsq[2] < TINY) + { + kflag = -8; + return (kflag); + } + + ratp = sigsq[3] / sigsq[2]; + ratm = sigsq[1] / sigsq[2]; + qfac1 = FOURTH * (q * q - ONE); + qfac2 = TWO / (q - ONE); + bb = ratp * ratm - ONE - qfac1 * ratp; + tem = ONE - qfac2 * bb; + + if (ABS(tem) < TINY) + { + kflag = -8; + return (kflag); + } + + rrb = ONE / tem; + + if (ABS(rrb - rr) > rrtol) + { + kflag = -9; + return (kflag); + } + + /* Check to see if rr is above cutoff rrcut */ + if (rr > rrcut) + { + if (kflag == 1) + kflag = 4; + if (kflag == 2) + kflag = 5; + if (kflag == 3) + kflag = 6; + } + + /* All positive kflag returned at this point */ + + return (kflag); + +} + + +/*******************************************************************/ +/********* END Private Helper Functions Implementation *************/ +/*******************************************************************/ + + +/***************************************************************/ +/************** END CVODE Implementation ***********************/ +/***************************************************************/ diff --git a/phreeqcpp/cvode.h b/phreeqcpp/cvode.h new file mode 100644 index 00000000..57e72692 --- /dev/null +++ b/phreeqcpp/cvode.h @@ -0,0 +1,940 @@ +#ifndef _INC_CVODE_H +#define _INC_CVODE_H +/************************************************************************** + * * + * File : cvode.h * + * Programmers : Scott D. Cohen, Alan C. Hindmarsh, Radu Serban * + * and Dan Shumaker @ LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the interface file for the main CVODE integrator. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ +#ifndef _cvode_h +#define _cvode_h + + +#include +#include "sundialstypes.h" +#include "nvector.h" + +#undef SS + +/****************************************************************** + * * + * CVODE is used to solve numerically the ordinary initial value * + * problem : * + * * + * y' = f(t,y), * + * y(t0) = y0, * + * * + * where t0, y0 in R^N, and f: R x R^N -> R^N are given. * + * * + ******************************************************************/ + + +/****************************************************************** + * * + * Enumerations for inputs to CVodeMalloc, CVReInit, and CVode. * + *----------------------------------------------------------------* + * Symbolic constants for the lmm, iter, and itol input * + * parameters to CVodeMalloc and CVReInit, as well as the input * + * parameter itask to CVode, are given below. * + * * + * lmm : The user of the CVODE package specifies whether to use * + * the ADAMS or BDF (backward differentiation formula) * + * linear multistep method. The BDF method is recommended * + * for stiff problems, and the ADAMS method is recommended * + * for nonstiff problems. * + * * + * iter : At each internal time step, a nonlinear equation must * + * be solved. The user can specify either FUNCTIONAL * + * iteration, which does not require linear algebra, or a * + * NEWTON iteration, which requires the solution of linear * + * systems. In the NEWTON case, the user also specifies a * + * CVODE linear solver. NEWTON is recommended in case of * + * stiff problems. * + * * + * itol : This parameter specifies the relative and absolute * + * tolerance types to be used. The SS tolerance type means * + * a scalar relative and absolute tolerance, while the SV * + * tolerance type means a scalar relative tolerance and a * + * vector absolute tolerance (a potentially different * + * absolute tolerance for each vector component). * + * * + * itask : The itask input parameter to CVode indicates the job * + * of the solver for the next user step. The NORMAL * + * itask is to have the solver take internal steps until * + * it has reached or just passed the user specified tout * + * parameter. The solver then interpolates in order to * + * return an approximate value of y(tout). The ONE_STEP * + * option tells the solver to just take one internal step * + * and return the solution at the point reached by that * + * step. * + * * + ******************************************************************/ + + enum + { ADAMS, BDF }; /* lmm */ + + enum + { FUNCTIONAL, NEWTON }; /* iter */ + + enum + { SS, SV }; /* itol */ + + enum + { NORMAL, ONE_STEP }; /* itask */ + + +/****************************************************************** + * * + * Type : RhsFn * + *----------------------------------------------------------------* + * The f function which defines the right hand side of the ODE * + * system y' = f(t,y) must have type RhsFn. * + * f takes as input the problem size N, the independent variable * + * value t, and the dependent variable vector y. It stores the * + * result of f(t,y) in the vector ydot. The y and ydot arguments * + * are of type N_Vector. * + * (Allocation of memory for ydot is handled within CVODE.) * + * The f_data parameter is the same as the f_data * + * parameter passed by the user to the CVodeMalloc routine. This * + * user-supplied pointer is passed to the user's f function * + * every time it is called. * + * A RhsFn f does not have a return value. * + * * + ******************************************************************/ + + typedef void (*RhsFn) (integertype N, realtype t, N_Vector y, + N_Vector ydot, void *f_data); + + +/****************************************************************** + * * + * Function : CVodeMalloc * + *----------------------------------------------------------------* + * CVodeMalloc allocates and initializes memory for a problem to * + * to be solved by CVODE. * + * * + * N is the number of equations in the ODE system. * + * * + * f is the right hand side function in y' = f(t,y). * + * * + * t0 is the initial value of t. * + * * + * y0 is the initial condition vector y(t0). * + * * + * lmm is the type of linear multistep method to be used. * + * The legal values are ADAMS and BDF (see previous * + * description). * + * * + * iter is the type of iteration used to solve the nonlinear * + * system that arises during each internal time step. * + * The legal values are FUNCTIONAL and NEWTON. * + * * + * itol is the type of tolerances to be used. * + * The legal values are: * + * SS (scalar relative and absolute tolerances), * + * SV (scalar relative tolerance and vector * + * absolute tolerance). * + * * + * reltol is a pointer to the relative tolerance scalar. * + * * + * abstol is a pointer to the absolute tolerance scalar or * + * an N_Vector of absolute tolerances. * + * * + * The parameters itol, reltol, and abstol define a vector of * + * error weights, ewt, with components * + * ewt[i] = 1/(reltol*abs(y[i]) + abstol) (if itol = SS), or * + * ewt[i] = 1/(reltol*abs(y[i]) + abstol[i]) (if itol = SV). * + * This vector is used in all error and convergence tests, which * + * use a weighted RMS norm on all error-like vectors v: * + * WRMSnorm(v) = sqrt( (1/N) sum(i=1..N) (v[i]*ewt[i])^2 ). * + * * + * f_data is a pointer to user data that will be passed to the * + * user's f function every time f is called. * + * * + * errfp is the file pointer for an error file where all CVODE * + * warning and error messages will be written. This * + * parameter can be stdout (standard output), stderr * + * (standard error), a file pointer (corresponding to * + * a user error file opened for writing) returned by * + * fopen, or NULL. If the user passes NULL, then all * + * messages will be written to standard output. * + * * + * optIn is a flag indicating whether there are any optional * + * inputs from the user in the arrays iopt and ropt. * + * Pass FALSE to indicate no optional inputs and TRUE * + * to indicate that optional inputs are present. * + * * + * iopt is the user-allocated array (of size OPT_SIZE given * + * later) that will hold optional integer inputs and * + * outputs. The user can pass NULL if he/she does not * + * wish to use optional integer inputs or outputs. * + * If optIn is TRUE, the user should preset to 0 those * + * locations for which default values are to be used. * + * * + * ropt is the user-allocated array (of size OPT_SIZE given * + * later) that will hold optional real inputs and * + * outputs. The user can pass NULL if he/she does not * + * wish to use optional real inputs or outputs. * + * If optIn is TRUE, the user should preset to 0.0 the * + * locations for which default values are to be used. * + * * + * machEnv is a pointer to machine environment-specific * + * information. * + * * + * Note: The tolerance values may be changed in between calls to * + * CVode for the same problem. These values refer to * + * (*reltol) and either (*abstol), for a scalar absolute * + * tolerance, or the components of abstol, for a vector * + * absolute tolerance. * + * * + * If successful, CVodeMalloc returns a pointer to initialized * + * problem memory. This pointer should be passed to CVode. If * + * an initialization error occurs, CVodeMalloc prints an error * + * message to the file specified by errfp and returns NULL. * + * * + ******************************************************************/ + + + void *CVodeMalloc(integertype N, RhsFn f, realtype t0, N_Vector y0, + int lmm, int iter, int itol, realtype * reltol, + void *abstol, void *f_data, FILE * errfp, + booleantype optIn, long int iopt[], realtype ropt[], + M_Env machEnv); + + +/****************************************************************** + * * + * Function : CVReInit * + *----------------------------------------------------------------* + * CVReInit re-initializes CVode for the solution of a problem, * + * where a prior call to CVodeMalloc has been made with the same * + * problem size N. CVReInit performs the same input checking * + * and initializations that CVodeMalloc does (except for N). * + * But it does no memory allocation, assuming that the existing * + * internal memory is sufficient for the new problem. * + * * + * The use of CVReInit requires that the maximum method order, * + * maxord, is no larger for the new problem than for the problem * + * specified in the last call to CVodeMalloc. This condition is * + * automatically fulfilled if the multistep method parameter lmm * + * is unchanged (or changed from ADAMS to BDF) and the default * + * value for maxord is specified. * + * * + * If iter = NEWTON, then following the call to CVReInit, a call * + * to the linear solver specification routine is necessary if a * + * different linear solver is chosen, but may not be otherwise. * + * If the same linear solver is chosen, and there are no changes * + * in the input parameters to the specification routine, then no * + * call to that routine is needed. * + * If there are changes in parameters, but they do not increase * + * the linear solver memory size, then a call to the corresponding* + * CVReInit routine must made to communicate the new * + * parameters; in that case the linear solver memory is reused. * + * If the parameter changes do increase the linear solver memory * + * size, then the main linear solver specification routine must be* + * called. See the linear solver documentation for full details. * + * * + * The first argument to CVReInit is: * + * * + * cvode_mem = pointer to CVODE memory returned by CVodeMalloc. * + * * + * All the remaining arguments to CVReInit have names and * + * meanings identical to those of CVodeMalloc. Note that the * + * problem size N is not passed as an argument to CVReInit, * + * as that is assumed to be unchanged since the CVodeMalloc call. * + * * + * The return value of CVReInit is equal to SUCCESS = 0 if there * + * were no errors; otherwise it is a negative int equal to: * + * CVREI_NO_MEM indicating cvode_mem was NULL, or * + * CVREI_ILL_INPUT indicating an input argument was illegal * + * (including an attempt to increase maxord). * + * In case of an error return, an error message is also printed. * + * * + * Note: the reported workspace sizes iopt[LENRW] and iopt[LENIW] * + * are left unchanged from the values computed by CVodeMalloc, and* + * so may be larger than would be computed for the new problem. * + ******************************************************************/ + + int CVReInit(void *cvode_mem, RhsFn f, realtype t0, N_Vector y0, + int lmm, int iter, int itol, realtype * reltol, + void *abstol, void *f_data, FILE * errfp, + booleantype optIn, long int iopt[], + realtype ropt[], M_Env machEnv); + + +/* CVReInit return values: */ + +/* SUCCESS = 0 (Defined under CVode return values, but listed + here also for completeness) */ + enum + { CVREI_NO_MEM = -1, CVREI_ILL_INPUT = -2 }; + + +/****************************************************************** + * * + * Function : CVode * + *----------------------------------------------------------------* + * CVode integrates the ODE over an interval in t. * + * If itask is NORMAL, then the solver integrates from its * + * current internal t value to a point at or beyond tout, then * + * interpolates to t = tout and returns y(tout) in the user- * + * allocated vector yout. If itask is ONE_STEP, then the solver * + * takes one internal time step and returns in yout the value of * + * y at the new internal time. In this case, tout is used only * + * during the first call to CVode to determine the direction of * + * integration and the rough scale of the problem. In either * + * case, the time reached by the solver is placed in (*t). The * + * user is responsible for allocating the memory for this value. * + * * + * cvode_mem is the pointer to CVODE memory returned by * + * CVodeMalloc. * + * * + * tout is the next time at which a computed solution is desired * + * * + * yout is the computed solution vector. In NORMAL mode with no * + * errors, yout=y(tout). * + * * + * t is a pointer to a real location. CVode sets (*t) to the * + * time reached by the solver and returns yout=y(*t). * + * * + * itask is either NORMAL or ONE_STEP mode. These two modes have * + * been described above. * + * * + * The return values for CVode are defined later in this file. * + * Here is a brief description of each return value: * + * * + * SUCCESS : CVode succeeded. * + * * + * CVODE_NO_MEM : The cvode_mem argument was NULL. * + * * + * ILL_INPUT : One of the inputs to CVode is illegal. This * + * includes the situation when a component of the * + * error weight vectors becomes < 0 during * + * internal time-stepping. The ILL_INPUT flag * + * will also be returned if the linear solver * + * routine CV--- (called by the user after * + * calling CVodeMalloc) failed to set one of the * + * linear solver-related fields in cvode_mem or * + * if the linear solver's init routine failed. In * + * any case, the user should see the printed * + * error message for more details. * + * * + * TOO_MUCH_WORK : The solver took mxstep internal steps but * + * could not reach tout. The default value for * + * mxstep is MXSTEP_DEFAULT = 500. * + * * + * TOO_MUCH_ACC : The solver could not satisfy the accuracy * + * demanded by the user for some internal step. * + * * + * ERR_FAILURE : Error test failures occurred too many times * + * (= MXNEF = 7) during one internal time step or * + * occurred with |h| = hmin. * + * * + * CONV_FAILURE : Convergence test failures occurred too many * + * times (= MXNCF = 10) during one internal time * + * step or occurred with |h| = hmin. * + * * + * SETUP_FAILURE : The linear solver's setup routine failed in an * + * unrecoverable manner. * + * * + * SOLVE_FAILURE : The linear solver's solve routine failed in an * + * unrecoverable manner. * + * * + ******************************************************************/ + + + int CVode(void *cvode_mem, realtype tout, N_Vector yout, + realtype * t, int itask); + + +/* CVode return values */ + + enum + { SUCCESS = 0, CVODE_NO_MEM = -1, ILL_INPUT = -2, TOO_MUCH_WORK = -3, + TOO_MUCH_ACC = -4, ERR_FAILURE = -5, CONV_FAILURE = -6, + SETUP_FAILURE = -7, SOLVE_FAILURE = -8 + }; + + +/****************************************************************** + * * + * Function : CVodeDky * + *----------------------------------------------------------------* + * CVodeDky computes the kth derivative of the y function at * + * time t, where tn-hu <= t <= tn, tn denotes the current * + * internal time reached, and hu is the last internal step size * + * successfully used by the solver. The user may request * + * k=0, 1, ..., qu, where qu is the current order. The * + * derivative vector is returned in dky. This vector must be * + * allocated by the caller. It is only legal to call this * + * function after a successful return from CVode. * + * * + * cvode_mem is the pointer to CVODE memory returned by * + * CVodeMalloc. * + * * + * t is the time at which the kth derivative of y is evaluated. * + * The legal range for t is [tn-hu,tn] as described above. * + * * + * k is the order of the derivative of y to be computed. The * + * legal range for k is [0,qu] as described above. * + * * + * dky is the output derivative vector [(D_k)y](t). * + * * + * The return values for CVodeDky are defined later in this file. * + * Here is a brief description of each return value: * + * * + * OKAY : CVodeDky succeeded. * + * * + * BAD_K : k is not in the range 0, 1, ..., qu. * + * * + * BAD_T : t is not in the interval [tn-hu,tn]. * + * * + * BAD_DKY : The dky argument was NULL. * + * * + * DKY_NO_MEM : The cvode_mem argument was NULL. * + * * + ******************************************************************/ + + + int CVodeDky(void *cvode_mem, realtype t, int k, N_Vector dky); + + +/* CVodeDky return values */ + + enum + { OKAY = 0, BAD_K = -1, BAD_T = -2, BAD_DKY = -3, DKY_NO_MEM = -4 }; + + +/****************************************************************** + * * + * Function : CVodeFree * + *----------------------------------------------------------------* + * CVodeFree frees the problem memory cvode_mem allocated by * + * CVodeMalloc. Its only argument is the pointer cvode_mem * + * returned by CVodeMalloc. * + * * + ******************************************************************/ + + void CVodeFree(void *cvode_mem); + + +/****************************************************************** + * * + * Optional Inputs and Outputs * + *----------------------------------------------------------------* + * The user should declare two arrays for optional input and * + * output, an iopt array for optional integer input and output * + * and an ropt array for optional real input and output. The * + * size of both these arrays should be OPT_SIZE. * + * So the user's declaration should look like: * + * * + * long int iopt[OPT_SIZE]; * + * realtype ropt[OPT_SIZE]; * + * * + * The enumerations below the OPT_SIZE definition * + * are indices into the iopt and ropt arrays. Here is a brief * + * description of the contents of these positions: * + * * + * iopt[MAXORD] : maximum lmm order to be used by the solver. * + * Optional input. (Default = 12 for ADAMS, 5 for * + * BDF). * + * * + * iopt[MXSTEP] : maximum number of internal steps to be taken by * + * the solver in its attempt to reach tout. * + * Optional input. (Default = 500). * + * * + * iopt[MXHNIL] : maximum number of warning messages issued by the* + * solver that t + h = t on the next internal step.* + * A value of -1 means no such messages are issued.* + * Optional input. (Default = 10). * + * * + * iopt[NST] : cumulative number of internal steps taken by * + * the solver (total so far). Optional output. * + * * + * iopt[NFE] : number of calls to the user's f function. * + * Optional output. * + * * + * iopt[NSETUPS] : number of calls made to the linear solver's * + * setup routine. Optional output. * + * * + * iopt[NNI] : number of NEWTON iterations performed. * + * Optional output. * + * * + * iopt[NCFN] : number of nonlinear convergence failures * + * that have occurred. Optional output. * + * * + * iopt[NETF] : number of local error test failures that * + * have occurred. Optional output. * + * * + * iopt[QU] : order used during the last internal step. * + * Optional output. * + * * + * iopt[QCUR] : order to be used on the next internal step. * + * Optional output. * + * * + * iopt[LENRW] : size of required CVODE internal real work * + * space, in realtype words. Optional output. * + * * + * iopt[LENIW] : size of required CVODE internal integer work * + * space, in integertype words. Optional output. * + * * + * iopt[SLDET] : Flag to turn on/off stability limit detection * + * (1 = on, 0 = off). When BDF is used and order * + * is 3 or greater, CVsldet is call to detect * + * stability limit. If limit is detected, the * + * order is reduced. Optional input. * + * * + * iopt[NOR] : Number of order reductions due to * + * stability limit detection. * + * Optional output. * + * * + * ropt[H0] : initial step size. Optional input. * + * * + * ropt[HMAX] : maximum absolute value of step size allowed. * + * Optional input. (Default is infinity). * + * Note: If optIn = TRUE, the value of ropt[HMAX] * + * is examined on every call to CVode, and so can * + * be changed between calls. * + * * + * ropt[HMIN] : minimum absolute value of step size allowed. * + * Optional input. (Default is 0.0). * + * * + * ropt[HU] : step size for the last internal step. * + * Optional output. * + * * + * ropt[HCUR] : step size to be attempted on the next internal * + * step. Optional output. * + * * + * ropt[TCUR] : current internal time reached by the solver. * + * Optional output. * + * * + * ropt[TOLSF] : a suggested factor by which the user's * + * tolerances should be scaled when too much * + * accuracy has been requested for some internal * + * step. Optional output. * + * * + ******************************************************************/ + +/* iopt, ropt array sizes */ + +#define OPT_SIZE 40 + + +/* iopt and ropt offsets * + * The constants CVODE_IOPT_SIZE and CVODE_ROPT_SIZE are equal to * + * the number of integer and real optional inputs and outputs * + * actually accessed in cvode.c. The locations beyond these * + * values are used by the linear solvers. */ + +#define CVODE_IOPT_SIZE 15 +#define CVODE_ROPT_SIZE 7 + +/* iopt indices */ + enum + { MAXORD, MXSTEP, MXHNIL, + NST, NFE, NSETUPS, NNI, NCFN, NETF, QU, QCUR, + LENRW, LENIW, SLDET, NOR + }; + +/* ropt indices */ + + enum + { H0, HMAX, HMIN, + HU, HCUR, TCUR, TOLSF + }; + + +/* Basic CVODE constants */ + +#define ADAMS_Q_MAX 12 /* max value of q for lmm == ADAMS */ +#define BDF_Q_MAX 5 /* max value of q for lmm == BDF */ +#define Q_MAX ADAMS_Q_MAX /* max value of q for either lmm */ +#define L_MAX (Q_MAX+1) /* max value of L for either lmm */ +#define NUM_TESTS 5 /* number of error test quantities */ + + +/****************************************************************** + * * + * Types : struct CVodeMemRec, CVodeMem * + *----------------------------------------------------------------* + * The type CVodeMem is type pointer to struct CVodeMemRec. This * + * structure contains fields to keep track of problem state. * + * * + ******************************************************************/ + + typedef struct CVodeMemRec + { + + realtype cv_uround; /* machine unit roundoff */ + + /* Problem Specification Data */ + + integertype cv_N; /* ODE system size */ + RhsFn cv_f; /* y' = f(t,y(t)) */ + void *cv_f_data; /* user pointer passed to f */ + int cv_lmm; /* lmm = ADAMS or BDF */ + int cv_iter; /* iter = FUNCTIONAL or NEWTON */ + int cv_itol; /* itol = SS or SV */ + realtype *cv_reltol; /* ptr to relative tolerance */ + void *cv_abstol; /* ptr to absolute tolerance */ + + /* Nordsieck History Array */ + + N_Vector cv_zn[L_MAX]; /* Nordsieck array, of size N x (q+1). */ + /* zn[j] is a vector of length N (j=0,...,q) */ + /* zn[j] = [1/factorial(j)] * h^j * (jth */ + /* derivative of the interpolating polynomial */ + + /* Vectors of length N */ + + N_Vector cv_ewt; /* error weight vector */ + N_Vector cv_y; /* y is used as temporary storage by the solver */ + /* The memory is provided by the user to CVode */ + /* where the vector is named yout. */ + N_Vector cv_acor; /* In the context of the solution of the */ + /* nonlinear equation, acor = y_n(m) - y_n(0). */ + /* On return, this vector is scaled to give */ + /* the estimated local error in y. */ + N_Vector cv_tempv; /* temporary storage vector */ + N_Vector cv_ftemp; /* temporary storage vector */ + + /* Step Data */ + + int cv_q; /* current order */ + int cv_qprime; /* order to be used on the next step */ + /* = q-1, q, or q+1 */ + int cv_qwait; /* number of internal steps to wait before */ + /* considering a change in q */ + int cv_L; /* L = q + 1 */ + + realtype cv_h; /* current step size */ + realtype cv_hprime; /* step size to be used on the next step */ + realtype cv_eta; /* eta = hprime / h */ + realtype cv_hscale; /* value of h used in zn */ + realtype cv_tn; /* current internal value of t */ + + realtype cv_tau[L_MAX + 1]; /* array of previous q+1 successful step */ + /* sizes indexed from 1 to q+1 */ + realtype cv_tq[NUM_TESTS + 1]; /* array of test quantities indexed from */ + /* 1 to NUM_TESTS(=5) */ + realtype cv_l[L_MAX]; /* coefficients of l(x) (degree q poly) */ + + realtype cv_rl1; /* 1 / l[1] */ + realtype cv_gamma; /* gamma = h * rl1 */ + realtype cv_gammap; /* gamma at the last setup call */ + realtype cv_gamrat; /* gamma / gammap */ + + realtype cv_crate; /* estimated corrector convergence rate */ + realtype cv_acnrm; /* | acor | wrms */ + int cv_mnewt; /* Newton iteration counter */ + + /* Limits */ + + int cv_qmax; /* q <= qmax */ + int cv_mxstep; /* maximum number of internal steps for one user call */ + int cv_maxcor; /* maximum number of corrector iterations for the */ + /* solution of the nonlinear equation */ + int cv_mxhnil; /* maximum number of warning messages issued to the */ + /* user that t + h == t for the next internal step */ + + realtype cv_hmin; /* |h| >= hmin */ + realtype cv_hmax_inv; /* |h| <= 1/hmax_inv */ + realtype cv_etamax; /* eta <= etamax */ + + /* Counters */ + + long int cv_nst; /* number of internal steps taken */ + long int cv_nfe; /* number of f calls */ + long int cv_ncfn; /* number of corrector convergence failures */ + long int cv_netf; /* number of error test failures */ + long int cv_nni; /* number of Newton iterations performed */ + long int cv_nsetups; /* number of setup calls */ + int cv_nhnil; /* number of messages issued to the user that */ + /* t + h == t for the next iternal step */ + long int cv_lrw; /* number of realtype words in CVODE work vectors */ + long int cv_liw; /* no. of integertype words in CVODE work vectors */ + long int cv_nscon; /* counter for STALD method */ + + realtype cv_etaqm1; /* ratio of new to old h for order q-1 */ + realtype cv_etaq; /* ratio of new to old h for order q */ + realtype cv_etaqp1; /* ratio of new to old h for order q+1 */ + realtype cv_ssdat[6][4]; /* scaled data array for STALD */ + + /* Linear Solver Data */ + + /* Linear Solver functions to be called */ + + int (*cv_linit) (struct CVodeMemRec * cv_mem); + + int (*cv_lsetup) (struct CVodeMemRec * cv_mem, int convfail, + N_Vector ypred, N_Vector fpred, + booleantype * jcurPtr, N_Vector vtemp1, + N_Vector vtemp2, N_Vector vtemp3); + + int (*cv_lsolve) (struct CVodeMemRec * cv_mem, N_Vector b, + N_Vector ycur, N_Vector fcur); + + void (*cv_lfree) (struct CVodeMemRec * cv_mem); + + /* Linear Solver specific memory */ + + void *cv_lmem; + + /* Saved Values */ + + int cv_qu; /* last successful q value used */ + long int cv_nstlp; /* step number of last setup call */ + realtype cv_hu; /* last successful h value used */ + realtype cv_saved_tq5; /* saved value of tq[5] */ + booleantype cv_jcur; /* Is the Jacobian info used by */ + /* linear solver current? */ + realtype cv_tolsf; /* tolerance scale factor */ + booleantype cv_setupNonNull; /* Does setup do something? */ + + /* Arrays for Optional Input and Optional Output */ + + booleantype cv_optIn; /* boolean input optIn */ + long int *cv_iopt; /* long int optional input, output */ + realtype *cv_ropt; /* real optional input, output */ + + /* Error File */ + +#if !defined(R_SO) + FILE *cv_errfp; /* CVODE error messages are sent to errfp */ +#endif + + /* Pointer to Machine Environment-Specific Information */ + + M_Env cv_machenv; + + /* Stability Limit Detection control flag */ + + booleantype cv_sldeton; /* Is Stability Limit Detection on */ + + } *CVodeMem; + + +/****************************************************************** + * * + * Communication between user and a CVODE Linear Solver * + *----------------------------------------------------------------* + * Return values of the linear solver specification routine. * + * The values of these are given in the enum statement below. * + * SUCCESS : The routine was successful. * + * * + * LMEM_FAIL : A memory allocation failed. * + * * + * LIN_ILL_INPUT: Some input was illegal (see message). * + * * + ******************************************************************/ + +/* SUCCESS = 0 (Defined under CVode return values, but listed + here also for completeness) */ + enum + { LMEM_FAIL = -1, LIN_ILL_INPUT = -2 }; + + +/****************************************************************** + * * + * Communication between cvode.c and a CVODE Linear Solver * + *----------------------------------------------------------------* + * (1) cv_linit return values * + * * + * LINIT_OK : The cv_linit routine succeeded. * + * * + * LINIT_ERR : The cv_linit routine failed. Each linear solver * + * init routine should print an appropriate error * + * message to (cv_mem->errfp). * + * * + * (2) convfail (input to cv_lsetup) * + * * + * NO_FAILURES : Either this is the first cv_setup call for this * + * step, or the local error test failed on the * + * previous attempt at this step (but the Newton * + * iteration converged). * + * * + * FAIL_BAD_J : This value is passed to cv_lsetup if * + * * + * (1) The previous Newton corrector iteration * + * did not converge and the linear solver's * + * setup routine indicated that its Jacobian- * + * related data is not current. * + * or * + * (2) During the previous Newton corrector * + * iteration, the linear solver's solve routine * + * failed in a recoverable manner and the * + * linear solver's setup routine indicated that * + * its Jacobian-related data is not current. * + * * + * FAIL_OTHER : During the current internal step try, the * + * previous Newton iteration failed to converge * + * even though the linear solver was using current * + * Jacobian-related data. * + * * + * (3) Parameter documentation, as well as a brief description * + * of purpose, for each CVODE linear solver routine to be * + * called in cvode.c is given below the constant declarations * + * that follow. * + * * + ******************************************************************/ + +/* cv_linit return values */ + +#define LINIT_OK 0 +#define LINIT_ERR -1 + +/* Constants for convfail (input to cv_lsetup) */ + +#define NO_FAILURES 0 +#define FAIL_BAD_J 1 +#define FAIL_OTHER 2 + + +/******************************************************************* + * * + * int (*cv_linit)(CVodeMem cv_mem); * + *-----------------------------------------------------------------* + * The purpose of cv_linit is to complete initializations for * + * specific linear solver, such as counters and statistics. * + * An LInitFn should return LINIT_OK (= 0) if it has successfully * + * initialized the CVODE linear solver and LINIT_ERR (= -1) * + * otherwise. These constants are defined above. If an error does * + * occur, an appropriate message should be sent to (cv_mem->errfp).* + * * + *******************************************************************/ + +/******************************************************************* + * * + * int (*cv_lsetup)(CVodeMem cv_mem, int convfail, N_Vector ypred, * + * N_Vector fpred, booleantype *jcurPtr, * + * N_Vector vtemp1, N_Vector vtemp2, N_Vector vtemp3); * + *-----------------------------------------------------------------* + * The job of cv_lsetup is to prepare the linear solver for * + * subsequent calls to cv_lsolve. It may re-compute Jacobian- * + * related data is it deems necessary. Its parameters are as * + * follows: * + * * + * cv_mem - problem memory pointer of type CVodeMem. See the big * + * typedef earlier in this file. * + * * + * convfail - a flag to indicate any problem that occurred during * + * the solution of the nonlinear equation on the * + * current time step for which the linear solver is * + * being used. This flag can be used to help decide * + * whether the Jacobian data kept by a CVODE linear * + * solver needs to be updated or not. * + * Its possible values have been documented above. * + * * + * ypred - the predicted y vector for the current CVODE internal * + * step. * + * * + * fpred - f(tn, ypred). * + * * + * jcurPtr - a pointer to a boolean to be filled in by cv_lsetup. * + * The function should set *jcurPtr=TRUE if its Jacobian * + * data is current after the call and should set * + * *jcurPtr=FALSE if its Jacobian data is not current. * + * Note: If cv_lsetup calls for re-evaluation of * + * Jacobian data (based on convfail and CVODE state * + * data), it should return *jcurPtr=TRUE unconditionally;* + * otherwise an infinite loop can result. * + * * + * vtemp1 - temporary N_Vector provided for use by cv_lsetup. * + * * + * vtemp3 - temporary N_Vector provided for use by cv_lsetup. * + * * + * vtemp3 - temporary N_Vector provided for use by cv_lsetup. * + * * + * The cv_lsetup routine should return 0 if successful, * + * a positive value for a recoverable error, and a negative value * + * for an unrecoverable error. * + * * + *******************************************************************/ + +/******************************************************************* + * * + * int (*cv_lsolve)(CVodeMem cv_mem, N_Vector b, N_Vector ycur, * + * N_Vector fcur); * + *-----------------------------------------------------------------* + * cv_lsolve must solve the linear equation P x = b, where * + * P is some approximation to (I - gamma J), J = (df/dy)(tn,ycur) * + * and the RHS vector b is input. The N-vector ycur contains * + * the solver's current approximation to y(tn) and the vector * + * fcur contains the N-vector f(tn,ycur). The solution is to be * + * returned in the vector b. cv_lsolve returns a positive value * + * for a recoverable error and a negative value for an * + * unrecoverable error. Success is indicated by a 0 return value. * + * * + *******************************************************************/ + +/******************************************************************* + * * + * void (*cv_lfree)(CVodeMem cv_mem); * + *-----------------------------------------------------------------* + * cv_lfree should free up any memory allocated by the linear * + * solver. This routine is called once a problem has been * + * completed and the linear solver is no longer needed. * + * * + *******************************************************************/ + + +#endif +/* +#ifdef __cplusplus +} +#endif +*/ +#endif /* _INC_CVODE_H */ diff --git a/phreeqcpp/cxxKinetics.cxx b/phreeqcpp/cxxKinetics.cxx new file mode 100644 index 00000000..17ad6626 --- /dev/null +++ b/phreeqcpp/cxxKinetics.cxx @@ -0,0 +1,644 @@ +// cxxKinetics.cxx: implementation of the cxxKinetics class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Utils.h" +#include "Phreeqc.h" +#include "cxxKinetics.h" +#include "cxxMix.h" +#include "phqalloc.h" +#include "PHRQ_io.h" +#include "Dictionary.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxKinetics::cxxKinetics(PHRQ_io *io) + // + // default constructor for cxxKinetics + // +: cxxNumKeyword(io) +{ + step_divide = 1.0; + rk = 3; + bad_step_max = 500; + use_cvode = false; + cvode_steps = 100; + cvode_order = 5; + totals.type = cxxNameDouble::ND_ELT_MOLES; + equalIncrements = false; + count = 0; +} +cxxKinetics::cxxKinetics(const std::map < int, cxxKinetics > &entities, + cxxMix & mix, int l_n_user, PHRQ_io *io): +cxxNumKeyword(io) +{ + this->n_user = this->n_user_end = l_n_user; + step_divide = 1.0; + rk = 3; + bad_step_max = 500; + use_cvode = false; + cvode_steps = 100; + cvode_order = 5; + totals.type = cxxNameDouble::ND_ELT_MOLES; + equalIncrements = false; + count = 0; +// +// Mix +// + const std::map < int, LDBLE >&mixcomps = mix.Get_mixComps(); + std::map < int, LDBLE >::const_iterator it; + for (it = mixcomps.begin(); it != mixcomps.end(); it++) + { + if (entities.find(it->first) != entities.end()) + { + const cxxKinetics *entity_ptr = + &(entities.find(it->first)->second); + this->add(*entity_ptr, it->second); + } + } +} + +cxxKinetics::~cxxKinetics() +{ +} + +#ifdef SKIP +void +cxxKinetics::dump_xml(std::ostream & s_oss, unsigned int indent) const const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Kinetics element and attributes + s_oss << indent0; + s_oss << " + pitzer_kinetics_gammas << "\"" << "\n"; + + // components + s_oss << indent1; + s_oss << "::const_iterator it = + kineticsComps.begin(); it != kineticsComps.end(); ++it) + { + it->dump_xml(s_oss, indent + 2); + } + + return; +} +#endif + +void +cxxKinetics::dump_raw(std::ostream & s_oss, unsigned int indent, int * n_out) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Kinetics element and attributes + s_oss << indent0; + int n_user_local = (n_out != NULL) ? *n_out : this->n_user; + s_oss << "KINETICS_RAW " << n_user_local << " " << this->description << "\n"; + + s_oss << indent1 << "# KINETICS_MODIFY candidate identifiers #\n"; + + s_oss << indent1; + s_oss << "-step_divide " << this->step_divide << "\n"; + + s_oss << indent1; + s_oss << "-rk " << this->rk << "\n"; + + s_oss << indent1; + s_oss << "-bad_step_max " << this->bad_step_max << "\n"; + + s_oss << indent1; + s_oss << "-use_cvode " << this->use_cvode << "\n"; + + s_oss << indent1; + s_oss << "-cvode_steps " << this->cvode_steps << "\n"; + + s_oss << indent1; + s_oss << "-cvode_order " << this->cvode_order << "\n"; + + // kineticsComps structures + for (size_t k = 0; k < this->kinetics_comps.size(); k++) + { + s_oss << indent1; + s_oss << "-component " << this->kinetics_comps[k].Get_rate_name() << "\n"; + this->kinetics_comps[k].dump_raw(s_oss, indent + 2); + } + + // equal_steps + s_oss << indent1; + s_oss << "-equal_increments " << this->equalIncrements << "\n"; + + // equal_steps + s_oss << indent1; + s_oss << "-count " << this->count << "\n"; + + // steps + s_oss << indent1; + s_oss << "-steps " << "\n"; + { + int i = 0; + s_oss << indent2; + for (std::vector < LDBLE >::const_iterator it = this->steps.begin(); + it != this->steps.end(); it++) + { + if (i++ == 5) + { + s_oss << "\n"; + s_oss << indent2; + i = 0; + } + s_oss << *it << " "; + } + s_oss << "\n"; + } + + s_oss << indent1 << "# KINETICS workspace variables #\n"; + // totals + s_oss << indent1; + s_oss << "-totals " << "\n"; + this->totals.dump_raw(s_oss, indent + 2); + return; +} +void +cxxKinetics::read_raw(CParser & parser, bool check) +{ + + LDBLE d; + + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + bool useLastLine(false); + std::vector < LDBLE > temp_steps; + + // Read kinetics number and description + this->read_number_description(parser); + + opt_save = CParser::OPT_ERROR; + bool step_divide_defined(false); + bool rk_defined(false); + bool bad_step_max_defined(false); + bool use_cvode_defined(false); + bool cvode_steps_defined(false); + bool cvode_order_defined(false); + bool steps_defined(false); + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + opt = parser.getOptionFromLastLine(vopts, next_char, true); + } + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + useLastLine = false; + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input in KINETICS_COMP_RAW keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + break; + + case 0: // step_divide + if (!(parser.get_iss() >> this->step_divide)) + { + this->step_divide = 1.0; + parser.incr_input_error(); + parser.error_msg("Expected numeric value for step_divide.", + PHRQ_io::OT_CONTINUE); + } + step_divide_defined = true; + break; + + case 1: // rk + if (!(parser.get_iss() >> this->rk)) + { + this->rk = 3; + parser.incr_input_error(); + parser.error_msg("Expected integer value for rk.", + PHRQ_io::OT_CONTINUE); + } + rk_defined = true; + break; + + case 2: // bad_step_max + if (!(parser.get_iss() >> this->bad_step_max)) + { + this->bad_step_max = 500; + parser.incr_input_error(); + parser.error_msg("Expected integer value for bad_step_max.", + PHRQ_io::OT_CONTINUE); + } + bad_step_max_defined = true; + break; + + case 3: // use_cvode + if (!(parser.get_iss() >> this->use_cvode)) + { + this->use_cvode = false; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for use_cvode.", + PHRQ_io::OT_CONTINUE); + } + use_cvode_defined = true; + break; + + case 4: // component + { + std::string str; + if (!(parser.get_iss() >> str)) + { + parser.incr_input_error(); + parser.error_msg("Expected string value for component name.", + PHRQ_io::OT_CONTINUE); + } + else + { + cxxKineticsComp temp_comp(this->io); + temp_comp.Set_rate_name(str.c_str()); + cxxKineticsComp *comp_ptr = this->Find(str); + if (comp_ptr) + { + temp_comp = *comp_ptr; + } + temp_comp.read_raw(parser, false); + if (comp_ptr) + { + for (size_t j = 0; j < this->kinetics_comps.size(); j++) + { + if (Utilities::strcmp_nocase(this->kinetics_comps[j].Get_rate_name().c_str(), str.c_str()) == 0) + { + this->kinetics_comps[j] = temp_comp; + } + } + } + else + { + this->kinetics_comps.push_back(temp_comp); + } + useLastLine = true; + } + } + break; + + case 5: // totals + if (this->totals.read_raw(parser, next_char) != + CParser::PARSER_OK) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected element name and molality for KineticsComp totals.", + PHRQ_io::OT_CONTINUE); + } + opt_save = 5; + break; + + case 6: // steps + while (parser.copy_token(token, next_char) == CParser::TT_DIGIT) + { + std::istringstream iss(token); + if (!(iss >> d)) + { + parser.incr_input_error(); + parser.error_msg("Expected numeric value for steps.", + PHRQ_io::OT_CONTINUE); + } + else + { + temp_steps.push_back(d); + steps_defined = true; + } + } + opt_save = 6; + break; + + case 7: // cvode_steps + if (!(parser.get_iss() >> this->cvode_steps)) + { + this->cvode_steps = 100; + parser.incr_input_error(); + parser.error_msg("Expected integer value for cvode_steps.", + PHRQ_io::OT_CONTINUE); + } + cvode_steps_defined = true; + break; + + case 8: // cvode_order + if (!(parser.get_iss() >> this->cvode_order)) + { + this->cvode_order = 5; + parser.incr_input_error(); + parser.error_msg("Expected integer value for cvode_order.", + PHRQ_io::OT_CONTINUE); + } + cvode_order_defined = true; + + break; + case 9: // equalIncrements + case 11: // equal_increments + if (!(parser.get_iss() >> this->equalIncrements)) + { + this->use_cvode = false; + parser.incr_input_error(); + parser.error_msg("Expected boolean value for equalIncrements.", + PHRQ_io::OT_CONTINUE); + } + break; + case 10: // count + if (!(parser.get_iss() >> this->count)) + { + this->count = 0; + parser.incr_input_error(); + parser.error_msg("Expected integer value for count.", + PHRQ_io::OT_CONTINUE); + } + break; + + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (steps_defined) + { + this->steps = temp_steps; + } + if (check) + { + // members that must be defined + if (step_divide_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Step_divide not defined for KINETICS_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (rk_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Rk not defined for KINETICS_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (bad_step_max_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Bad_step_max not defined for KINETICS_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (use_cvode_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Use_cvode not defined for KINETICS_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (cvode_steps_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Cvode_steps not defined for KINETICS_RAW input.", + PHRQ_io::OT_CONTINUE); + } + if (cvode_order_defined == false) + { + parser.incr_input_error(); + parser.error_msg("Cvode_order not defined for KINETICS_RAW input.", + PHRQ_io::OT_CONTINUE); + } + } +} +void +cxxKinetics::add(const cxxKinetics & addee, LDBLE extensive) + // + // Add to existing ppassemblage to "this" ppassemblage + // +{ + if (extensive == 0.0) + return; + for (size_t i_add = 0; i_add < addee.kinetics_comps.size(); i_add++) + { + bool found(false); + size_t i; + for (i = 0; i < this->kinetics_comps.size(); i++) + { + if (this->kinetics_comps[i].Get_rate_name() == addee.kinetics_comps[i_add].Get_rate_name()) + { + found = true; + break; + } + } + if (found) + { + this->kinetics_comps[i].add(addee.kinetics_comps[i_add], extensive); + } + else + { + cxxKineticsComp entity = addee.kinetics_comps[i_add]; + entity.multiply(extensive); + this->kinetics_comps.push_back(entity); + } + } + this->steps = addee.steps; + this->step_divide = addee.step_divide; + this->rk = addee.rk; + this->bad_step_max = addee.bad_step_max; + this->use_cvode = addee.use_cvode; + this->cvode_steps = addee.cvode_steps; + this->cvode_order = addee.cvode_order; + this->equalIncrements = addee.equalIncrements; + this->count = addee.count; +} +cxxKineticsComp * cxxKinetics:: +Find(const std::string &s) +{ + for (size_t i = 0; i < this->kinetics_comps.size(); i++) + { + if (Utilities::strcmp_nocase(this->kinetics_comps[i].Get_rate_name().c_str(), s.c_str()) == 0) + { + return & (this->kinetics_comps[i]); + } + } + return NULL; +} +int cxxKinetics:: +Get_reaction_steps(void) const +{ + if(equalIncrements) + { + return count; + } + return ((int) steps.size()); +} +LDBLE cxxKinetics:: +Current_step(bool incremental_reactions, int reaction_step) const +{ + if (this->steps.size() == 0) + return 1; + LDBLE kin_time = 1; + if (!incremental_reactions) + { + if (!this->equalIncrements) + { + if (reaction_step > (int) this->steps.size()) + { + kin_time = this->steps[this->steps.size() - 1]; + } + else + { + kin_time = this->steps[reaction_step - 1]; + } + } + else + { + if (reaction_step > this->count) + { + kin_time = this->steps[0]; + } + else + { + kin_time = (LDBLE) reaction_step * this->steps[0] / ((LDBLE) (this->count)); + } + } + } + else + { + /* incremental reactions */ + if (!this->equalIncrements) + { + if (reaction_step > (int) this->steps.size()) + { + kin_time = this->steps[this->steps.size() - 1]; + } + else + { + kin_time = this->steps[reaction_step - 1]; + } + } + else + { + if (reaction_step > this->count) + { + kin_time = 0; + } + else + { + kin_time = this->steps[0] / ((LDBLE) (this->count)); + } + } + } + return kin_time; +} +void +cxxKinetics::Serialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles) +{ + ints.push_back(this->n_user); + ints.push_back((int) this->kinetics_comps.size()); + for (size_t i = 0; i < this->kinetics_comps.size(); i++) + { + this->kinetics_comps[i].Serialize(dictionary, ints, doubles); + } + ints.push_back((int) this->steps.size()); + for (size_t i = 0; i < this->steps.size(); i++) + { + doubles.push_back(this->steps[i]); + } + ints.push_back(this->count); + ints.push_back(this->equalIncrements ? 1 : 0); + doubles.push_back(this->step_divide); + ints.push_back(this->rk); + ints.push_back(this->bad_step_max); + ints.push_back(this->use_cvode ? 1 : 0); + ints.push_back(this->cvode_steps); + ints.push_back(this->cvode_order); + this->totals.Serialize(dictionary, ints, doubles); +} + +void +cxxKinetics::Deserialize(Dictionary & dictionary, std::vector < int >&ints, + std::vector < double >&doubles, int &ii, int &dd) +{ + this->n_user = ints[ii++]; + this->n_user_end = this->n_user; + this->description = " "; + + int n = ints[ii++]; + this->kinetics_comps.clear(); + for (int i = 0; i < n; i++) + { + cxxKineticsComp kc; + kc.Deserialize(dictionary, ints, doubles, ii, dd); + this->kinetics_comps.push_back(kc); + } + n = ints[ii++]; + this->steps.clear(); + for (int i = 0; i < n; i++) + { + this->steps.push_back(doubles[dd++]); + } + this->count = ints[ii++]; + this->equalIncrements = (ints[ii++] != 0); + this->step_divide = doubles[dd++]; + this->rk = ints[ii++]; + this->bad_step_max = ints[ii++]; + this->use_cvode = (ints[ii++] != 0); + this->cvode_steps = ints[ii++]; + this->cvode_order = ints[ii++]; + this->totals.Deserialize(dictionary, ints, doubles, ii, dd); +} + + +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("step_divide"), // 0 + std::vector< std::string >::value_type("rk"), // 1 + std::vector< std::string >::value_type("bad_step_max"), // 2 + std::vector< std::string >::value_type("use_cvode"), // 3 + std::vector< std::string >::value_type("component"), // 4 + std::vector< std::string >::value_type("totals"), // 5 + std::vector< std::string >::value_type("steps"), // 6 + std::vector< std::string >::value_type("cvode_steps"), // 7 + std::vector< std::string >::value_type("cvode_order"), // 8 + std::vector< std::string >::value_type("equalincrements"), // 9 + std::vector< std::string >::value_type("count"), // 10 + std::vector< std::string >::value_type("equal_increments") // 11 +}; +const std::vector< std::string > cxxKinetics::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/cxxKinetics.h b/phreeqcpp/cxxKinetics.h new file mode 100644 index 00000000..eff885ae --- /dev/null +++ b/phreeqcpp/cxxKinetics.h @@ -0,0 +1,78 @@ +#if !defined(CXXKINETICS_H_INCLUDED) +#define CXXKINETICS_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector +#include "phrqtype.h" +#include "NumKeyword.h" +#include "KineticsComp.h" +#include "PHRQ_base.h" +class cxxMix; + +class cxxKinetics:public cxxNumKeyword +{ + + public: + cxxKinetics(PHRQ_io *io=NULL); + cxxKinetics(const std::map < int, cxxKinetics > &entity_map, cxxMix & mx, + int n_user, PHRQ_io *io=NULL); + ~cxxKinetics(); + + //void dump_xml(std::ostream& os, unsigned int indent = 0)const; + + void dump_raw(std::ostream & s_oss, unsigned int indent, int * n_out=NULL) const; + + void read_raw(CParser & parser, bool check = true); + + std::vector < LDBLE > &Get_steps(void) {return steps;} + const std::vector < LDBLE > &Get_steps(void)const {return steps;} + LDBLE Get_step_divide(void) const {return step_divide;} + void Set_step_divide(LDBLE t) {step_divide = t;} + int Get_rk(void) const {return rk;}; + void Set_rk(int t) {rk = t;} + int Get_bad_step_max(void) const {return bad_step_max;} + void Set_bad_step_max(int t) {bad_step_max = t;} + bool Get_use_cvode(void) const {return use_cvode;} + void Set_use_cvode(bool tf) {use_cvode = tf;} + int Get_cvode_steps(void) const {return cvode_steps;} + void Set_cvode_steps(int t) {cvode_steps = t;} + int Get_cvode_order(void) const {return cvode_order;} + void Set_cvode_order(int t) {cvode_order = t;} + std::vector < cxxKineticsComp > &Get_kinetics_comps(void) {return kinetics_comps;} + const std::vector < cxxKineticsComp > &Get_kinetics_comps(void)const {return kinetics_comps;} + cxxNameDouble & Get_totals(void) {return this->totals;} + void Set_totals(const cxxNameDouble &nd) {totals = nd;} + bool Get_equalIncrements(void) const {return equalIncrements;} + void Set_equalIncrements(bool tf) {equalIncrements = tf;} + int Get_count(void) const {return count;} + void Set_count(int i) {count = i;} + int Get_reaction_steps(void) const; + cxxKineticsComp * Find(const std::string &str); + LDBLE Current_step(const bool incremental_reactions, const int reaction_step) const; + void Serialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles); + void Deserialize(Dictionary & dictionary, std::vector < int >&ints, std::vector < double >&doubles, int &ii, int &dd); + + protected: + void add(const cxxKinetics & addee, LDBLE extensive); + + protected: + // KINETICS_MODIFY candidates + std::vector < cxxKineticsComp > kinetics_comps; + std::vector < LDBLE >steps; + int count; + bool equalIncrements; + LDBLE step_divide; + int rk; + int bad_step_max; + bool use_cvode; + int cvode_steps; + int cvode_order; + // internal variables + cxxNameDouble totals; + const static std::vector < std::string > vopts; +}; + +#endif // !defined(CXXKINETICS_H_INCLUDED) diff --git a/phreeqcpp/cxxMix.cxx b/phreeqcpp/cxxMix.cxx new file mode 100644 index 00000000..a9161c36 --- /dev/null +++ b/phreeqcpp/cxxMix.cxx @@ -0,0 +1,181 @@ +// Mix.cxx: implementation of the cxxMix class. +// +////////////////////////////////////////////////////////////////////// +#ifdef _DEBUG +#pragma warning(disable : 4786) // disable truncation warning (Only used by debugger) +#endif +#include // assert +#include // std::sort + +#include "Utils.h" // define first +#include "Parser.h" +#include "Phreeqc.h" +#include "cxxMix.h" +#include "phqalloc.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +cxxMix::cxxMix(PHRQ_io *io) + // + // default constructor for cxxMix + // +: cxxNumKeyword(io) +{ +} + +cxxMix::~cxxMix() +{ +} + +#ifdef SKIP +void +cxxMix::dump_xml(std::ostream & s_oss, unsigned int indent) const const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Mix element and attributes + s_oss << indent0; + s_oss << " + pitzer_mix_gammas << "\"" << "\n"; + + // components + s_oss << indent1; + s_oss << "::const_iterator it = mixComps.begin(); + it != mixComps.end(); ++it) + { + it->dump_xml(s_oss, indent + 2); + } + + return; +} +#endif + +void +cxxMix::dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out) const +{ + unsigned int i; + s_oss.precision(DBL_DIG - 1); + std::string indent0(""), indent1(""), indent2(""); + for (i = 0; i < indent; ++i) + indent0.append(Utilities::INDENT); + for (i = 0; i < indent + 1; ++i) + indent1.append(Utilities::INDENT); + for (i = 0; i < indent + 2; ++i) + indent2.append(Utilities::INDENT); + + // Mix element and attributes + s_oss << indent0; + int n_user_local = (n_out != NULL) ? *n_out : this->n_user; + s_oss << "MIX_RAW " << n_user_local << " " << this->description << "\n"; + + for (std::map < int, LDBLE >::const_iterator it = this->mixComps.begin(); + it != this->mixComps.end(); it++) + { + s_oss << indent1 << it->first << " " << it->second << "\n"; + } +} + +void +cxxMix::read_raw(CParser & parser) +{ + + int i; + LDBLE d; + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + bool useLastLine(false); + + // Read mix number and description + this->read_number_description(parser); + + opt_save = CParser::OPT_DEFAULT; + + for (;;) + { + int opt; + if (useLastLine == false) + { + opt = parser.get_option(vopts, next_char); + } + else + { + opt = parser.getOptionFromLastLine(vopts, next_char, true); + } + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input in MIX_COMP_RAW keyword.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + useLastLine = false; + break; + + case CParser::OPT_DEFAULT: // solution number, mix fraction + if (parser.copy_token(token, next_char) != CParser::TT_EMPTY) + { + std::istringstream iss(token); + if (!(iss >> i)) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected integer value for solution number.", + PHRQ_io::OT_CONTINUE); + break; + } + if (!(parser.get_iss() >> d)) + { + parser.incr_input_error(); + parser. + error_msg + ("Expected numeric value for solution fraction.", + PHRQ_io::OT_CONTINUE); + break; + } + this->mixComps[i] = d; + } + opt_save = CParser::OPT_DEFAULT; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } +} +void cxxMix::Vectorize(std::vector &n, std::vector &f) +{ + n.clear(); + f.clear(); + for (std::map < int, LDBLE >::const_iterator it = this->mixComps.begin(); + it != this->mixComps.end(); it++) + { + n.push_back(it->first); + f.push_back(it->second); + } +} +const std::vector< std::string > cxxMix::vopts; \ No newline at end of file diff --git a/phreeqcpp/cxxMix.h b/phreeqcpp/cxxMix.h new file mode 100644 index 00000000..6d27a2fd --- /dev/null +++ b/phreeqcpp/cxxMix.h @@ -0,0 +1,58 @@ +#if !defined(CXXMIX_H_INCLUDED) +#define CXXMIX_H_INCLUDED + +#include // assert +#include // std::map +#include // std::string +#include // std::list +#include // std::vector +#include "NumKeyword.h" +#include "PHRQ_base.h" +#include "phrqtype.h" + +class cxxMix:public cxxNumKeyword +{ + + public: + cxxMix(PHRQ_io *io=NULL); + ~cxxMix(); + + //void dump_xml(std::ostream& os, unsigned int indent = 0)const; + + void dump_raw(std::ostream & s_oss, unsigned int indent, int *n_out=NULL) const; + + void read_raw(CParser & parser); + + void Add(int n, LDBLE f) + { + if (this->mixComps.find(n) != this->mixComps.end()) + { + mixComps[n] += f; + } + else + { + mixComps[n] = f; + } + } + void Multiply(LDBLE f) + { + for (std::map < int, LDBLE >::iterator it = this->mixComps.begin(); + it != this->mixComps.end(); it++) + { + it->second *= f; + } + } + + const std::map < int, LDBLE > & Get_mixComps() const + { + return mixComps; + } + void Vectorize(std::vector &n, std::vector &f); + protected: + friend class cxxStorageBin; + std::map < int, LDBLE >mixComps; + const static std::vector < std::string > vopts; + +}; + +#endif // !defined(CXXMIX_H_INCLUDED) diff --git a/phreeqcpp/dense.cpp b/phreeqcpp/dense.cpp new file mode 100644 index 00000000..059fa6cc --- /dev/null +++ b/phreeqcpp/dense.cpp @@ -0,0 +1,168 @@ +/************************************************************************** + * * + * File : dense.c * + * Programmers : Scott D. Cohen, Alan C. Hindmarsh, and * + * Radu Serban @ LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the implementation file for a generic DENSE linear * + * solver package. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ + +#include +#include +#include "sundialstypes.h" +#include "sundialsmath.h" +#include "dense.h" +#include "smalldense.h" + +/* WARNING don`t include any headers below here */ + +#define ZERO RCONST(0.0) +#define ONE RCONST(1.0) + + +/* Implementation */ + + +DenseMat +DenseAllocMat(integertype N) +{ + DenseMat A; + + if (N <= 0) + return (NULL); + + A = (DenseMat) malloc(sizeof *A); + if (A == NULL) + return (NULL); + + A->data = denalloc(N); + if (A->data == NULL) + { + free(A); + return (NULL); + } + + A->size = N; + + return (A); +} + + +integertype * +DenseAllocPiv(integertype N) +{ + if (N <= 0) + return (NULL); + + return ((integertype *) malloc(N * sizeof(integertype))); +} + + +integertype +DenseFactor(DenseMat A, integertype * p) +{ + return (gefa(A->data, A->size, p)); +} + + +void +DenseBacksolve(DenseMat A, integertype * p, realtype * b) +{ + gesl(A->data, A->size, p, b); +} + + +void +DenseZero(DenseMat A) +{ + denzero(A->data, A->size); +} + +void +DenseCopy(DenseMat A, DenseMat B) +{ + dencopy(A->data, B->data, A->size); +} + +void +DenseScale(realtype c, DenseMat A) +{ + denscale(c, A->data, A->size); +} + +void +DenseAddI(DenseMat A) +{ + denaddI(A->data, A->size); +} + +void +DenseFreeMat(DenseMat A) +{ + denfree(A->data); + free(A); +} + +void +DenseFreePiv(integertype * p) +{ + free(p); +} + +void +DensePrint(DenseMat A) +{ + denprint(A->data, A->size); +} diff --git a/phreeqcpp/dense.h b/phreeqcpp/dense.h new file mode 100644 index 00000000..2e23263f --- /dev/null +++ b/phreeqcpp/dense.h @@ -0,0 +1,341 @@ +#ifndef _INC_DENSE_H +#define _INC_DENSE_H +/************************************************************************** + * * + * File : dense.h * + * Programmers : Scott D. Cohen, Alan C. Hindmarsh, and * + * Radu Serban @ LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the header file for a generic DENSE linear solver * + * package. The routines listed in this file all use type * + * DenseMat, defined below, for matrices. These routines in turn * + * call routines in the smalldense.h/smalldense.c module, which * + * use the type realtype** for matrices. This separation allows * + * for possible modifications in which matrices of type DenseMat * + * may not be stored contiguously, while small matrices can still * + * be treated with the routines in smalldense. * + * * + * Routines that work with the type DenseMat begin with "Dense". * + * The DenseAllocMat function allocates a dense matrix for use in * + * the other DenseMat routines listed in this file. Matrix * + * storage details are given in the documentation for the type * + * DenseMat. The DenseAllocPiv function allocates memory for * + * pivot information. The storage allocated by DenseAllocMat and * + * DenseAllocPiv is deallocated by the routines DenseFreeMat and * + * DenseFreePiv, respectively. The DenseFactor and DenseBacksolve * + * routines perform the actual solution of a dense linear system. * + * * + * Routines that work with realtype** begin with "den" (except for * + * the factor and solve routines which are called gefa and gesl, * + * respectively). The underlying matrix storage is described in * + * the documentation for denalloc in smalldense.h * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ +#ifndef _dense_h +#define _dense_h + + +#include "sundialstypes.h" +#include "smalldense.h" + + +/****************************************************************** + * * + * Type: DenseMat * + *----------------------------------------------------------------* + * The type DenseMat is defined to be a pointer to a structure * + * with a size and a data field. The size field indicates the * + * number of columns (== number of rows) of a dense matrix, while * + * the data field is a two dimensional array used for component * + * storage. The elements of a dense matrix are stored columnwise * + * (i.e columns are stored one on top of the other in memory). If * + * A is of type DenseMat, then the (i,j)th element of A (with * + * 0 <= i,j <= size-1) is given by the expression (A->data)[j][i] * + * or by the expression (A->data)[0][j*n+i]. The macros below * + * allow a user to access efficiently individual matrix * + * elements without writing out explicit data structure * + * references and without knowing too much about the underlying * + * element storage. The only storage assumption needed is that * + * elements are stored columnwise and that a pointer to the jth * + * column of elements can be obtained via the DENSE_COL macro. * + * Users should use these macros whenever possible. * + * * + ******************************************************************/ + + typedef struct _DenseMat + { + integertype size; + realtype **data; + } *DenseMat; + + +/* DenseMat accessor macros */ + + +/****************************************************************** + * * + * Macro : DENSE_ELEM * + * Usage : DENSE_ELEM(A,i,j) = a_ij; OR * + * a_ij = DENSE_ELEM(A,i,j); * + *----------------------------------------------------------------* + * DENSE_ELEM(A,i,j) references the (i,j)th element of the N by N * + * DenseMat A, 0 <= i,j <= N-1. * + * * + ******************************************************************/ + +#define DENSE_ELEM(A,i,j) ((A->data)[j][i]) + + +/****************************************************************** + * * + * Macro : DENSE_COL * + * Usage : col_j = DENSE_COL(A,j); * + *----------------------------------------------------------------* + * DENSE_COL(A,j) references the jth column of the N by N * + * DenseMat A, 0 <= j <= N-1. The type of the expression * + * DENSE_COL(A,j) is realtype *. After the assignment in the usage* + * above, col_j may be treated as an array indexed from 0 to N-1. * + * The (i,j)th element of A is referenced by col_j[i]. * + * * + ******************************************************************/ + +#define DENSE_COL(A,j) ((A->data)[j]) + + +/* Functions that use the DenseMat representation for a dense matrix */ + + +/****************************************************************** + * * + * Function : DenseAllocMat * + * Usage : A = DenseAllocMat(N); * + * if (A == NULL) ... memory request failed * + *----------------------------------------------------------------* + * DenseAllocMat allocates memory for an N by N dense matrix and * + * returns the storage allocated (type DenseMat). DenseAllocMat * + * returns NULL if the request for matrix storage cannot be * + * satisfied. See the above documentation for the type DenseMat * + * for matrix storage details. * + * * + ******************************************************************/ + + DenseMat DenseAllocMat(integertype N); + + +/****************************************************************** + * * + * Function : DenseAllocPiv * + * Usage : p = DenseAllocPiv(N); * + * if (p == NULL) ... memory request failed * + *----------------------------------------------------------------* + * DenseAllocPiv allocates memory for pivot information to be * + * filled in by the DenseFactor routine during the factorization * + * of an N by N dense matrix. The underlying type for pivot * + * information is an array of N integers and this routine returns * + * the pointer to the memory it allocates. If the request for * + * pivot storage cannot be satisfied, DenseAllocPiv returns NULL. * + * * + ******************************************************************/ + + integertype *DenseAllocPiv(integertype N); + + +/****************************************************************** + * * + * Function : DenseFactor * + * Usage : ier = DenseFactor(A, p); * + * if (ier != 0) ... A is singular * + *----------------------------------------------------------------* + * DenseFactor performs the LU factorization of the N by N dense * + * matrix A. This is done using standard Gaussian elimination * + * with partial pivoting. * + * * + * A successful LU factorization leaves the matrix A and the * + * pivot array p with the following information: * + * * + * (1) p[k] contains the row number of the pivot element chosen * + * at the beginning of elimination step k, k=0, 1, ..., N-1. * + * * + * (2) If the unique LU factorization of A is given by PA = LU, * + * where P is a permutation matrix, L is a lower triangular * + * matrix with all 1's on the diagonal, and U is an upper * + * triangular matrix, then the upper triangular part of A * + * (including its diagonal) contains U and the strictly lower * + * triangular part of A contains the multipliers, I-L. * + * * + * DenseFactor returns 0 if successful. Otherwise it encountered * + * a zero diagonal element during the factorization. In this case * + * it returns the column index (numbered from one) at which * + * it encountered the zero. * + * * + ******************************************************************/ + + integertype DenseFactor(DenseMat A, integertype * p); + + +/****************************************************************** + * * + * Function : DenseBacksolve * + * Usage : DenseBacksolve(A, p, b); * + *----------------------------------------------------------------* + * DenseBacksolve solves the N-dimensional system A x = b using * + * the LU factorization in A and the pivot information in p * + * computed in DenseFactor. The solution x is returned in b. This * + * routine cannot fail if the corresponding call to DenseFactor * + * did not fail. * + * * + ******************************************************************/ + + void DenseBacksolve(DenseMat A, integertype * p, realtype * b); + + +/****************************************************************** + * * + * Function : DenseZero * + * Usage : DenseZero(A); * + *----------------------------------------------------------------* + * DenseZero sets all the elements of the N by N matrix A to 0.0. * + * * + ******************************************************************/ + + void DenseZero(DenseMat A); + + +/****************************************************************** + * * + * Function : DenseCopy * + * Usage : DenseCopy(A, B); * + *----------------------------------------------------------------* + * DenseCopy copies the contents of the N by N matrix A into the * + * N by N matrix B. * + * * + ******************************************************************/ + + void DenseCopy(DenseMat A, DenseMat B); + + +/****************************************************************** + * * + * Function: DenseScale * + * Usage : DenseScale(c, A); * + *----------------------------------------------------------------* + * DenseScale scales the elements of the N by N matrix A by the * + * constant c and stores the result back in A. * + * * + ******************************************************************/ + + void DenseScale(realtype c, DenseMat A); + + +/****************************************************************** + * * + * Function : DenseAddI * + * Usage : DenseAddI(A); * + *----------------------------------------------------------------* + * DenseAddI adds the identity matrix to A and stores the result * + * back in A. * + * * + ******************************************************************/ + + void DenseAddI(DenseMat A); + + +/****************************************************************** + * * + * Function : DenseFreeMat * + * Usage : DenseFreeMat(A); * + *----------------------------------------------------------------* + * DenseFreeMat frees the memory allocated by DenseAllocMat for * + * the N by N matrix A. * + * * + ******************************************************************/ + + void DenseFreeMat(DenseMat A); + + +/****************************************************************** + * * + * Function : DenseFreePiv * + * Usage : DenseFreePiv(p); * + *----------------------------------------------------------------* + * DenseFreePiv frees the memory allocated by DenseAllocPiv for * + * the pivot information array p. * + * * + ******************************************************************/ + + void DenseFreePiv(integertype * p); + + +/****************************************************************** + * * + * Function : DensePrint * + * Usage : DensePrint(A); * + *----------------------------------------------------------------* + * This routine prints the N by N dense matrix A to standard * + * output as it would normally appear on paper. It is intended * + * as a debugging tool with small values of N. The elements are * + * printed using the %g option. A blank line is printed before * + * and after the matrix. * + * * + ******************************************************************/ + + void DensePrint(DenseMat A); + + +#endif +/* +#ifdef __cplusplus +} +#endif +*/ +#endif /* _INC_DENSE_H */ diff --git a/phreeqcpp/dumper.cpp b/phreeqcpp/dumper.cpp new file mode 100644 index 00000000..bc89a50e --- /dev/null +++ b/phreeqcpp/dumper.cpp @@ -0,0 +1,269 @@ +#include // std::replace + +#include "dumper.h" +#include "Parser.h" +#include "PHRQ_io.h" + +dumper::dumper(PHRQ_io *io) +: +PHRQ_base(io) +{ + this->file_name = "dump.out"; + this->append = false; + this->on = false; +} +dumper::dumper(CParser & parser, PHRQ_io *io) +: +PHRQ_base(io) +{ + this->file_name = "dump.out"; + this->append = false; + this->Read(parser); +} + +dumper::~dumper(void) +{ +} + +void dumper::SetAll(bool tf) +{ + this->binList.SetAll(tf); + +} +bool dumper::Read(CParser & parser) +{ + bool return_value(true); + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + opt_save = CParser::OPT_DEFAULT; + bool cleared_once = false; + this->on = true; + + for (;;) + { + int opt; + StorageBinListItem cells; + opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + else + { + opt_save = opt; + } + if (opt > 1 && !cleared_once) + { + binList.SetAll(false); + cleared_once = true; + } + // Select StorageBinListItem + StorageBinListItem *item = NULL; + switch (opt) + { + case 3: // cell + case 4: // cells + item = &cells; + break; + case 5: + case 6: + item = &(this->binList.Get_solution()); + break; + case 7: + case 8: + case 9: + case 10: + item = &(this->binList.Get_pp_assemblage()); + break; + case 11: + item = &(this->binList.Get_exchange()); + break; + case 12: + item = &(this->binList.Get_surface()); + break; + case 13: + case 14: + case 15: + item = &(this->binList.Get_ss_assemblage()); + break; + case 16: + case 17: + item = &(this->binList.Get_gas_phase()); + break; + case 18: + item = &(this->binList.Get_kinetics()); + break; + + case 19: // mix + item = &(this->binList.Get_mix()); + break; + case 20: // reaction + case 21: // reactions + item = &(this->binList.Get_reaction()); + break; + case 22: // temperature + case 23: // reaction_temperature + case 24: // reaction_temperatures + item = &(this->binList.Get_temperature()); + break; + case 25: // pressure + case 26: // reaction_pressure + case 27: // reaction_pressures + item = &(this->binList.Get_pressure()); + break; + default: + break; + } + + // Read dump entity list of numbers or number ranges for line, store in item + if ((opt > 2)) + { + for (;;) + { + CParser::TOKEN_TYPE j = parser.copy_token(token, next_char); + if (item && j == CParser::TT_DIGIT) + { + item->Augment(token); + } + else if (item && j == CParser::TT_EMPTY) + { + item->Augment(token); + break; + } + else + { + parser.error_msg("Expected single number or range of numbers.", + PHRQ_io::OT_CONTINUE); + } + } + } + + if (opt == 3 || opt == 4) + { + this->binList.TransferAll(cells); + } + // Process other identifiers + std::set < int >::iterator it; + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + break; + case 0: //file + std::getline(parser.get_iss(), this->file_name); + trim(this->file_name); + if (this->file_name.size() == 0) + { + this->file_name = "dump.out"; + } + + break; + case 1: //append + { + parser.copy_token(token, next_char); + //if (!(parser.get_iss() >> this->append)) + this->append = true; + if (token.c_str()[0] == 'f' || token.c_str()[0] == 'F') + { + this->append = false; + } + } + break; + case 2: //all + this->SetAll(true); + break; + default: + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input reading DUMP definition.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + return_value = false; + break; + } + + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + return(return_value); +} + +bool dumper::Get_bool_any(void) +{ + return ( + Get_bool_solution() || + Get_bool_pp_assemblage() || + Get_bool_exchange() || + Get_bool_surface() || + Get_bool_ss_assemblage() || + Get_bool_gas_phase() || + Get_bool_kinetics() || + Get_bool_mix() || + Get_bool_reaction() || + Get_bool_temperature() || + Get_bool_pressure() + ); +} +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("file"), // 0 + std::vector< std::string >::value_type("append"), // 1 + std::vector< std::string >::value_type("all"), // 2 + std::vector< std::string >::value_type("cell"), // 3 + std::vector< std::string >::value_type("cells"), // 4 + std::vector< std::string >::value_type("solution"), // 5 + std::vector< std::string >::value_type("solutions"), // 6 + std::vector< std::string >::value_type("pp_assemblage"), // 7 + std::vector< std::string >::value_type("pp_assemblages"), // 8 + std::vector< std::string >::value_type("equilibrium_phase"), // 9 + std::vector< std::string >::value_type("equilibrium_phases"), // 10 + std::vector< std::string >::value_type("exchange"), // 11 + std::vector< std::string >::value_type("surface"), // 12 + std::vector< std::string >::value_type("ss_assemblage"), // 13 + std::vector< std::string >::value_type("solid_solution"), // 14 + std::vector< std::string >::value_type("solid_solutions"), // 15 + std::vector< std::string >::value_type("gas_phase"), // 16 + std::vector< std::string >::value_type("gas_phases"), // 17 + std::vector< std::string >::value_type("kinetics"), // 18 + std::vector< std::string >::value_type("mix"), // 19 + std::vector< std::string >::value_type("reaction"), // 20 + std::vector< std::string >::value_type("reactions"), // 21 + std::vector< std::string >::value_type("temperature"), // 22 + std::vector< std::string >::value_type("reaction_temperature"), // 23 + std::vector< std::string >::value_type("reaction_temperatures"), // 24 + std::vector< std::string >::value_type("pressure"), // 25 + std::vector< std::string >::value_type("reaction_pressure"), // 26 + std::vector< std::string >::value_type("reaction_pressures") // 27 +}; +const std::vector< std::string > dumper::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/dumper.h b/phreeqcpp/dumper.h new file mode 100644 index 00000000..7ff0aac0 --- /dev/null +++ b/phreeqcpp/dumper.h @@ -0,0 +1,60 @@ +#if !defined(DUMPER_H_INCLUDED) +#define DUMPER_H_INCLUDED +#include // std::set +#include // std::string +#include // std::list +#include // std::vector +#include "StorageBinList.h" +class CParser; + +class dumper: public PHRQ_base +{ +public: + dumper(PHRQ_io *io=NULL); + dumper(CParser & parser, PHRQ_io *io=NULL); + virtual ~dumper(void); + bool Read(CParser & parser); + void SetAll(bool tf); + std::string Get_file_name(void) { return(this->file_name); }; + void Set_file_name(std::string fn) { this->file_name = fn; }; + bool Get_append(void)const { return(this->append); }; + void Set_append(bool app) { this->append = app; }; + bool Get_bool_solution(void) { return(this->binList.Get_solution().Get_defined()); }; + bool Get_bool_pp_assemblage(void) { return(this->binList.Get_pp_assemblage().Get_defined()); }; + bool Get_bool_exchange(void) { return(this->binList.Get_exchange().Get_defined()); }; + bool Get_bool_surface(void) { return(this->binList.Get_surface().Get_defined()); }; + bool Get_bool_ss_assemblage(void) { return(this->binList.Get_ss_assemblage().Get_defined()); }; + bool Get_bool_gas_phase(void) { return(this->binList.Get_gas_phase().Get_defined()); }; + bool Get_bool_kinetics(void) { return(this->binList.Get_kinetics().Get_defined()); }; + bool Get_bool_mix(void) { return(this->binList.Get_mix().Get_defined()); }; + bool Get_bool_reaction(void) { return(this->binList.Get_reaction().Get_defined()); }; + bool Get_bool_temperature(void) { return(this->binList.Get_temperature().Get_defined()); }; + bool Get_bool_pressure(void) { return(this->binList.Get_pressure().Get_defined()); }; + bool Get_bool_any(void); + + std::set < int > & Get_solution(void) { return(this->binList.Get_solution().Get_numbers()); }; + std::set < int > & Get_pp_assemblage(void) { return(this->binList.Get_pp_assemblage().Get_numbers()); }; + std::set < int > & Get_exchange(void) { return(this->binList.Get_exchange().Get_numbers()); }; + std::set < int > & Get_surface(void) { return(this->binList.Get_surface().Get_numbers()); }; + std::set < int > & Get_ss_assemblage(void) { return(this->binList.Get_ss_assemblage().Get_numbers()); }; + std::set < int > & Get_gas_phase(void) { return(this->binList.Get_gas_phase().Get_numbers()); }; + std::set < int > & Get_kinetics(void) { return(this->binList.Get_kinetics().Get_numbers()); }; + std::set < int > & Get_mix(void) { return(this->binList.Get_mix().Get_numbers()); }; + std::set < int > & Get_reaction(void) { return(this->binList.Get_reaction().Get_numbers()); }; + std::set < int > & Get_temperature(void) { return(this->binList.Get_temperature().Get_numbers()); }; + std::set < int > & Get_pressure(void) { return(this->binList.Get_pressure().Get_numbers()); }; + bool Get_on(void) { return this->on; }; + void Set_on(bool tf) { this->on = tf; }; + + StorageBinList & Get_StorageBinList(void) { return this->binList; }; + const StorageBinList & Get_StorageBinList(void)const { return this->binList; }; + void Set_StorageBinList(StorageBinList sbl) { this->binList = sbl; }; +protected: + std::string file_name; + bool append; + bool on; + StorageBinList binList; + const static std::vector < std::string > vopts; +}; + +#endif // !defined(DUMPER_H_INCLUDED) diff --git a/phreeqcpp/gases.cpp b/phreeqcpp/gases.cpp new file mode 100644 index 00000000..77a484ce --- /dev/null +++ b/phreeqcpp/gases.cpp @@ -0,0 +1,680 @@ +#include "Phreeqc.h" +#include "GasPhase.h" +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +setup_fixed_volume_gas(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Fill in data for gas phase unknown (sum of partial pressures) + * in unknown structure + */ + if (use.Get_gas_phase_ptr() == NULL) + return (OK); + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); +/* + * One for each gas component + */ + gas_unknowns.clear(); + gas_unknown = NULL; + gas_phase_ptr->Set_total_moles(0); + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + const cxxGasComp *comp_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + int j; + struct phase *phase_ptr = phase_bsearch(comp_ptr->Get_phase_name().c_str(), &j, FALSE); + x[count_unknowns]->type = GAS_MOLES; + x[count_unknowns]->description = phase_ptr->name; + x[count_unknowns]->phase = phase_ptr; + x[count_unknowns]->moles = comp_ptr->Get_moles(); + if (x[count_unknowns]->moles <= 0) + { + x[count_unknowns]->moles = MIN_TOTAL; + } + x[count_unknowns]->ln_moles = log(x[count_unknowns]->moles); + gas_unknowns.push_back(x[count_unknowns]); + gas_phase_ptr->Set_total_moles(gas_phase_ptr->Get_total_moles() + x[count_unknowns]->moles); + x[count_unknowns]->phase->moles_x = x[count_unknowns]->moles; + count_unknowns++; + } + if (gas_unknowns.size() > 0) + { + gas_unknown = gas_unknowns[0]; + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_fixed_volume_gas(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Put coefficients into lists to sum iaps to test for equilibrium + * Put coefficients into lists to build jacobian for + * sum of partial pressures equation and + * mass balance equations for elements contained in gases + */ + int row, col; + struct master *master_ptr; + struct rxn_token *rxn_ptr; + struct unknown *unknown_ptr; + LDBLE coef, coef_elt; + + if (gas_unknown == NULL) + return (OK); + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + const cxxGasComp *comp_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + int j; + struct phase *phase_ptr = phase_bsearch(comp_ptr->Get_phase_name().c_str(), &j, FALSE); +/* + * Determine elements in gas component + */ + count_elts = 0; + paren_count = 0; + if (phase_ptr->rxn_x == NULL) + continue; + add_elt_list(phase_ptr->next_elt, 1.0); +#define COMBINE +#ifdef COMBINE + change_hydrogen_in_elt_list(0); +#endif +/* + * Build mass balance sums for each element in gas + */ + if (debug_prep == TRUE) + { + output_msg(sformatf( "\n\tMass balance summations %s.\n\n", + phase_ptr->name)); + } + + /* All elements in gas */ + for (j = 0; j < count_elts; j++) + { + unknown_ptr = NULL; + if (strcmp(elt_list[j].elt->name, "H") == 0) + { + unknown_ptr = mass_hydrogen_unknown; + } + else if (strcmp(elt_list[j].elt->name, "O") == 0) + { + unknown_ptr = mass_oxygen_unknown; + } + else + { + if (elt_list[j].elt->primary->in == TRUE) + { + unknown_ptr = elt_list[j].elt->primary->unknown; + } + else if (elt_list[j].elt->primary->s->secondary != NULL) + { + unknown_ptr = + elt_list[j].elt->primary->s->secondary->unknown; + } + } + if (unknown_ptr != NULL) + { + coef = elt_list[j].coef; + store_mb(&(gas_unknowns[i]->moles), &(unknown_ptr->f), coef); + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\n", + unknown_ptr->description, (double) coef)); + } + } + } + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + /* Total pressure of gases */ + store_mb(&(phase_ptr->p_soln_x), &(gas_unknown->f), + 1.0); + } +/* + * Build jacobian sums for mass balance equations + */ + if (debug_prep == TRUE) + { + output_msg(sformatf( "\n\tJacobian summations %s.\n\n", + phase_ptr->name)); + } + for (j = 0; j < count_elts; j++) + { + unknown_ptr = NULL; + if (strcmp(elt_list[j].elt->name, "H") == 0) + { + unknown_ptr = mass_hydrogen_unknown; + } + else if (strcmp(elt_list[j].elt->name, "O") == 0) + { + unknown_ptr = mass_oxygen_unknown; + } + else + { + if (elt_list[j].elt->primary->in == TRUE) + { + unknown_ptr = elt_list[j].elt->primary->unknown; + } + else if (elt_list[j].elt->primary->s->secondary != NULL) + { + unknown_ptr = + elt_list[j].elt->primary->s->secondary->unknown; + } + } + if (unknown_ptr == NULL) + { + continue; + } + if (debug_prep == TRUE) + { + output_msg(sformatf( "\n\t%s.\n", + unknown_ptr->description)); + } + row = unknown_ptr->number * (count_unknowns + 1); + coef_elt = elt_list[j].coef; + for (rxn_ptr = phase_ptr->rxn_x->token + 1; + rxn_ptr->s != NULL; rxn_ptr++) + { + + if (rxn_ptr->s->secondary != NULL + && rxn_ptr->s->secondary->in == TRUE) + { + master_ptr = rxn_ptr->s->secondary; + } + else if (rxn_ptr->s->primary != NULL && rxn_ptr->s->primary->in == TRUE) + { + master_ptr = rxn_ptr->s->primary; + } + else + { + master_ptr = master_bsearch_primary(rxn_ptr->s->name); + master_ptr->s->la = -999.0; + } + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%s\n", + master_ptr->s->name)); + } + if (master_ptr->unknown == NULL) + { + continue; + } + if (master_ptr->in == FALSE) + { + error_string = sformatf( + "Element, %s, in phase, %s, is not in model.", + master_ptr->elt->name, phase_ptr->name); + error_msg(error_string, CONTINUE); + input_error++; + } + col = master_ptr->unknown->number; + coef = coef_elt * rxn_ptr->coef; + store_jacob(&(gas_unknowns[i]->moles), + &(array[row + col]), coef); + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d\n", + master_ptr->s->name, (double) coef, + row / (count_unknowns + 1), col)); + } + } + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + /* derivative wrt total moles of gas */ + store_jacob(&(phase_ptr->fraction_x), + &(array[row + gas_unknown->number]), coef_elt); + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d\n", + "gas moles", (double) elt_list[j].coef, + row / (count_unknowns + 1), + gas_unknown->number)); + } + } + } +/* + * Build jacobian sums for sum of partial pressures equation + */ + if (gas_phase_ptr->Get_type() != cxxGasPhase::GP_PRESSURE) + continue; + if (debug_prep == TRUE) + { + output_msg(sformatf( "\n\tPartial pressure eqn %s.\n\n", + phase_ptr->name)); + } + unknown_ptr = gas_unknown; + row = unknown_ptr->number * (count_unknowns + 1); + for (rxn_ptr = phase_ptr->rxn_x->token + 1; rxn_ptr->s != NULL; rxn_ptr++) + { + if (rxn_ptr->s != s_eminus && rxn_ptr->s->in == FALSE) + { + error_string = sformatf( + "Element in species, %s, in phase, %s, is not in model.", + rxn_ptr->s->name, phase_ptr->name); + warning_msg(error_string); + } + else + { + if (rxn_ptr->s->secondary != NULL + && rxn_ptr->s->secondary->in == TRUE) + { + master_ptr = rxn_ptr->s->secondary; + } + else if (rxn_ptr->s->primary != NULL && rxn_ptr->s->primary->in == TRUE) + { + master_ptr = rxn_ptr->s->primary; + } + else + { + master_ptr = master_bsearch_primary(rxn_ptr->s->name); + if (master_ptr && master_ptr->s) + { + master_ptr->s->la = -999.0; + } + } + + if (master_ptr == NULL) + { + error_string = sformatf( + "Master species for %s, in phase, %s, is not in model.", + rxn_ptr->s->name, phase_ptr->name); + error_msg(error_string, CONTINUE); + input_error++; + } + else + { + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%s\n", master_ptr->s->name)); + } + if (master_ptr->unknown == NULL) + { + assert(false); + continue; + } + if (master_ptr->in == FALSE) + { + error_string = sformatf( + "Element, %s, in phase, %s, is not in model.", + master_ptr->elt->name, phase_ptr->name); + warning_msg(error_string); + } + col = master_ptr->unknown->number; + coef = rxn_ptr->coef; + store_jacob(&(phase_ptr->p_soln_x), &(array[row + col]), coef); + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d\n", + master_ptr->s->name, (double) coef, + row / (count_unknowns + 1), col)); + } + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_PR(void) +/* ---------------------------------------------------------------------- */ +/* Calculate fugacity and fugacity coefficient for gas pressures if critical T and P + are defined. + 1) Solve molar volume V_m or total pressure P from Peng-Robinson's EOS: + P = R * T / (V_m - b) - a * aa / (V_m^2 + 2 * b * V_m - b^2) + a = 0.457235 * (R * T_c)^2 / P_c + b = 0.077796 * R * T_c / P_c + aa = (1 + kk * (1 - T_r^0.5))^2 + kk = 0.37464 + 1.54226 * omega - 0.26992 * omega^2 + T_r = T / T_c + multicomponent gas phase: + use: b_sum = Sum(x_i * b), x_i is mole-fraction + a_aa_sum = Sum_i( Sum_j(x_i * x_j * (a_i * aa_i * a_j * aa_j)^0.5) ) + 2) Find the fugacity coefficient phi for gas i: + log(phi_i) = B_ratio * (z - 1) - log(z - B) + A / (2.8284 * B) * (B_ratio - 2 / a_aa_sum * a_aa_sum2) *\ + log((z + 2.4142 * B) / (z - 0.4142 * B)) + B_ratio = b_i / b_sum + A = a_aa_sum * P / R_TK^2 + B = b_sum * P / R_TK + a_aa_sum2 = Sum_j(x_j * (a_aa_i * a_aa_j)^0.5 + 3) correct the solubility of gas i with: + pr_si_f = log10(phi_i) - Delta_V_i * (P - 1) / (2.303 * R * TK); +*/ +{ + LDBLE T_c, P_c; + LDBLE A, B, B_r, /*b2,*/ kk, oo, a_aa, T_r; + LDBLE m_sum, /*b_sum, a_aa_sum,*/ a_aa_sum2; + LDBLE phi; + LDBLE /*R_TK,*/ R = R_LITER_ATM; /* L atm / (K mol) */ + LDBLE r3[4], r3_12, rp, rp3, rq, rz, ri, ri1, one_3 = 0.33333333333333333; + LDBLE disct, vinit, v1, ddp, dp_dv, dp_dv2; + int it; + struct phase *phase_ptr; + LDBLE V_m = 0, P = 0; + + LDBLE TK = tk_x; + bool halved; + R_TK = R * TK; + m_sum = b_sum = a_aa_sum = 0.0; + size_t i; + + if (gas_unknowns.size() == 0) + { + error_msg("No gas unknowns.", STOP); + } + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + + for (i = 0; i < gas_unknowns.size(); i++) + { + m_sum += gas_unknowns[i]->moles; + phase_ptr = gas_unknowns[i]->phase; + if (phase_ptr->t_c == 0.0 || phase_ptr->p_c == 0.0) + error_msg("Cannot calculate a mixture of ideal and Peng_Robinson gases,\n please define Tc and Pc for the active gases in PHASES.", STOP); + //continue; + if (!phase_ptr->pr_a) + { + T_c = phase_ptr->t_c; + P_c = phase_ptr->p_c; + phase_ptr->pr_a = 0.457235 * R * R * T_c * T_c / P_c; + phase_ptr->pr_b = 0.077796 * R * T_c / P_c; + T_r = TK / T_c; + oo = phase_ptr->omega; + kk = 0.37464 + oo * (1.54226 - 0.26992 * oo); + phase_ptr->pr_alpha = pow(1 + kk * (1 - sqrt(T_r)), 2); + phase_ptr->pr_tk = TK; + phase_ptr->pr_in = true; + } + if (phase_ptr->pr_tk != TK) + { + T_r = TK / phase_ptr->t_c; + oo = phase_ptr->omega; + kk = 0.37464 + oo * (1.54226 - 0.26992 * oo); + phase_ptr->pr_alpha = pow(1 + kk * (1 - sqrt(T_r)), 2); + phase_ptr->pr_tk = TK; + phase_ptr->pr_in = true; + } + } + if (m_sum == 0) + return (OK); + gas_phase_ptr->Set_v_m(gas_phase_ptr->Get_volume() / m_sum); + for (i = 0; i < gas_unknowns.size(); i++) + { + phase_ptr = gas_unknowns[i]->phase; + phase_ptr->fraction_x = gas_unknowns[i]->moles / m_sum; // phase_ptr->fraction_x updated + } + + for (i = 0; i < gas_unknowns.size(); i++) + { + a_aa_sum2 = 0.0; + phase_ptr = gas_unknowns[i]->phase; + //if (phase_ptr->t_c == 0.0 || phase_ptr->p_c == 0.0) + // continue; + b_sum += phase_ptr->fraction_x * phase_ptr->pr_b; + size_t i1; + struct phase *phase_ptr1; + for (i1 = 0; i1 < gas_unknowns.size(); i1++) + { + phase_ptr1 = gas_unknowns[i1]->phase; + //if (phase_ptr1->t_c == 0.0 || phase_ptr1->p_c == 0.0) + // continue; + if (phase_ptr1->fraction_x == 0) + continue; + a_aa = sqrt(phase_ptr->pr_a * phase_ptr->pr_alpha * + phase_ptr1->pr_a * phase_ptr1->pr_alpha); + if (!strcmp(phase_ptr->name, "H2O(g)")) + { + if (!strcmp(phase_ptr1->name, "CO2(g)")) + a_aa *= 0.81; // Soreide and Whitson, 1992, FPE 77, 217 + else if (!strcmp(phase_ptr1->name, "H2S(g)")) + a_aa *= 0.81; + else if (!strcmp(phase_ptr1->name, "CH4(g)")) + a_aa *= 0.51; + else if (!strcmp(phase_ptr1->name, "N2(g)")) + a_aa *= 0.51; + } + if (!strcmp(phase_ptr1->name, "H2O(g)")) + { + if (!strcmp(phase_ptr->name, "CO2(g)")) + a_aa *= 0.81; + else if (!strcmp(phase_ptr->name, "H2S(g)")) + a_aa *= 0.81; + else if (!strcmp(phase_ptr->name, "CH4(g)")) + a_aa *= 0.51; + else if (!strcmp(phase_ptr->name, "N2(g)")) + a_aa *= 0.51; + } + a_aa_sum += phase_ptr->fraction_x * phase_ptr1->fraction_x * a_aa; + a_aa_sum2 += phase_ptr1->fraction_x * a_aa; + } + phase_ptr->pr_aa_sum2 = a_aa_sum2; + } + b2 = b_sum * b_sum; + + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME) + { + V_m = gas_phase_ptr->Get_volume() / m_sum; + P = 0.0; + while (P <= 0) + { + P = R_TK / (V_m - b_sum) - a_aa_sum / (V_m * (V_m + 2 * b_sum) - b2); + if (P <= 0.0) + { + V_m *= 2.0; + //a_aa_sum /= 2.0; + } + } + if (iterations > 0 && P < 150 && V_m < 1.01) + { + // check for 3-roots... + r3[1] = b_sum - R_TK / P; + r3[2] = -3.0 * b2 + (a_aa_sum - R_TK * 2.0 * b_sum) / P; + r3[3] = b2 * b_sum + (R_TK * b2 - b_sum * a_aa_sum) / P; + // the discriminant of the cubic eqn... + disct = 18. * r3[1] * r3[2] * r3[3] - + 4. * pow(r3[1], 3) * r3[3] + + r3[1] * r3[1] * r3[2] * r3[2] - + 4. * pow(r3[2], 3) - + 27. * r3[3] * r3[3]; + if (disct > 0) + { + // 3-roots, find the largest P... + it = 0; + halved = false; + ddp = 1e-9; + v1 = vinit = 0.729; + dp_dv = f_Vm(v1, this); + while (fabs(dp_dv) > 1e-11 && it < 40) + { + it +=1; + dp_dv2 = f_Vm(v1 - ddp, this); + v1 -= (dp_dv * ddp / (dp_dv - dp_dv2)); + if (!halved && (v1 > vinit || v1 < 0.03)) + { + if (vinit > 0.329) + vinit -= 0.1; + else + vinit -=0.05; + if (vinit < 0.03) + { + vinit = halve(f_Vm, 0.03, 1.0, 1e-3); + if (f_Vm(vinit - 2e-3, this) < 0) + vinit = halve(f_Vm, vinit + 2e-3, 1.0, 1e-3); + halved = true; + } + v1 = vinit; + } + dp_dv = f_Vm(v1, this); + if (fabs(dp_dv) < 1e-11) + { + if (f_Vm(v1 - 1e-4, this) < 0) + { + v1 = halve(f_Vm, v1 + 1e-4, 1.0, 1e-3); + dp_dv = f_Vm(v1, this); + } + } + } + if (it == 40) + { +// accept a (possible) whobble in the curve... +// error_msg("No convergence when calculating P in Peng-Robinson.", STOP); + } + if (V_m < v1 && it < 40) + P = R_TK / (v1 - b_sum) - a_aa_sum / (v1 * (v1 + 2 * b_sum) - b2); + } + } + if (P <= 0) // iterations = -1 + P = 1.; + gas_phase_ptr->Set_total_p(P); // phase_ptr->total_p updated + gas_phase_ptr->Set_v_m(V_m); + } + else + { + assert(false); + P = gas_phase_ptr->Get_total_p(); + r3[1] = b_sum - R_TK / P; + r3_12 = r3[1] * r3[1]; + r3[2] = -3.0 * b2 + (a_aa_sum - R_TK * 2.0 * b_sum) / P; + r3[3] = b2 * b_sum + (R_TK * b2 - b_sum * a_aa_sum) / P; + // solve t^3 + rp*t + rq = 0. + // molar volume V_m = t - r3[1] / 3... + rp = r3[2] - r3_12 / 3; + rp3 = rp * rp * rp; + rq = (2.0 * r3_12 * r3[1] - 9.0 * r3[1] * r3[2]) / 27 + r3[3]; + rz = rq * rq / 4 + rp3 / 27; + if (rz >= 0) // Cardono's method... + { + ri = sqrt(rz); + if (ri + rq / 2 <= 0) + { + V_m = pow(ri - rq / 2, one_3) + pow(- ri - rq / 2, one_3) - r3[1] / 3; + } else + { + ri = - pow(ri + rq / 2, one_3); + V_m = ri - rp / (3.0 * ri) - r3[1] / 3; + } + } else // use complex plane... + { + ri = sqrt(- rp3 / 27); // rp < 0 + ri1 = acos(- rq / 2 / ri); + V_m = 2.0 * pow(ri, one_3) * cos(ri1 / 3) - r3[1] / 3; + } + gas_phase_ptr->Set_v_m(V_m); // phase_ptr->fraction_x updated + } + // calculate the fugacity coefficients... + for (i = 0; i < gas_unknowns.size(); i++) + { + phase_ptr = gas_unknowns[i]->phase; + if (phase_ptr->fraction_x == 0.0) + { + phase_ptr->pr_p = 0; + phase_ptr->pr_phi = 1; + phase_ptr->pr_si_f = 0.0; + //phase_ptr->logk[vm0] = 0.0; // *** + continue; + } + phase_ptr->pr_p = phase_ptr->fraction_x * P; + + //if (phase_ptr->t_c == 0.0 || phase_ptr->p_c == 0.0) + //{ + // phase_ptr->pr_phi = 1; + // continue; + //} + rz = P * V_m / R_TK; + A = a_aa_sum * P / (R_TK * R_TK); + B = b_sum * P / R_TK; + B_r = phase_ptr->pr_b / b_sum; + if (rz > B) + { + phi = B_r * (rz - 1) - log(rz - B) + A / (2.828427 * B) * (B_r - 2.0 * phase_ptr->pr_aa_sum2 / a_aa_sum) * + log((rz + 2.41421356 * B) / (rz - 0.41421356 * B)); + phi = (phi > 4.44 ? 4.44 : (phi < -3 ? -3 : phi)); + } + else + phi = -3.0; // fugacity coefficient > 0.05 + phase_ptr->pr_phi = exp(phi); + phase_ptr->pr_si_f = phi / LOG_10; // pr_si_f updated + // **** + //phase_ptr->logk[vm0] = V_m * 1e3 * phase_ptr->fraction_x; + phase_ptr->pr_in = true; + } + return (V_m); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_fixed_volume_gas_pressures(void) +/* ---------------------------------------------------------------------- */ +{ + int n_g = 0; + LDBLE lp; + struct rxn_token *rxn_ptr; + struct phase *phase_ptr; + bool PR = false, pr_done = false; + size_t i; +/* + * moles and partial pressures for gases + */ + if (use.Get_gas_phase_ptr() == NULL) + return (OK); + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + gas_phase_ptr->Set_total_moles(0); + + for (i = 0; i < gas_unknowns.size(); i++) + { + phase_ptr = gas_unknowns[i]->phase; + if (phase_ptr->in == TRUE) + { + if (!PR && phase_ptr->t_c > 0 && phase_ptr->p_c > 0) + PR = true; + n_g++; + } + gas_phase_ptr->Set_total_moles(gas_phase_ptr->Get_total_moles() + gas_unknowns[i]->moles); + } + if (PR && gas_phase_ptr->Get_total_moles() > 0) + { + calc_PR(); + pr_done = true; + gas_phase_ptr->Set_total_moles(0); + } else + { + gas_phase_ptr->Set_total_p(0); + gas_phase_ptr->Set_total_moles(0); + } + for (i = 0; i < gas_unknowns.size(); i++) + { + phase_ptr = gas_unknowns[i]->phase; + if (phase_ptr->in == TRUE) + { + lp = -phase_ptr->lk; + //lp = -k_calc(phase_ptr->rxn_x->logk, tk_x, use.Get_gas_phase_ptr()->total_p * PASCAL_PER_ATM); + for (rxn_ptr = phase_ptr->rxn_x->token + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + lp += rxn_ptr->s->la * rxn_ptr->coef; + } + phase_ptr->p_soln_x = exp(LOG_10 * (lp - phase_ptr->pr_si_f)); + + if (pr_done) + { + lp = phase_ptr->p_soln_x / gas_phase_ptr->Get_total_p() * + gas_phase_ptr->Get_volume() / gas_phase_ptr->Get_v_m(); + phase_ptr->moles_x = lp; + } + else + { + phase_ptr->moles_x = phase_ptr->p_soln_x * gas_phase_ptr->Get_volume() / (R_LITER_ATM * tk_x); + gas_phase_ptr->Set_total_p(gas_phase_ptr->Get_total_p() + phase_ptr->p_soln_x); + } + gas_phase_ptr->Set_total_moles(gas_phase_ptr->Get_total_moles() + phase_ptr->moles_x); + } + else + { + phase_ptr->moles_x = 0; + phase_ptr->fraction_x = 0; + } + } + + return (OK); +} diff --git a/phreeqcpp/global_structures.h b/phreeqcpp/global_structures.h new file mode 100644 index 00000000..aec2ab56 --- /dev/null +++ b/phreeqcpp/global_structures.h @@ -0,0 +1,1113 @@ +#ifndef _INC_GLOBAL_STRUCTURES_H +#define _INC_GLOBAL_STRUCTURES_H +#include "Surface.h" +/* ---------------------------------------------------------------------- + * #define DEFINITIONS + * ---------------------------------------------------------------------- */ +#ifndef NAN +# define NAN -99999999 +#endif +#define MISSING -9999.999 +#include "NA.h" /* NA = not available */ + +#define F_C_MOL 96493.5 /* C/mol or joule/volt-eq */ +#define F_KJ_V_EQ 96.4935 /* kJ/volt-eq */ +#define F_KCAL_V_EQ 23.0623 /* kcal/volt-eq */ +#define R_LITER_ATM 0.0820597 /* L-atm/deg-mol */ +#define R_KCAL_DEG_MOL 0.00198726 /* kcal/deg-mol */ +#define R_KJ_DEG_MOL 0.00831470 /* kJ/deg-mol */ +#define EPSILON 78.5 /* dialectric constant, dimensionless. Is calculated as eps_r(P, T) in calc_dielectrics. Update the code?? */ +#define EPSILON_ZERO 8.854e-12 /* permittivity of free space, C/V-m = C**2/m-J */ +#define JOULES_PER_CALORIE 4.1840 +#define PASCAL_PER_ATM 1.01325E5 /* conversion from atm to Pa */ +#define AVOGADRO 6.02252e23 /* atoms / mole */ +#define pi 3.14159265358979 +#define AH2O_FACTOR 0.017 + +#define TRUE 1 +#define FALSE 0 +#define OK 1 +#define ERROR 0 +#define STOP 1 +#define CONTINUE 0 + +#define OPTION_EOF -1 +#define OPTION_KEYWORD -2 +#define OPTION_ERROR -3 +#define OPTION_DEFAULT -4 +#define OPT_1 -5 + +#define DISP 2 +#define STAG 3 +#define NOMIX 4 + +#define CONVERGED 2 +#define MASS_BALANCE 3 + +#define REWRITE 2 +#define INIT -1 + +/* check_line values, plus EMPTY, EOF, OK */ +#define KEYWORD 3 + +/* copy_token values */ +#define EMPTY 2 +#define UPPER 4 +#define LOWER 5 +#define DIGIT 6 +#define UNKNOWN 7 +#define OPTION 8 + +/* species types */ +#define AQ 0 +#define HPLUS 1 +#define H2O 2 +#define EMINUS 3 +#define SOLID 4 +#define EX 5 +#define SURF 6 +#define SURF_PSI 7 +#define SURF_PSI1 8 +#define SURF_PSI2 9 + +/* unknown types */ +#define MB 10 +#define ALK 11 +#define CB 12 +#define SOLUTION_PHASE_BOUNDARY 13 +#define MU 14 +#define AH2O 15 +#define MH 16 +#define MH2O 17 +#define PP 18 +#define EXCH 19 +#define SURFACE 20 +#define SURFACE_CB 21 +#define SURFACE_CB1 22 +#define SURFACE_CB2 23 +#define GAS_MOLES 24 +#define SS_MOLES 25 +#define PITZER_GAMMA 26 +#define SLACK 28 +/* state */ +#define INITIALIZE 0 +#define INITIAL_SOLUTION 1 +#define INITIAL_EXCHANGE 2 +#define INITIAL_SURFACE 3 +#define INITIAL_GAS_PHASE 4 +#define REACTION 5 +#define INVERSE 6 +#define ADVECTION 7 +#define TRANSPORT 8 +#define PHAST 9 + +/* constaints in mass balance */ +#define EITHER 0 +#define DISSOLVE 1 +#define PRECIPITATE -1 + +/* gas phase type */ +#define PRESSURE 1 +#define VOLUME 2 + +#define MAX_PP_ASSEMBLAGE 10 /* default estimate of the number of phase assemblages */ +#define MAX_ADD_EQUATIONS 20 /* maximum number of equations added together to reduce eqn to + master species */ +#define MAX_ELEMENTS 50 /* default estimate of the number of elements */ +#define MAX_LENGTH 256 /* maximum number of characters component name */ +#define MAX_LINE 4096 /* estimate of maximum line length */ +#define MAX_MASS_BALANCE 10 /* initial guess of number mass balance equations for a solution */ +#define MAX_MASTER 50 /* default estimate of the number of master species */ +#define MAX_ELTS 15 /* default estimate for maximum number of times elements occur in + an equation */ +#define MAX_PHASES 500 /* initial guess of number of phases defined */ +#define MAX_S 500 /* default estimate for maximum number of species in aqueous model */ +#define MAX_SUM_JACOB0 50 /* list used to calculate jacobian */ +#define MAX_SUM_JACOB1 500 /* list used to calculate jacobian */ +#define MAX_SUM_JACOB2 500 /* list used to calculate jacobian */ +#define MAX_SUM_MB 500 /* list used to calculate mass balance sums */ +#define MAX_TRXN 16 /* default estimate for maximum number of components in an eqn */ +#define MAX_UNKNOWNS 15 /* default estimate for maximum number of unknowns in model */ +#define TOL 1e-9 /* tolerance for comparisons of double numbers */ +#define MAX_LM 3.0 /* maximum log molality allowed in intermediate iterations */ +#define MAX_M 1000.0 +#ifdef USE_DECIMAL128 +// #define MIN_LM -80.0 /* minimum log molality allowed before molality set to zero */ +// #define LOG_ZERO_MOLALITY -80 /* molalities <= LOG_ZERO_MOLALITY are considered equal to zero */ +// #define MIN_TOTAL 1e-60 +// #define MIN_TOTAL_SS MIN_TOTAL/100 +// #define MIN_RELATED_SURFACE MIN_TOTAL*100 +// #define MIN_RELATED_LOG_ACTIVITY -60 +#else +// #define MIN_LM -30.0 /* minimum log molality allowed before molality set to zero */ +// #define LOG_ZERO_MOLALITY -30 /* molalities <= LOG_ZERO_MOLALITY are considered equal to zero */ +// #define MIN_TOTAL 1e-25 +// #define MIN_TOTAL_SS MIN_TOTAL/100 +// #define MIN_RELATED_SURFACE MIN_TOTAL*100 +// #define MIN_RELATED_LOG_ACTIVITY -30 +#endif +#define REF_PRES_PASCAL 1.01325E5 /* Reference pressure: 1 atm */ +/* + * Hash definitions + */ +# define SegmentSize 256 +# define SegmentSizeShift 8 /* log2(SegmentSize) */ +# define DirectorySize 256 +# define DirectorySizeShift 8 /* log2(DirectorySize) */ +# define Prime1 37 +# define Prime2 1048583 +# define DefaultMaxLoadFactor 5 +// +// Typedefs and structure definitions +// +typedef enum { kcal, cal, kjoules, joules } DELTA_H_UNIT; +typedef enum { cm3_per_mol, dm3_per_mol, m3_per_mol } DELTA_V_UNIT; +enum entity_type +{ Solution, Reaction, Exchange, Surface, Gas_phase, Pure_phase, Ss_phase, + Kinetics, Mix, Temperature, Pressure, UnKnown +}; + +typedef enum { + logK_T0, + delta_h, + T_A1, + T_A2, + T_A3, + T_A4, + T_A5, + T_A6, + delta_v, /* set in calc_delta_v: calculated molar volume-change of the reaction */ + vm_tc, /* set in calc_vm: calculated molal volume of the species at tc */ + vm0, /* read: molar volume of a phase */ + vma1, vma2, vma3, vma4, /* read: a1..a4 from supcrt, see calc_vm */ + wref, /* from supcrt */ + b_Av, /* b in z^2 * A_v * log(1 + b * I^0.5) / (2 * b) */ + vmi1, vmi2, vmi3, vmi4, /* ionic strength terms: (i1 + i2/(TK - 228) + i3 * (TK - 228) ) * I^i4 */ + MAX_LOG_K_INDICES /* Keep this definition at the end of the enum */ +} LOG_K_INDICES; +/* HSEARCH(3C) */ +typedef struct entry +{ + const char *key; + void *data; +} ENTRY; +typedef enum +{ FIND, ENTER } ACTION; + +/* TSEARCH(3C) */ +typedef enum +{ preorder, postorder, endorder, leaf } VISIT; + +typedef struct Element +{ + /* + ** The user only sees the first two fields, + ** as we pretend to pass back only a pointer to ENTRY. + ** {S}he doesn`t know what else is in here. + */ + const char *Key; + char *Data; + struct Element *Next; /* secret from user */ +} Element, *Segment; + +typedef struct +{ + short p; /* Next bucket to be split */ + short maxp; /* upper bound on p during expansion */ + long KeyCount; /* current # keys */ + short SegmentCount; /* current # segments */ + short MinLoadFactor; + short MaxLoadFactor; + Segment *Directory[DirectorySize]; +} HashTable; + +typedef unsigned long Address; + +typedef struct PHRQMemHeader +{ + struct PHRQMemHeader *pNext; /* memory allocated just after this one */ + struct PHRQMemHeader *pPrev; /* memory allocated just prior to this one */ + size_t size; /* memory request + sizeof(PHRQMemHeader) */ +#if !defined(NDEBUG) + char *szFileName; /* file name */ + int nLine; /* line number */ + int dummy; /* alignment */ +#endif +} PHRQMemHeader; + +struct model +{ + int force_prep; + LDBLE temperature; + int count_exchange; + struct master **exchange; + + int count_kinetics; + struct kinetics *kinetics; + + int count_gas_phase; + struct phase **gas_phase; + + int count_ss_assemblage; + const char **ss_assemblage; + + int count_pp_assemblage; + struct phase **pp_assemblage; + const char **add_formula; + LDBLE *si; + + cxxSurface::DIFFUSE_LAYER_TYPE dl_type; + cxxSurface::SURFACE_TYPE surface_type; + int only_counter_ions; + + LDBLE thickness; + int count_surface_comp; + const char **surface_comp; + int count_surface_charge; + const char **surface_charge; + LDBLE pressure; + bool numerical_fixed_volume; +}; + + + +struct name_master +{ + const char *name; + struct master *master; +}; +struct name_species +{ + const char *name; + struct species *s; +}; +struct name_phase +{ + const char *name; + struct phase *phase; +}; +#ifdef SKIP +struct punch +{ + int in; + int new_def; + struct name_master *totals; + int count_totals; + struct name_species *molalities; + int count_molalities; + struct name_species *activities; + int count_activities; + struct name_phase *pure_phases; + int count_pure_phases; + struct name_phase *si; + int count_si; + struct name_phase *gases; + int count_gases; + struct name_phase *s_s; + int count_s_s; + struct name_phase *kinetics; + int count_kinetics; + struct name_master *isotopes; + int count_isotopes; + struct name_master *calculate_values; + int count_calculate_values; + int inverse; + int sim; + int state; + int soln; + int dist; + int time; + int step; + int rxn; + int temp; + int ph; + int pe; + int alk; + int mu; + int water; + int high_precision; + int user_punch; + int charge_balance; + int percent_error; +}; +#endif +struct Change_Surf +{ + const char *comp_name; + LDBLE fraction; + const char *new_comp_name; + LDBLE new_Dw; + int cell_no; + int next; +}; + +struct Charge_Group +{ + LDBLE z; + LDBLE eq; +}; + +/*---------------------------------------------------------------------- + * Save + *---------------------------------------------------------------------- */ +struct save_values +{ + LDBLE value; + int count_subscripts; + int *subscripts; +}; + +struct save +{ + int solution; + int n_solution_user; + int n_solution_user_end; + int mix; + int n_mix_user; + int n_mix_user_end; + int reaction; + int n_reaction_user; + int n_reaction_user_end; + int pp_assemblage; + int n_pp_assemblage_user; + int n_pp_assemblage_user_end; + int exchange; + int n_exchange_user; + int n_exchange_user_end; + int kinetics; + int n_kinetics_user; + int n_kinetics_user_end; + int surface; + int n_surface_user; + int n_surface_user_end; + int gas_phase; + int n_gas_phase_user; + int n_gas_phase_user_end; + int ss_assemblage; + int n_ss_assemblage_user; + int n_ss_assemblage_user_end; +}; + +/*---------------------------------------------------------------------- + * Copy + *---------------------------------------------------------------------- */ +struct copier +{ + int count; + int max; + int *n_user; + int *start; + int *end; +}; + +/*---------------------------------------------------------------------- + * Inverse + *---------------------------------------------------------------------- */ +struct inverse +{ + int n_user; + char *description; + int new_def; + int minimal; + int range; + int mp; + LDBLE mp_censor; + LDBLE range_max; + LDBLE tolerance; + LDBLE mp_tolerance; + int count_uncertainties; + LDBLE *uncertainties; + int count_ph_uncertainties; + LDBLE *ph_uncertainties; + LDBLE water_uncertainty; + int mineral_water; + int carbon; + LDBLE *dalk_dph; + LDBLE *dalk_dc; + int count_solns; + int *solns; + int count_force_solns; + int *force_solns; + int count_elts; + struct inv_elts *elts; + int count_phases; + struct inv_phases *phases; + int count_master_list; + struct master **master_list; + int count_redox_rxns; + int count_isotopes; + struct inv_isotope *isotopes; + int count_i_u; + struct inv_isotope *i_u; + int count_isotope_unknowns; + struct isotope *isotope_unknowns; + const char *netpath; + const char *pat; +}; +struct inv_elts +{ + const char *name; + struct master *master; + int row; + int count_uncertainties; + LDBLE *uncertainties; +}; +struct inv_isotope +{ + const char *isotope_name; + LDBLE isotope_number; + const char *elt_name; + int count_uncertainties; + LDBLE *uncertainties; +}; +struct inv_phases +{ + const char *name; + struct phase *phase; + int column; + int constraint; + int force; + int count_isotopes; + struct isotope *isotopes; +}; +struct name_coef +{ + const char *name; + LDBLE coef; +}; +/*---------------------------------------------------------------------- + * Species_list + *---------------------------------------------------------------------- */ +struct species_list +{ + struct species *master_s; + struct species *s; + LDBLE coef; +}; + +/*---------------------------------------------------------------------- + * Jacobian and Mass balance lists + *---------------------------------------------------------------------- */ +struct list0 +{ + LDBLE *target; + LDBLE coef; +}; +struct list1 +{ + LDBLE *source; + LDBLE *target; +}; +struct list2 +{ + LDBLE *source; + LDBLE *target; + LDBLE coef; +}; + + struct isotope + { + LDBLE isotope_number; + const char *elt_name; + const char *isotope_name; + LDBLE total; + LDBLE ratio; + LDBLE ratio_uncertainty; + LDBLE x_ratio_uncertainty; + struct master *master; + struct master *primary; + LDBLE coef; /* coefficient of element in phase */ +}; +struct iso +{ + const char *name; + LDBLE value; + LDBLE uncertainty; +}; +/*---------------------------------------------------------------------- + * Transport data + *---------------------------------------------------------------------- */ +struct stag_data +{ + int count_stag; + LDBLE exch_f; + LDBLE th_m; + LDBLE th_im; +}; +struct cell_data +{ + LDBLE length; + LDBLE mid_cell_x; + LDBLE disp; + LDBLE temp; + LDBLE por; /* free (uncharged) porewater porosities */ + LDBLE por_il; /* interlayer water porosities */ + LDBLE potV; /* potential (V) */ + int punch; + int print; +}; + +/*---------------------------------------------------------------------- + * Keywords + *---------------------------------------------------------------------- */ + struct key + { + char *name; + int keycount; + }; + struct const_key + { + const char *name; + int keycount; +}; + +/*---------------------------------------------------------------------- + * Elements + *---------------------------------------------------------------------- */ +struct element +{ + const char *name; /* element name */ + /* int in; */ + struct master *master; + struct master *primary; + LDBLE gfw; +}; +/*---------------------------------------------------------------------- + * Element List + *---------------------------------------------------------------------- */ +struct elt_list +{ /* list of name and number of elements in an equation */ + struct element *elt; /* pointer to element structure */ + LDBLE coef; /* number of element e's in eqn */ +}; +/*---------------------------------------------------------------------- + * Reaction + *---------------------------------------------------------------------- */ +struct reaction +{ + LDBLE logk[MAX_LOG_K_INDICES]; + LDBLE dz[3]; + struct rxn_token *token; +}; +struct rxn_token +{ + struct species *s; + LDBLE coef; + const char *name; +}; +class cxxChemRxn +{ +public: + cxxChemRxn(void) + { + for (size_t i = 0; i < MAX_LOG_K_INDICES; i++) + { + logk[i] = 0.0; + } + for (size_t i = 0; i < 3; i++) + { + dz[i] =0.0; + } + } + cxxChemRxn(struct reaction *rxn) + { + for (size_t i = 0; i < MAX_LOG_K_INDICES; i++) + { + logk[i] = rxn->logk[i]; + } + for (size_t i = 0; i < 3; i++) + { + dz[i] = rxn->dz[i]; + } + struct rxn_token *next_token; + next_token = rxn->token; + this->tokens.push_back(*next_token++); + while (next_token->s != NULL || next_token->name != NULL) + { + this->tokens.push_back(*next_token++); + } + } + ~cxxChemRxn(void) {} + LDBLE *Get_logk(void) {return this->logk;} + void Set_logk(LDBLE *d) + { + for (size_t i = 0; i < MAX_LOG_K_INDICES; i++) + { + logk[i] = d[i]; + } + + } + LDBLE *Get_dz(void) {return this->dz;} + void Set_dz(LDBLE *d) + { + for (size_t i = 0; i < 3; i++) + { + dz[i] = d[i]; + } + } + std::vector &Get_tokens(void) {return this->tokens;} + void Set_tokens(const std::vector &t) {this->tokens = t;} + +protected: + LDBLE logk[MAX_LOG_K_INDICES]; + LDBLE dz[3]; + std::vector tokens; +}; +/*---------------------------------------------------------------------- + * Species + *---------------------------------------------------------------------- */ +struct species +{ /* all data pertinent to an aqueous species */ + const char *name; /* name of species */ + const char *mole_balance; /* formula for mole balance */ + int in; /* species used in model if TRUE */ + int number; + struct master *primary; /* points to master species list, NULL if not primary master */ + struct master *secondary; /* points to master species list, NULL if not secondary master */ + LDBLE gfw; /* gram formula wt of species */ + LDBLE z; /* charge of species */ + LDBLE dw; /* tracer diffusion coefficient in water at 25oC, m2/s */ + LDBLE dw_t; /* correct Dw for temperature: Dw(TK) = Dw(298.15) * exp(dw_t / TK - dw_t / 298.15) */ + LDBLE dw_a; /* parms for calc'ng SC = SC0 * exp(-dw_a * z * mu^0.5 / (1 + DH_B * dw_a2 * mu^0.5)) */ + LDBLE dw_a2; /* */ + LDBLE dw_a_visc; /* viscosity correction of SC */ + LDBLE dw_t_SC; /* contribution to SC, for calc'ng transport number with BASIC */ + LDBLE dw_corr; /* dw corrected for TK and mu */ + LDBLE erm_ddl; /* enrichment factor in DDL */ + LDBLE equiv; /* equivalents in exchange species */ + LDBLE alk; /* alkalinity of species, used for cec in exchange */ + LDBLE carbon; /* stoichiometric coefficient of carbon in species */ + LDBLE co2; /* stoichiometric coefficient of C(4) in species */ + LDBLE h; /* stoichiometric coefficient of H in species */ + LDBLE o; /* stoichiometric coefficient of O in species */ + LDBLE dha, dhb, a_f; /* WATEQ Debye Huckel a and b-dot; active_fraction coef for exchange species */ + LDBLE lk; /* log10 k at working temperature */ + LDBLE logk[MAX_LOG_K_INDICES]; /* log kt0, delh, 6 coefficients analytical expression + volume terms */ + LDBLE Jones_Dole[10]; /* 7 coefficients analytical expression for B, D, anion terms and pressure in Jones_Dole viscosity eqn */ +/* VP: Density Start */ + LDBLE millero[7]; /* regression coefficients to calculate temperature dependent phi_0 and b_v of Millero density model */ + /* VP: Density End */ + DELTA_H_UNIT original_units; /* enum with original delta H units */ + int count_add_logk; + struct name_coef *add_logk; + LDBLE lg; /* log10 activity coefficient, gamma */ + LDBLE lg_pitzer; /* log10 activity coefficient, from pitzer calculation */ + LDBLE lm; /* log10 molality */ + LDBLE la; /* log10 activity */ + LDBLE dg; /* gamma term for jacobian */ + LDBLE dg_total_g; + LDBLE moles; /* moles in solution; moles/mass_water = molality */ + int type; /* flag indicating presence in model and types of equations */ + int gflag; /* flag for preferred activity coef eqn */ + int exch_gflag; /* flag for preferred activity coef eqn */ + struct elt_list *next_elt; /* pointer to next element */ + struct elt_list *next_secondary; + struct elt_list *next_sys_total; + int check_equation; /* switch to check equation for charge and element balance */ + struct reaction *rxn; /* pointer to data base reaction */ + struct reaction *rxn_s; /* pointer to reaction converted to secondary and primary + master species */ + struct reaction *rxn_x; /* reaction to be used in model */ + LDBLE tot_g_moles; /* (1 + sum(g)) * moles */ + LDBLE tot_dh2o_moles; /* sum(moles*g*Ws/Waq) */ + LDBLE cd_music[5]; + LDBLE dz[3]; + DELTA_V_UNIT original_deltav_units; +}; +struct logk +{ /* Named log K's */ + const char *name; /* name of species */ + LDBLE lk; /* log10 k at working temperature */ + LDBLE log_k[MAX_LOG_K_INDICES]; /* log kt0, delh, 6 coefficients analalytical expression */ + DELTA_H_UNIT original_units; /* enum with original delta H units */ + int count_add_logk; + int done; + struct name_coef *add_logk; + LDBLE log_k_original[MAX_LOG_K_INDICES]; /* log kt0, delh, 5 coefficients analalytical expression */ + DELTA_V_UNIT original_deltav_units; +}; + +/*---------------------------------------------------------------------- + * Phases + *---------------------------------------------------------------------- */ +struct phase +{ /* all data pertinent to a pure solid phase */ + const char *name; /* name of species */ + const char *formula; /* chemical formula */ + int in; /* species used in model if TRUE */ + LDBLE lk; /* log10 k at working temperature */ + LDBLE logk[MAX_LOG_K_INDICES]; /* log kt0, delh, 6 coefficients analalytical expression */ + DELTA_H_UNIT original_units; /* enum with original delta H units */ + DELTA_V_UNIT original_deltav_units; + int count_add_logk; + struct name_coef *add_logk; + LDBLE moles_x; + LDBLE delta_max; + LDBLE p_soln_x; + LDBLE fraction_x; + LDBLE log10_lambda, log10_fraction_x; + LDBLE dn, dnb, dnc; + LDBLE gn, gntot; + LDBLE gn_n, gntot_n; + LDBLE t_c, p_c, omega; /* gas: critical TK, critical P(atm), Pitzer acentric coeff */ + LDBLE pr_a, pr_b, pr_alpha; /* Peng-Robinson parm's */ + LDBLE pr_tk, pr_p; /* Temperature (K), Pressure (atm) */ + LDBLE pr_phi; /* fugacity coefficient (-) */ + LDBLE pr_aa_sum2; /* for calculating multicomponent phi */ + LDBLE delta_v[9]; /* delta_v[0] = [1] + [2]*T + [3]/T + [4]*log10(T) + [5]/T^2 + [6]*T^2 + [7]*P */ + LDBLE pr_si_f; /* si adapter: log10(phi) - delta_v[0] * (P - 1) /RT */ + bool pr_in; /* Peng-Robinson in the calc's, or not */ + + int type; /* flag indicating presence in model and types of equations */ + struct elt_list *next_elt; /* pointer to list of elements in phase */ + struct elt_list *next_sys_total; + int check_equation; /* switch to check equation for charge and element balance */ + struct reaction *rxn; /* pointer to data base reaction */ + struct reaction *rxn_s; /* pointer to reaction converted to secondary and primary + master species */ + struct reaction *rxn_x; /* reaction to be used in model */ + int replaced; /* equation contains solids or gases */ + int in_system; +}; +/*---------------------------------------------------------------------- + * Master species + *---------------------------------------------------------------------- */ + struct master + { /* list of name and number of elements in an equation */ + int in; /* TRUE if in model, FALSE if out, REWRITE if other mb eq */ + int number; /* sequence number in list of masters */ + int last_model; /* saved to determine if model has changed */ + int type; /* AQ or EX */ + int primary; /* TRUE if master species is primary */ + LDBLE coef; /* coefficient of element in master species */ + LDBLE total; /* total concentration for element or valence state */ + LDBLE isotope_ratio; + LDBLE isotope_ratio_uncertainty; + int isotope; + LDBLE total_primary; + /* LDBLE la; */ /* initial guess of master species log activity */ + struct element *elt; /* element structure */ + LDBLE alk; /* alkalinity of species */ + LDBLE gfw; /* default gfw for species */ + const char *gfw_formula; /* formula from which to calcuate gfw */ + struct unknown *unknown; /* pointer to unknown structure */ + struct species *s; /* pointer to species structure */ + struct reaction *rxn_primary; /* reaction writes master species in terms of primary + master species */ + struct reaction *rxn_secondary; /* reaction writes master species in terms of secondary + master species */ + const char * pe_rxn; + int minor_isotope; +}; +/*---------------------------------------------------------------------- + * Unknowns + *---------------------------------------------------------------------- */ +struct unknown +{ + int type; + LDBLE moles; + LDBLE ln_moles; + LDBLE f; + LDBLE sum; + LDBLE delta; + LDBLE la; + int number; + const char *description; + struct master **master; + struct phase *phase; + LDBLE si; + int n_gas_phase_user; + struct species *s; + const char * exch_comp; + const char *pp_assemblage_comp_name; + void *pp_assemblage_comp_ptr; + const char * ss_name; + void *ss_ptr; + const char * ss_comp_name; + void *ss_comp_ptr; + int ss_comp_number; + int ss_in; + const char *surface_comp; + const char *surface_charge; + LDBLE related_moles; + struct unknown *potential_unknown, *potential_unknown1, + *potential_unknown2; + int count_comp_unknowns; + struct unknown **comp_unknowns; /* list for CD_MUSIC of comps that contribute to 0 plane mass-balance term */ + struct unknown *phase_unknown; + LDBLE mass_water; + int dissolve_only; + LDBLE inert_moles; + LDBLE V_m; + LDBLE pressure; + int mb_number; + int iteration; +}; + +/*---------------------------------------------------------------------- + * Reaction work space + *---------------------------------------------------------------------- */ +struct reaction_temp +{ + LDBLE logk[MAX_LOG_K_INDICES]; + LDBLE dz[3]; + struct rxn_token_temp *token; +}; +struct rxn_token_temp +{ /* data for equations, aq. species or minerals */ + const char *name; /* pointer to a species name (formula) */ + LDBLE z; /* charge on species */ + struct species *s; + struct unknown *unknown; + LDBLE coef; /* coefficient of species name */ +}; +struct unknown_list +{ + struct unknown *unknown; + LDBLE *source; + LDBLE *gamma_source; + /* int row; */ + /* int col; */ + LDBLE coef; +}; +/* ---------------------------------------------------------------------- + * Print + * ---------------------------------------------------------------------- */ +struct prints +{ + int all; + int initial_solutions; + int initial_exchangers; + int reactions; + int gas_phase; + int ss_assemblage; + int pp_assemblage; + int surface; + int exchange; + int kinetics; + int totals; + int eh; + int species; + int saturation_indices; + int irrev; + int mix; + int reaction; + int use; + int logfile; + int punch; + int status; + int inverse; + int dump; + int user_print; + int headings; + int user_graph; + int echo_input; + int warnings; + int initial_isotopes; + int isotope_ratios; + int isotope_alphas; + int hdf; + int alkalinity; +}; +/* ---------------------------------------------------------------------- + * RATES + * ---------------------------------------------------------------------- */ +struct rate +{ + const char *name; + char *commands; + int new_def; + void *linebase; + void *varbase; + void *loopbase; +}; +/* ---------------------------------------------------------------------- + * GLOBAL DECLARATIONS + * ---------------------------------------------------------------------- */ +struct spread_row +{ + int count; + int empty, string, number; + char **char_vector; + LDBLE *d_vector; + int *type_vector; +}; +struct defaults +{ + LDBLE temp; + LDBLE density; + bool calc_density; + const char *units; + const char *redox; + LDBLE ph; + LDBLE pe; + LDBLE water; + int count_iso; + struct iso *iso; + LDBLE pressure; /* pressure in atm */ +}; +struct spread_sheet +{ + struct spread_row *heading; + struct spread_row *units; + int count_rows; + struct spread_row **rows; + struct defaults defaults; +}; +/* ---------------------------------------------------------------------- + * ISOTOPES + * ---------------------------------------------------------------------- */ +struct master_isotope +{ + const char *name; + struct master *master; + struct element *elt; + const char *units; + LDBLE standard; + LDBLE ratio; + LDBLE moles; + int total_is_major; + int minor_isotope; +}; +struct calculate_value +{ + const char *name; + LDBLE value; + char *commands; + int new_def; + int calculated; + void *linebase; + void *varbase; + void *loopbase; +}; +struct isotope_ratio +{ + const char *name; + const char *isotope_name; + LDBLE ratio; + LDBLE converted_ratio; +}; +struct isotope_alpha +{ + const char *name; + const char *named_logk; + LDBLE value; +}; +struct system_species +{ + char *name; + char *type; + LDBLE moles; +}; + +/* tally.c ------------------------------- */ +struct tally_buffer +{ + const char *name; + struct master *master; + LDBLE moles; + LDBLE gfw; +}; +struct tally +{ + const char *name; + enum entity_type type; + const char *add_formula; + LDBLE moles; + struct elt_list *formula; + /* + * first total is initial + * second total is final + * third total is difference (final - initial) + */ + struct tally_buffer *total[3]; +}; + +/* transport.c ------------------------------- */ +struct spec +{ + const char *name; /* name of species */ + const char *aq_name; /* name of aqueous species in EX species */ + int type; /* type: AQ or EX */ + LDBLE a; /* activity */ + LDBLE lm; /* log(concentration) */ + LDBLE lg; /* log(gamma) */ + LDBLE c; /* concentration for AQ, equivalent fraction for EX */ + LDBLE z; /* charge number */ + LDBLE Dwt; /* temperature corrected free water diffusion coefficient, m2/s */ + LDBLE dw_t; /* temperature factor for Dw */ + LDBLE erm_ddl; /* enrichment factor in ddl */ +}; +struct sol_D +{ + int count_spec; /* number of aqueous + exchange species */ + int count_exch_spec; /* number of exchange species */ + LDBLE exch_total, x_max, tk_x; /* total moles of X-, max X- in transport step in sol_D[1], tk */ + LDBLE viscos_f; /* (tk_x * viscos_0_25) / (298 * viscos) */ + struct spec *spec; + int spec_size; +}; +struct J_ij +{ + const char *name; + LDBLE tot1, tot2; /* species change in cells i and j */ +}; +struct M_S +{ + const char *name; + LDBLE tot1, tot2; +}; +// Pitzer definitions +typedef enum +{ TYPE_B0, TYPE_B1, TYPE_B2, TYPE_C0, TYPE_THETA, TYPE_LAMDA, TYPE_ZETA, + TYPE_PSI, TYPE_ETHETA, TYPE_ALPHAS, TYPE_MU, TYPE_ETA, TYPE_Other, + TYPE_SIT_EPSILON, TYPE_SIT_EPSILON_MU, TYPE_APHI +} pitz_param_type; + +struct pitz_param +{ + const char *species[3]; + int ispec[3]; + pitz_param_type type; + LDBLE p; + union + { + LDBLE b0; + LDBLE b1; + LDBLE b2; + LDBLE c0; + LDBLE theta; + LDBLE lamda; + LDBLE zeta; + LDBLE psi; + LDBLE alphas; + LDBLE mu; + LDBLE eta; + LDBLE eps; + LDBLE eps1; + LDBLE aphi; + } U; + LDBLE a[6]; + LDBLE alpha; + LDBLE os_coef; + LDBLE ln_coef[3]; + struct theta_param *thetas; +}; + +struct theta_param +{ + LDBLE zj; + LDBLE zk; + LDBLE etheta; + LDBLE ethetap; +}; + +struct const_iso +{ + const char *name; + LDBLE value; + LDBLE uncertainty; +}; + +#endif /* _INC_GLOBAL_STRUCTURES_H */ + diff --git a/phreeqcpp/input.cpp b/phreeqcpp/input.cpp new file mode 100644 index 00000000..10d120c8 --- /dev/null +++ b/phreeqcpp/input.cpp @@ -0,0 +1,122 @@ +#include +#include "Utils.h" +#include "Phreeqc.h" +#include +#include +#include "phqalloc.h" + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +set_reading_database(int reading_database) +/* ---------------------------------------------------------------------- */ +{ + reading_db = reading_database; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +reading_database(void) +/* ---------------------------------------------------------------------- */ +{ + return reading_db; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_line(const char *string, int allow_empty, int allow_eof, + int allow_keyword, int print) +/* ---------------------------------------------------------------------- */ +{ + if (reading_database()) + print = FALSE; + return check_line_impl(string, allow_empty, allow_eof, allow_keyword, + print); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_line_impl(const char *string, int allow_empty, int allow_eof, + int allow_keyword, int print) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function gets a new line and checks for empty, eof, and keywords. + * + * Arguments: + * string Input, character string used in printing error message + * allow_empty Input, True or false, if a blank line is accepable + * if false, another line is read + * allow_eof Input, True or false, if EOF is acceptable + * allow_keyword Input, True or false, if a keyword is acceptable + * + * Returns: + * EMPTY if empty line read and allow_empty == true + * KEYWORD if line begins with keyword + * EOF if eof and allow_eof == true + * OK otherwise + * OPTION if line begins with -[alpha] + * + * Terminates if EOF and allow_eof == false. + */ + int i; + + +/* Get line */ + do + { + i = get_line(); + if ((print == TRUE && i != EOF) || i == KEYWORD) + { + echo_msg(sformatf( "\t%s\n", line_save)); + } + } + while (i == EMPTY && allow_empty == FALSE); +/* Check eof */ + if (i == EOF && allow_eof == FALSE) + { + error_string = sformatf( + "Unexpected eof while reading %s\nExecution terminated.\n", + string); + error_msg(error_string, STOP); + } +/* Check keyword */ + if (i == KEYWORD && allow_keyword == FALSE) + { + error_string = sformatf( + "Expected data for %s, but got a keyword ending data block.", + string); + error_msg(error_string, CONTINUE); + input_error++; + } + check_line_return = i; + return (i); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_line(void) +/* ---------------------------------------------------------------------- */ +{ + PHRQ_io::LINE_TYPE j = phrq_io->get_line(); + // check_key sets next_keyword + next_keyword = phrq_io->Get_m_next_keyword(); + + // copy parser line to line and line_save + // make sure there is enough space + size_t l1 = strlen(phrq_io->Get_m_line().c_str()) + 1; + size_t l2 = strlen(phrq_io->Get_m_line_save().c_str()) + 1; + size_t l = (l1 > l2) ? l1 : l2; + if (l >= (size_t) max_line) + { + max_line = (int) l * 2; + line_save = (char *) PHRQ_realloc(line_save, + (size_t) max_line * sizeof(char)); + if (line_save == NULL) + malloc_error(); + line = (char *) PHRQ_realloc(line, (size_t) max_line * sizeof(char)); + if (line == NULL) + malloc_error(); + } + strcpy(line, phrq_io->Get_m_line().c_str()); + strcpy(line_save, phrq_io->Get_m_line_save().c_str()); + return j; +} diff --git a/phreeqcpp/integrate.cpp b/phreeqcpp/integrate.cpp new file mode 100644 index 00000000..fa3fb255 --- /dev/null +++ b/phreeqcpp/integrate.cpp @@ -0,0 +1,1088 @@ +#include "Phreeqc.h" +#include "phqalloc.h" +#include "Utils.h" +#include "Solution.h" + +#define MAX_QUAD 20 +#define K_POLY 5 + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_all_g(void) +/* ---------------------------------------------------------------------- */ +{ + int converge, converge1; + LDBLE new_g, xd1; + LDBLE epsilon; + + if (use.Get_surface_ptr() == NULL) + return (OK); +/* + * calculate g for each surface + */ + epsilon = convergence_tolerance; + if (convergence_tolerance >= 1e-8) + { + G_TOL = 1e-9; + } + else + { + G_TOL = 1e-10; + } + + converge = TRUE; + + for (int j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE_CB) + continue; + if (debug_diffuse_layer == TRUE) + output_msg(sformatf( "Calc_all_g, X[%d]\n", j)); + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + std::map temp_g_map; + cxxSurfDL temp_g; + charge_ptr->Get_g_map()[0] = temp_g; + temp_g_map[0] = temp_g; + xd_global = exp(-2 * x[j]->master[0]->s->la * LOG_10); + /* alpha = 0.02935 @ 25; (ee0RT/2)**1/2, (L/mol)**1/2 C / m**2 */ + /* 1000 J/kJ and 1000 L/m**3 */ + //alpha_global = sqrt(EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000.0) * 1000.0 * + // tk_x * 0.5); + alpha_global = sqrt(eps_r * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000.0) * 1000.0 * + tk_x * 0.5); +/* + * calculate g for given surface for each species + */ + for (int i = 0; i < count_s_x; i++) + { + if (s_x[i]->type > HPLUS) + continue; + if (temp_g_map.find(s_x[i]->z) != temp_g_map.end()) + continue; + z_global = s_x[i]->z; + if (charge_ptr->Get_grams() > 0.0) + { + + if ((use.Get_surface_ptr()->Get_only_counter_ions() == false) || + (((x[j]->master[0]->s->la > 0) && (z_global < 0)) + || ((x[j]->master[0]->s->la < 0) && (z_global > 0)))) + { + if (xd_global > 0.1) + { + new_g = qromb_midpnt(charge_ptr, 1.0, xd_global); + } + else if (xd_global > 0.01) + { + new_g = qromb_midpnt(charge_ptr, 1.0, 0.1); + new_g += qromb_midpnt(charge_ptr, 0.1, xd_global); + } + else if (xd_global > 0.001) + { + new_g = qromb_midpnt(charge_ptr, 1.0, 0.1); + new_g += qromb_midpnt(charge_ptr, 0.1, 0.01); + new_g += qromb_midpnt(charge_ptr, 0.01, xd_global); + } + else if (xd_global > 0.0001) + { + new_g = qromb_midpnt(charge_ptr, 1.0, 0.1); + new_g += qromb_midpnt(charge_ptr, 0.1, 0.01); + new_g += qromb_midpnt(charge_ptr, 0.01, .001); + new_g += qromb_midpnt(charge_ptr, 0.001, xd_global); + } + else if (xd_global > 0.00001) + { + new_g = qromb_midpnt(charge_ptr, 1.0, 0.1); + new_g += qromb_midpnt(charge_ptr, 0.1, 0.01); + new_g += qromb_midpnt(charge_ptr, 0.01, .001); + new_g += qromb_midpnt(charge_ptr, 0.001, .0001); + new_g += qromb_midpnt(charge_ptr, 0.0001, xd_global); + } + else if (xd_global > 0.000001) + { + new_g = qromb_midpnt(charge_ptr, 1.0, 0.1); + new_g += qromb_midpnt(charge_ptr, 0.1, 0.01); + new_g += qromb_midpnt(charge_ptr, 0.01, .001); + new_g += qromb_midpnt(charge_ptr, 0.001, .0001); + new_g += qromb_midpnt(charge_ptr, 0.0001, .00001); + new_g += qromb_midpnt(charge_ptr, 0.00001, xd_global); + } + else if (xd_global > 0.0000001) + { + new_g = qromb_midpnt(charge_ptr, 1.0, 0.1); + new_g += qromb_midpnt(charge_ptr, 0.1, 0.01); + new_g += qromb_midpnt(charge_ptr, 0.01, .001); + new_g += qromb_midpnt(charge_ptr, 0.001, .0001); + new_g += qromb_midpnt(charge_ptr, 0.0001, .00001); + new_g += qromb_midpnt(charge_ptr, 0.00001, .000001); + new_g += qromb_midpnt(charge_ptr, 0.000001, xd_global); + } + else if (xd_global > 0.00000001) + { + new_g = qromb_midpnt(charge_ptr, 1.0, 0.1); + new_g += qromb_midpnt(charge_ptr, 0.1, 0.01); + new_g += qromb_midpnt(charge_ptr, 0.01, .001); + new_g += qromb_midpnt(charge_ptr, 0.001, .0001); + new_g += qromb_midpnt(charge_ptr, 0.0001, .00001); + new_g += qromb_midpnt(charge_ptr, 0.00001, .000001); + new_g += qromb_midpnt(charge_ptr, 0.000001, .0000001); + new_g += qromb_midpnt(charge_ptr, 0.0000001, xd_global); + } + else + { + new_g = qromb_midpnt(charge_ptr, 1.0, 0.1); + new_g += qromb_midpnt(charge_ptr, 0.1, 0.01); + new_g += qromb_midpnt(charge_ptr, 0.01, .001); + new_g += qromb_midpnt(charge_ptr, 0.001, .0001); + new_g += qromb_midpnt(charge_ptr, 0.0001, .00001); + new_g += qromb_midpnt(charge_ptr, 0.00001, .000001); + new_g += qromb_midpnt(charge_ptr, 0.000001, .0000001); + new_g += qromb_midpnt(charge_ptr, 0.0000001, .00000001); + new_g += qromb_midpnt(charge_ptr, 0.00000001, xd_global); + } + } + else + { + new_g = 0; + } + } + else + { + new_g = 0.0; + } + if ((use.Get_surface_ptr()->Get_only_counter_ions()) && new_g < 0) + new_g = 0; + converge1 = TRUE; + if (fabs(new_g) >= 1.) + { + if (fabs((new_g - charge_ptr->Get_g_map()[z_global].Get_g()) / new_g) > epsilon) + { + converge1 = FALSE; + } + } + else + { + if (fabs(new_g - charge_ptr->Get_g_map()[z_global].Get_g()) > epsilon) + { + converge1 = FALSE; + } + } + if (converge1 == FALSE) + { + converge = FALSE; + if (debug_diffuse_layer == TRUE) + { + output_msg(sformatf( + "\t%12f\t%12.4e\t%12.4e\t%12.4e\n", + (double) z_global, + (double) charge_ptr->Get_g_map()[z_global].Get_g(), + (double) new_g, + (double) (new_g - charge_ptr->Get_g_map()[z_global].Get_g()))); + } + } + charge_ptr->Get_g_map()[z_global].Set_g(new_g); + if (new_g == 0) + { + charge_ptr->Get_g_map()[z_global].Set_dg(0.); + } + else + { + if (charge_ptr->Get_grams() > 0.0) + { + LDBLE dg = charge_ptr->Get_grams() * + charge_ptr->Get_specific_area() * alpha_global * + g_function(xd_global) / F_C_MOL; + dg *= + -2. / (exp(x[j]->master[0]->s->la * LOG_10) * + exp(x[j]->master[0]->s->la * LOG_10)); + if ((xd_global - 1) < 0.0) + { + dg *= -1.0; + } + + if (fabs(dg) < 1e-8) + { + xd1 = exp(-2 * 1e-3 * LOG_10); + new_g = qromb_midpnt(charge_ptr, 1.0, xd1); + dg = new_g / .001; + } + charge_ptr->Get_g_map()[z_global].Set_dg(dg); + } + else + { + charge_ptr->Get_g_map()[z_global].Set_dg(0.0); + } + } + temp_g_map[z_global] = charge_ptr->Get_g_map()[z_global]; + } + if (debug_diffuse_layer == TRUE) + { + output_msg(sformatf("\nSurface component %d: charge,\tg,\tdg/dlny,\txd\n", + (int) charge_ptr->Get_g_map().size())); + std::map::iterator it; + for (it = charge_ptr->Get_g_map().begin(); it != charge_ptr->Get_g_map().end(); it++) + { + output_msg(sformatf( + "\t%12f\t%12.4e\t%12.4e\t%12.4e\n", + (double) it->first, + (double) it->second.Get_g(), + (double) it->second.Get_dg(), + (double) xd_global)); + } + } + } + return (converge); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +g_function(LDBLE x_value) +/* ---------------------------------------------------------------------- */ +{ + LDBLE sum, return_value, sum1; + int i; + LDBLE ln_x_value; + + if (equal(x_value, 1.0, G_TOL * 100) == TRUE) + return (0.0); + sum = 0.0; + ln_x_value = log(x_value); + + cxxSurfaceCharge *charge_ptr = &(use.Get_surface_ptr()->Get_surface_charges()[0]); + std::map::iterator it = charge_ptr->Get_g_map().begin(); + for ( ; it != charge_ptr->Get_g_map().end(); it++) + { + it->second.Set_psi_to_z(exp(ln_x_value * it->first) - 1.0); + } + for (i = 0; i < count_s_x; i++) + { + if (s_x[i]->type < H2O && s_x[i]->z != 0.0) + { + sum += s_x[i]->moles * charge_ptr->Get_g_map()[s_x[i]->z].Get_psi_to_z(); + } + } + if (sum < 0.0) + { + sum = 0.0; + sum1 = 0.0; + output_msg(sformatf( + "Species\tmoles\tX**z-1\tsum\tsum charge\n")); + for (i = 0; i < count_s_x; i++) + { + if (s_x[i]->type < H2O && s_x[i]->z != 0.0) + { + sum += s_x[i]->moles * (pow(x_value, s_x[i]->z) - 1.0); + sum1 += s_x[i]->moles * s_x[i]->z; + output_msg(sformatf( "%s\t%e\t%e\t%e\t%e\n", + s_x[i]->name, (double) s_x[i]->moles, + (double) (pow((LDBLE) x_value, (LDBLE) s_x[i]->z) - + 1.0), (double) sum, (double) sum1)); + } + } + error_string = sformatf( "Negative sum in g_function, %e\t%e.", + (double) sum, (double) x_value); + error_msg(error_string, CONTINUE); + error_string = sformatf( + "Solutions must be charge balanced, charge imbalance is %e\n", + (double) sum1); + error_msg(error_string, STOP); + } + + return_value = + (exp(ln_x_value * z_global) - + 1) / sqrt((x_value * x_value * mass_water_aq_x * sum)); + return (return_value); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +polint(LDBLE * xa, LDBLE * ya, int n, LDBLE xv, LDBLE * yv, LDBLE * dy) +/* ---------------------------------------------------------------------- */ +{ + int i, m, ns; + LDBLE den, dif, dift, ho, hp, w; + LDBLE *c, *d; + + ns = 1; + dif = fabs(xv - xa[1]); +/* + * Malloc work space + */ + c = (LDBLE *) PHRQ_malloc((size_t) (n + 1) * sizeof(LDBLE)); + if (c == NULL) + malloc_error(); + d = (LDBLE *) PHRQ_malloc((size_t) (n + 1) * sizeof(LDBLE)); + if (d == NULL) + malloc_error(); + + + + for (i = 1; i <= n; i++) + { + dift = fabs(xv - xa[i]); + if (dift < dif) + { + ns = i; + dif = dift; + } + c[i] = ya[i]; + d[i] = ya[i]; + } + + *yv = ya[ns--]; + for (m = 1; m < n; m++) + { + for (i = 1; i <= n - m; i++) + { + ho = xa[i] - xv; + hp = xa[i + m] - xv; + w = c[i + 1] - d[i]; + if ((den = ho - hp) == 0.0) + { + error_msg("In subroutine polint.", STOP); + } + den = w / den; + d[i] = hp * den; + c[i] = ho * den; + } + if (2 * ns < (n - m)) + { + *dy = c[ns + 1]; + } + else + { + *dy = d[ns--]; + } + *yv += *dy; + +/* *yv += (*dy = (2 * ns < (n-m) ? c[ns+1] : d[ns--])); */ + } + c = (LDBLE *) free_check_null(c); + d = (LDBLE *) free_check_null(d); + return; +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +midpnt(LDBLE x1, LDBLE x2, int n) +/* ---------------------------------------------------------------------- */ +{ + LDBLE xv, tnm, sum, del, ddel; + int it, j; + + if (n == 1) + { + midpoint_sv = (x2 - x1) * g_function(0.5 * (x1 + x2)); + return (midpoint_sv); + } + else + { + for (it = 1, j = 1; j < n - 1; j++) + it *= 3; + tnm = (LDBLE) it; + del = (x2 - x1) / (3 * tnm); + ddel = del + del; + xv = x1 + 0.5 * del; + sum = 0.0; + for (j = 1; j <= it; j++) + { +#if defined(PHREEQCI_GUI) + PhreeqcIWait(this); +#endif + sum += g_function(xv); + xv += ddel; + sum += g_function(xv); + xv += del; + } + midpoint_sv = (midpoint_sv + (x2 - x1) * sum / tnm) / 3.0; + return midpoint_sv; + } +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +qromb_midpnt(cxxSurfaceCharge *charge_ptr, LDBLE x1, LDBLE x2) +/* ---------------------------------------------------------------------- */ +{ + LDBLE ss, dss; + LDBLE sv[MAX_QUAD + 2], h[MAX_QUAD + 2]; + int j; + + h[0] = 1.0; + sv[0] = midpnt(x1, x2, 1); + for (j = 1; j < MAX_QUAD; j++) + { + sv[j] = midpnt(x1, x2, j + 1); + h[j] = h[j - 1] / 9.0; + + if (fabs(sv[j] - sv[j - 1]) <= G_TOL * fabs(sv[j])) + { + sv[j] *= charge_ptr->Get_grams() * charge_ptr->Get_specific_area() * alpha_global / F_C_MOL; /* (ee0RT/2)**1/2, (L/mol)**1/2 C / m**2 */ + if ((x2 - 1) < 0.0) + sv[j] *= -1.0; + if (debug_diffuse_layer == TRUE) + { + output_msg(sformatf( + "Iterations in qromb_midpnt: %d\n", j)); + } + return (sv[j]); + } + + if (j >= K_POLY - 1) + { + polint(&h[j - K_POLY], &sv[j - K_POLY], K_POLY, 0.0, &ss, &dss); + if (fabs(dss) <= G_TOL * fabs(ss) || fabs(dss) < G_TOL) + { + ss *= charge_ptr->Get_grams() * charge_ptr->Get_specific_area() * alpha_global / F_C_MOL; /* (ee0RT/2)**1/2, (L/mol)**1/2 C / m**2 */ + if ((x2 - 1) < 0.0) + ss *= -1.0; + if (debug_diffuse_layer == TRUE) + { + output_msg(sformatf( + "Iterations in qromb_midpnt: %d\n", j)); + } + return (ss); + } + } + + } + error_string = sformatf( + "\nToo many iterations integrating diffuse layer.\n"); + error_msg(error_string, STOP); + return (-999.9); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_init_g(void) +/* ---------------------------------------------------------------------- */ +{ + if (use.Get_surface_ptr() == NULL) + return (OK); +/* + * calculate g for each surface + */ + for (int j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + xd_global = exp(-2 * x[j]->master[0]->s->la * LOG_10); + /* alpha = 0.02935 @ 25; (ee0RT/2)**1/2, (L/mol)**1/2 C / m**2 */ + /* second 1000 is liters/m**3 */ + //alpha_global = sqrt(EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000.0) * + // 1000.0 * tk_x * 0.5); + alpha_global = sqrt(eps_r * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000.0) * + 1000.0 * tk_x * 0.5); + + if (charge_ptr->Get_g_map().size() == 0) + { + cxxSurfDL temp_g; + charge_ptr->Get_g_map()[0.0] = temp_g; + } +/* + * calculate g for given surface for each species + */ + for (int i = 0; i < count_s_x; i++) + { + if (s_x[i]->type > HPLUS) + continue; + + if (charge_ptr->Get_g_map().find(s_x[i]->z) == charge_ptr->Get_g_map().end()) + { + cxxSurfDL temp_g; + /* save g for charge */ + if (charge_ptr->Get_grams() > 0.0) + { + temp_g.Set_g(2 * alpha_global * sqrt(mu_x) * (pow(xd_global, s_x[i]->z / 2.0) - 1) * + charge_ptr->Get_grams() * + charge_ptr->Get_specific_area() / F_C_MOL); + temp_g.Set_dg(-s_x[i]->z); + if (use.Get_surface_ptr()->Get_only_counter_ions() && + temp_g.Get_g() < 0) + { + temp_g.Set_g(0); + temp_g.Set_dg(0); + } + } + else + { + temp_g.Set_g(0); + temp_g.Set_dg(-s_x[i]->z); + } + charge_ptr->Get_g_map()[s_x[i]->z] = temp_g; + } + + { + int is = s_x[i]->number; + assert (is < (int) s_diff_layer.size()); + // species found in diff_layer + s_diff_layer[is][charge_ptr->Get_name()].Set_g_moles(0); + s_diff_layer[is][charge_ptr->Get_name()].Set_dg_g_moles(0); + } + } + if (debug_diffuse_layer == TRUE) + { + output_msg(sformatf( + "\nSurface component %d: charge,\tg,\tdg\n", + (int) charge_ptr->Get_g_map().size())); + std::map::iterator it; + for (it = charge_ptr->Get_g_map().begin(); it != charge_ptr->Get_g_map().end(); it++) + { + output_msg(sformatf( "\t%12f\t%12.4e\t%12.4e\n", + (double) it->first, + (double) it->second.Get_g(), + (double) it->second.Get_dg())); + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +initial_surface_water(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * In initial surface calculation, need to calculate + * mass of water in diffuse layer. + * diffuse layer water + aqueous solution water = bulk water. + * Ionic strength is fixed, so diffuse-layer water will not change + */ + LDBLE debye_length, b, r, rd, ddl_limit, rd_limit, fraction, sum_surfs, l_s; + LDBLE damp_aq; +/* + * Debye length = 1/k = sqrt[eta*eta_zero*R*T/(2*F**2*mu_x*1000)], Dzombak and Morel, p 36 + * + * 1000 converts kJ to J; 1000 converts Liters to meter**3; debye_length is in meters. + */ + //debye_length = (EPSILON * EPSILON_ZERO * R_KJ_DEG_MOL * 1000.0 * tk_x) + // / (2. * F_C_MOL * F_C_MOL * mu_x * 1000.); + debye_length = (eps_r * EPSILON_ZERO * R_KJ_DEG_MOL * 1000.0 * tk_x) + / (2. * F_C_MOL * F_C_MOL * mu_x * 1000.); + debye_length = sqrt(debye_length); + + /* ddl is at most the fraction ddl_limit of bulk water */ + ddl_limit = use.Get_surface_ptr()->Get_DDL_limit(); + +/* + * Loop through all surface components, calculate each H2O surface (diffuse layer), + * H2O aq, and H2O bulk (diffuse layers plus aqueous). + */ + + if (use.Get_surface_ptr()->Get_debye_lengths() > 0) + { + sum_surfs = 0.0; + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + sum_surfs += + charge_ptr->Get_specific_area() * + charge_ptr->Get_grams(); + } + rd = debye_length * use.Get_surface_ptr()->Get_debye_lengths(); + use.Get_surface_ptr()->Set_thickness(rd); + + if (!sum_surfs) + { + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + charge_ptr->Set_mass_water(0); + } + } + else if (state == INITIAL_SURFACE) + { + /* distribute water over DDL (rd) and free pore (r - rd) */ + /* find r: free pore (m3) = pi * (r - rd)^2 * L, where L = A / (2*pi*r), + A = sum_surfs = sum of the surface areas */ + b = -2 * (rd + use.Get_solution_ptr()->Get_mass_water() / (1000 * sum_surfs)); + r = 0.5 * (-b + sqrt(b * b - 4 * rd * rd)); + /* DDL (m3) = pi * (r^2 - (r - rd)^2) * L */ + rd_limit = (1 - sqrt(1 - ddl_limit)) * r; + /* rd should be smaller than r and the ddl limit */ + if (rd > rd_limit) + { + mass_water_surfaces_x = + use.Get_solution_ptr()->Get_mass_water() * ddl_limit / (1 - + ddl_limit); + r = 0.002 * (mass_water_surfaces_x + + use.Get_solution_ptr()->Get_mass_water()) / sum_surfs; + rd_limit = (1 - sqrt(1 - ddl_limit)) * r; + rd = rd_limit; + use.Get_surface_ptr()->Set_thickness(rd); + } + else + mass_water_surfaces_x = + (r * r / pow(r - rd, 2) - + 1) * use.Get_solution_ptr()->Get_mass_water(); + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + l_s =charge_ptr->Get_specific_area() * + charge_ptr->Get_grams(); + charge_ptr->Set_mass_water(mass_water_surfaces_x * l_s / sum_surfs); + } + } + else + { + r = 0.002 * mass_water_bulk_x / sum_surfs; + rd_limit = (1 - sqrt(1 - ddl_limit)) * r; + if (rd > rd_limit) + { + rd = rd_limit; + use.Get_surface_ptr()->Set_thickness(rd); + fraction = ddl_limit; + } + else + fraction = 1 - pow(r - rd, 2) / (r * r); + damp_aq = 1.0; + if (g_iterations > 10) + damp_aq = 0.2; + else if (g_iterations > 5) + damp_aq = 0.5; + mass_water_surfaces_x = damp_aq * fraction * mass_water_bulk_x + + (1 - damp_aq) * mass_water_surfaces_x; + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + l_s = charge_ptr->Get_specific_area() * + charge_ptr->Get_grams(); + charge_ptr->Set_mass_water(mass_water_surfaces_x * l_s / sum_surfs); + } + } + } + else + { + /* take constant thickness of, default 1e-8 m (100 Angstroms) */ + mass_water_surfaces_x = 0.0; + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + charge_ptr->Set_mass_water(charge_ptr->Get_specific_area() * + charge_ptr->Get_grams() * use.Get_surface_ptr()->Get_thickness() * + 1000); + mass_water_surfaces_x += charge_ptr->Get_mass_water(); + } + } + + if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + mass_water_bulk_x = mass_water_aq_x + mass_water_surfaces_x; + else + { + /* for variable distribution of water over DDL and bulk... */ + if (state > INITIAL_SURFACE) + mass_water_aq_x = mass_water_bulk_x - mass_water_surfaces_x; + else + mass_water_bulk_x = mass_water_aq_x + mass_water_surfaces_x; + } + + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +sum_diffuse_layer(cxxSurfaceCharge *charge_ptr) +/* ---------------------------------------------------------------------- */ +{ + LDBLE mass_water_surface; + LDBLE molality, moles_excess, moles_surface; + + if (use.Get_surface_ptr() == NULL) + return (OK); +/* + * Find position of component in list of components + */ + +/* + * Loop through all surface components, calculate each H2O surface (diffuse layer), + * H2O aq, and H2O bulk (diffuse layers plus aqueous). + */ + count_elts = 0; + paren_count = 0; + mass_water_surface = charge_ptr->Get_mass_water(); + for (int j = 0; j < count_s_x; j++) + { + if (s_x[j]->type > HPLUS) + continue; + molality = under(s_x[j]->lm); + LDBLE g = charge_ptr->Get_g_map()[s_x[j]->z].Get_g(); + if (s_x[j]->erm_ddl != 1) + { + LDBLE ratio_aq = mass_water_surface / mass_water_aq_x; + LDBLE g2 = g / ratio_aq + 1; + g = ratio_aq * (g2 * s_x[j]->erm_ddl - 1); + } + moles_excess = mass_water_aq_x * molality * g; + moles_surface = mass_water_surface * molality + moles_excess; +/* + * Accumulate elements in diffuse layer + */ + add_elt_list(s_x[j]->next_elt, moles_surface); + } + add_elt_list(s_h2o->next_elt, mass_water_surface / gfw_water); + + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_all_donnan(void) +/* ---------------------------------------------------------------------- */ +{ + bool converge; + int cd_m; + LDBLE new_g, f_psi, surf_chrg_eq, psi_avg, f_sinh, A_surf, ratio_aq, ratio_aq_tot; + LDBLE new_g2, f_psi2, surf_chrg_eq2, psi_avg2, dif, var1; + + if (use.Get_surface_ptr() == NULL) + return (OK); + //f_sinh = sqrt(8000.0 * EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000.0) * + // tk_x * mu_x); + f_sinh = sqrt(8000.0 * eps_r * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000.0) * + tk_x * mu_x); +/* + * calculate g for each surface... + */ + converge = TRUE; + for (int j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + + if (debug_diffuse_layer == TRUE) + output_msg(sformatf( "Calc_all_g, X[%d]\n", j)); +/* + * sum eq of each charge number in solution... + */ + std::map::iterator it; + for (it = charge_group_map.begin(); it != charge_group_map.end(); it++) + { + it->second = 0.0; + } + charge_group_map.clear(); + for (int i = 0; i < count_s_x; i++) + { + if (s_x[i]->type > HPLUS) + continue; + charge_group_map[s_x[i]->z] += s_x[i]->z * s_x[i]->moles * s_x[i]->erm_ddl; + } + /* find surface charge from potential... */ + A_surf = charge_ptr->Get_specific_area() * charge_ptr->Get_grams(); + if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + f_psi = x[j + 2]->master[0]->s->la * LOG_10; /* -FPsi/RT */ + f_psi = f_psi / 2; + cd_m = 1; + } else + { + f_psi = x[j]->master[0]->s->la * LOG_10; + cd_m = -1; + } + surf_chrg_eq = A_surf * f_sinh * sinh(f_psi) / F_C_MOL; + if (surf_chrg_eq < -5e3) + { + surf_chrg_eq = -5e3; + var1 = surf_chrg_eq / (A_surf * f_sinh / F_C_MOL); + var1 = (var1 + sqrt(var1 * var1 + 1)); + f_psi = (var1 > 1e-8 ? log(var1) : -18.4); + surf_chrg_eq = A_surf * f_sinh * sinh(f_psi) / F_C_MOL; + x[j]->master[0]->s->la = f_psi / LOG_10; + } + /* also for the derivative... */ + dif = 1e-5; + f_psi2 = f_psi + dif; + surf_chrg_eq2 = A_surf * f_sinh * sinh(f_psi2) / F_C_MOL; + + /* find psi_avg that matches surface charge... */ + psi_avg = calc_psi_avg(charge_ptr, surf_chrg_eq); + psi_avg2 = calc_psi_avg(charge_ptr, surf_chrg_eq2); + + /*output_msg(sformatf( "psi's %e %e %e\n", f_psi, psi_avg, surf_chrg_eq)); */ + + /* fill in g's */ + ratio_aq = charge_ptr->Get_mass_water() / mass_water_aq_x; + ratio_aq_tot = charge_ptr->Get_mass_water() / mass_water_bulk_x; + + for (it = charge_group_map.begin(); it != charge_group_map.end(); it++) + { + LDBLE z = it->first; + if (!ratio_aq) + { + charge_ptr->Get_g_map()[z].Set_g(0); + charge_ptr->Get_g_map()[z].Set_dg(0); + charge_ptr->Get_z_gMCD_map()[z] = 0; + converge = true; + continue; + } + new_g = ratio_aq * (exp(cd_m * z * psi_avg) - 1); + if (use.Get_surface_ptr()->Get_only_counter_ions() && surf_chrg_eq * z > 0) + //((surf_chrg_eq < 0 && z < 0) + // || (surf_chrg_eq > 0 && z > 0))) + new_g = -ratio_aq; + if (new_g <= -ratio_aq) + new_g = -ratio_aq + G_TOL * 1e-3; + new_g2 = ratio_aq * (exp(cd_m * z * psi_avg2) - 1); + if (use.Get_surface_ptr()->Get_only_counter_ions() && + ((surf_chrg_eq < 0 && z < 0) + || (surf_chrg_eq > 0 && z > 0))) + new_g2 = -ratio_aq; + if (new_g2 <= -ratio_aq) + new_g2 = -ratio_aq + G_TOL * 1e-3; + if (fabs(new_g) >= 1) + { + if (fabs((new_g - charge_ptr->Get_g_map()[z].Get_g()) / new_g) > convergence_tolerance) + { + converge = FALSE; + } + } + else + { + if (fabs(new_g - charge_ptr->Get_g_map()[z].Get_g()) > convergence_tolerance) + { + converge = FALSE; + } + } + charge_ptr->Get_g_map()[z].Set_g(new_g); + if (new_g != 0) + { + charge_ptr->Get_g_map()[z].Set_dg((new_g2 - new_g) / dif); + } + else + { + charge_ptr->Get_g_map()[z].Set_dg(-z); + } + /* save Boltzmann factor * water fraction for MCD calc's in transport */ + if (converge) + { + if (use.Get_surface_ptr()->Get_only_counter_ions()) + { + if (surf_chrg_eq * z > 0) // co-ions are not in the DL + charge_ptr->Get_z_gMCD_map()[z] = 0; + else // assume that counter-ions have the free water conc for diffusion + charge_ptr->Get_z_gMCD_map()[z] = ratio_aq_tot; + } + else + charge_ptr->Get_z_gMCD_map()[z] = (new_g / ratio_aq + 1) * ratio_aq_tot; + } + } + if (debug_diffuse_layer == TRUE) + { + std::string name = x[j]->master[0]->elt->name; + Utilities::replace("_psi", "", name); + output_msg(sformatf( + "\nDonnan all on %s (%d): charge, \tg, \tdg, Psi_surface = %8f V. \n", + name.c_str(), (int) charge_ptr->Get_g_map().size(), + x[j]->master[0]->s->la * 2 * LOG_10 * R_KJ_DEG_MOL * + tk_x / F_KJ_V_EQ)); + for (std::map::iterator i_it = charge_ptr->Get_g_map().begin(); + i_it != charge_ptr->Get_g_map().end(); i_it++) + { + output_msg(sformatf( "\t%12f\t%12.4e\t%12.4e\n", + (double) i_it->first, + (double) i_it->second.Get_g(), + (double) i_it->second.Get_dg())); + } + } + } + return (converge); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_init_donnan(void) +/* ---------------------------------------------------------------------- */ +{ + LDBLE f_psi, surf_chrg_eq, psi_avg, f_sinh, A_surf, ratio_aq; + + if (use.Get_surface_ptr() == NULL) + return (OK); + f_sinh = + //sqrt(8000.0 * EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000.0) * + // tk_x * mu_x); + sqrt(8000.0 * eps_r * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000.0) * + tk_x * mu_x); + if (convergence_tolerance >= 1e-8) + { + G_TOL = 1e-9; + } + else + { + G_TOL = 1e-13; + } +/* + * sum eq of each charge number in solution... + */ + charge_group_map.clear(); + charge_group_map[0.0] = 0.0; + + for (int i = 0; i < count_s_x; i++) + { + if (s_x[i]->type > HPLUS) + continue; + if (charge_group_map.find(s_x[i]->z) != charge_group_map.end()) + { + charge_group_map.find(s_x[i]->z)->second += s_x[i]->z * s_x[i]->moles * s_x[i]->erm_ddl; + } + else + { + charge_group_map[s_x[i]->z] = s_x[i]->z * s_x[i]->moles * s_x[i]->erm_ddl; + } + } +/* + * calculate g for each surface... + */ + for (int j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + charge_ptr->Get_g_map().clear(); + + /* find surface charge from potential... */ + A_surf = charge_ptr->Get_specific_area() * charge_ptr->Get_grams(); + if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + f_psi = x[j + 2]->master[0]->s->la * LOG_10; /* -FPsi/RT */ + f_psi = f_psi / 2; + } else + f_psi = x[j]->master[0]->s->la * LOG_10; + surf_chrg_eq = A_surf * f_sinh * sinh(f_psi) / F_C_MOL; + + /* find psi_avg that matches surface charge... */ +/* psi_avg = calc_psi_avg (0); + appt 7/9/8... may have to change above one */ + psi_avg = calc_psi_avg(charge_ptr, 0 * surf_chrg_eq); + + /* fill in g's */ + ratio_aq = charge_ptr->Get_mass_water() / mass_water_aq_x; + + std::map::iterator kit; + for (kit = charge_group_map.begin(); kit != charge_group_map.end(); kit++) + { + LDBLE z = kit->first; + LDBLE eq = kit->second; + + charge_ptr->Get_g_map()[z].Set_g(ratio_aq * (exp(-z * psi_avg) - 1)); + + if (use.Get_surface_ptr()->Get_only_counter_ions() + && ((surf_chrg_eq < 0 && z < 0) + || (surf_chrg_eq > 0 && z > 0))) + charge_ptr->Get_g_map()[z].Set_g(-ratio_aq); + + if (charge_ptr->Get_g_map()[z].Get_g() != 0) + { + charge_ptr->Get_g_map()[z].Set_dg(-A_surf * f_sinh * cosh(f_psi) / + (eq * F_C_MOL)); + } + else + { + charge_ptr->Get_g_map()[z].Set_dg(-z); + } + /* save g for species */ + for (int i = 0; i < count_s_x; i++) + { + int is = s_x[i]->number; + assert (is < (int) s_diff_layer.size()); + + s_diff_layer[is][charge_ptr->Get_name()].Set_g_moles(0.0); + s_diff_layer[is][charge_ptr->Get_name()].Set_dg_g_moles(0.0); + } + } + if (debug_diffuse_layer == TRUE) + { + std::string name = x[j]->master[0]->elt->name; + Utilities::replace("_psi", "", name); + output_msg(sformatf( + "\nDonnan init on %s : charge, \tg, \tdg, Psi_surface = %8f V. \n", + name.c_str(), + x[j]->master[0]->s->la * 2 * LOG_10 * R_KJ_DEG_MOL * + tk_x / F_KJ_V_EQ)); + for (std::map::iterator i_it = charge_ptr->Get_g_map().begin(); + i_it != charge_ptr->Get_g_map().end(); i_it++) + { + output_msg(sformatf( "\t%12f\t%12.4e\t%12.4e\n", + (double) i_it->first, + (double) i_it->second.Get_g(), + (double) i_it->second.Get_dg())); + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_psi_avg(cxxSurfaceCharge *charge_ptr, LDBLE surf_chrg_eq) +/* ---------------------------------------------------------------------- */ +{ +/* + * calculate the average (F * Psi / RT) that lets the DL charge counter the surface charge + */ + LDBLE fd, fd1, p, temp, ratio_aq; + + ratio_aq = charge_ptr->Get_mass_water() / mass_water_aq_x; + p = 0; + if (surf_chrg_eq == 0 || ratio_aq == 0) + return (0.0); + else if (surf_chrg_eq < 0) + p = -0.5 * log(-surf_chrg_eq * ratio_aq / mu_x + 1); + else if (surf_chrg_eq > 0) + p = 0.5 * log(surf_chrg_eq * ratio_aq / mu_x + 1); +/* + * Optimize p in SS{s_x[i]->moles * z_i * g(p)} = -surf_chrg_eq + * g(p) = exp(-p * z_i) * ratio_aq + * Elsewhere in PHREEQC, g is the excess, after subtraction of conc's for p = 0: + * g(p) = (exp(-p *z_i) - 1) * ratio_aq + */ + int l_iter = 0; + do + { + fd = surf_chrg_eq; + fd1 = 0.0; + std::map::iterator it; + for (it = charge_group_map.begin(); it != charge_group_map.end(); it++) + { + LDBLE z = it->first; + LDBLE eq = it->second; + /* multiply with ratio_aq for multiplier options cp and cm + in calc_all_donnan (not used now)... */ + temp = exp(-z * p) * ratio_aq; + + if (use.Get_surface_ptr()->Get_only_counter_ions() && + ((surf_chrg_eq < 0 && z < 0) + || (surf_chrg_eq > 0 && z > 0))) + temp = 0.0; + fd += eq * temp; + fd1 -= z * eq * temp; + } + fd /= -fd1; + p += (fd > 1) ? 1 : ((fd < -1) ? -1 : fd); + if (fabs(p) < G_TOL) + p = 0.0; + l_iter++; + if (l_iter > 50) + { + error_string = sformatf( + "\nToo many iterations in subroutine calc_psi_avg; surface charge = %12.4e; surface water = %12.4e.\n", + (double) surf_chrg_eq, (double) charge_ptr->Get_mass_water()); + error_msg(error_string, STOP); + } + } + while (fabs(fd) > 1e-12 && p != 0.0); + if (debug_diffuse_layer == TRUE) + output_msg(sformatf( + "iter in calc_psi_avg = %d. g(+1) = %8f. surface charge = %12.4e.\n", + l_iter, (double) (exp(-p) - 1), (double) surf_chrg_eq)); + + return (p); +} diff --git a/phreeqcpp/inverse.cpp b/phreeqcpp/inverse.cpp new file mode 100644 index 00000000..f9f500e9 --- /dev/null +++ b/phreeqcpp/inverse.cpp @@ -0,0 +1,5363 @@ +#include "Phreeqc.h" +#include "phqalloc.h" +#include "Utils.h" +#include "Solution.h" +#include "SolutionIsotope.h" + + +#define MAX_MODELS 20 +#define MIN_TOTAL_INVERSE 1e-14 + +/* variables local to module */ +#define SCALE_EPSILON .0009765625 +#define SCALE_WATER 1. +#define SCALE_ALL 1. +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +inverse_models(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through list of inverse models, make calculations + * for any marked "new". + */ + int n/*, print1*/; + char string[MAX_LENGTH]; + if (count_inverse <= 0) return OK; + // Revert to previous headings after inverse modeling + std::vector old_headings; + //int i; + + //for (i = 0; i < user_punch_count_headings; i++) + //{ + // old_headings.push_back(user_punch_headings[i]); + //} + + array1 = NULL; + inv_zero = NULL; + inv_res = NULL; + inv_delta1 = NULL; + delta2 = NULL; + delta3 = NULL; + delta_save = NULL; + inv_cu = NULL; + inv_iu = NULL; + inv_is = NULL; + col_name = NULL; + row_name = NULL; + min_delta = NULL; + max_delta = NULL; + good = NULL; + bad = NULL; + minimal = NULL; + + state = INVERSE; + dl_type_x = cxxSurface::NO_DL; + + for (n = 0; n < count_inverse; n++) + { + if (inverse[n].new_def == TRUE) + { +/* + * dump .lon file + */ + if (inverse[n].netpath != NULL) + dump_netpath(&inverse[n]); + +/* + * open .pat file + */ + if (inverse[n].pat != NULL) + { + strcpy(string, inverse[n].pat); + if (replace(".pat", ".pat", string) != TRUE) + { + strcat(string, ".pat"); + } + netpath_file = fopen(string, "w"); + if (netpath_file == NULL) + { + error_string = sformatf( "Can`t open file, %s.", string); + error_msg(error_string, STOP); +#if !defined(R_SO) + exit(4); +#endif + } + count_inverse_models = 0; + count_pat_solutions = 0; + /* Header */ + fprintf(netpath_file, "2.14 # File format\n"); + } +/* + * Fill in stucture "use". + */ + use.Set_inverse_in(true); + use.Set_inverse_ptr(&inverse[n]); + use.Set_n_inverse_user(inverse[n].n_user); +/* + * Initial prints + */ + error_string = sformatf( + "Beginning of inverse modeling %d calculations.", + inverse[n].n_user); + dup_print(error_string, TRUE); + + if (inverse[n].mp == TRUE) + { + output_msg(sformatf( + "Using Cl1MP multiprecision optimization routine.\n")); + + } + else + { + output_msg(sformatf( + "Using Cl1 standard precision optimization routine.\n")); + } + status(0, NULL); + +/* + * Setup and solve + */ + count_calls = 0; + setup_inverse(&(inverse[n])); + punch_model_heading(&inverse[n]); + solve_inverse(&(inverse[n])); + if (inverse[n].count_isotope_unknowns > 0) + { + inverse[n].isotope_unknowns = + (struct isotope *) free_check_null(inverse[n]. + isotope_unknowns); + } + inverse[n].new_def = FALSE; + if (inverse[n].pat != NULL) + { + fclose(netpath_file); + netpath_file = NULL; + } + } + } + + //user_punch_count_headings = (int) old_headings.size(); + //user_punch_headings = (const char **) PHRQ_realloc(user_punch_headings, + // (size_t) (user_punch_count_headings + 1) * sizeof(char *)); + //if (user_punch_headings == NULL) + // malloc_error(); + //for (i = 0; i < user_punch_count_headings; i++) + //{ + // user_punch_headings[i] = string_hsave(old_headings[i].c_str()); + //} + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +setup_inverse(struct inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Fill in array for an inverse problem + */ + int i, j, k, i_alk, i_carb; + int max; + int count_rows_t; + int column, row; + int temp; + LDBLE isotope_number; + LDBLE f, coef, cb, conc; + char token[MAX_LENGTH]; + struct phase *phase_ptr; + cxxSolution *solution_ptr; + struct reaction *rxn_ptr; + struct master *master_ptr; +/* + * Determine array sizes, row and column positions + */ + toler = inv_ptr->tolerance; + if (inv_ptr->mp == TRUE) + { + toler = inv_ptr->mp_tolerance; + } +/* + * Alkalinity derivatives with pH and carbon + */ + carbon = 1; + temp = pr.status; + pr.status = FALSE; + + // current_selected_output is NULL at this point + carbon_derivs(inv_ptr); + pr.status = temp; + + //current_selected_output->Set_inverse(temp_inv); + state = INVERSE; +/* + * tidy isotopes if necessary + */ + inv_ptr->count_isotope_unknowns = 0; + if (inv_ptr->count_isotopes > 0) + { + inv_ptr->count_isotope_unknowns = + count_isotope_unknowns(inv_ptr, &inv_ptr->isotope_unknowns); + if (get_input_errors() > 0) + { + error_msg("Stopping because of input errors.", STOP); + } + check_isotopes(inv_ptr); + if (get_input_errors() > 0) + { + error_msg("Stopping because of input errors.", STOP); + } + } + +/* + * count unknowns + */ + max_column_count = inv_ptr->count_elts * inv_ptr->count_solns + /* epsilons */ + inv_ptr->count_solns + /* solutions */ + inv_ptr->count_phases + /* phases */ + inv_ptr->count_redox_rxns + /* redox reactions */ + carbon * inv_ptr->count_solns + /* pH */ + 1 + /* water */ + inv_ptr->count_isotope_unknowns * inv_ptr->count_solns + /* isotopes in solution */ + inv_ptr->count_isotopes * inv_ptr->count_phases + /* isotopes in phases */ + 1 + 1; /* rhs, ineq */ + count_unknowns = max_column_count - 2; + col_phases = inv_ptr->count_solns; + col_redox = col_phases + inv_ptr->count_phases; + col_epsilon = col_redox + inv_ptr->count_redox_rxns; + col_ph = col_epsilon + inv_ptr->count_elts * inv_ptr->count_solns; + col_water = col_ph + carbon * inv_ptr->count_solns; + col_isotopes = col_water + 1; + col_phase_isotopes = + col_isotopes + inv_ptr->count_isotope_unknowns * inv_ptr->count_solns; + max_row_count = inv_ptr->count_solns * inv_ptr->count_elts + /* optimize */ + carbon * inv_ptr->count_solns + /* optimize ph */ + 1 + /* optimize water */ + inv_ptr->count_solns * inv_ptr->count_isotope_unknowns + /* optimize isotopes */ + inv_ptr->count_isotopes * inv_ptr->count_phases + /* optimize phase isotopes */ + inv_ptr->count_elts + /* mass balances */ + 1 + 1 + /* fractions, init and final */ + inv_ptr->count_solns + /* charge balances */ + carbon * inv_ptr->count_solns + /* dAlk = dC + dph */ + inv_ptr->count_isotopes + /* isotopes */ + 2 * inv_ptr->count_solns * inv_ptr->count_elts + /* epsilon constraints */ + 2 * carbon * inv_ptr->count_solns + /* epsilon on ph */ + 2 + /* epsilon for water */ + 2 * inv_ptr->count_isotope_unknowns * inv_ptr->count_solns + /* epsilon for isotopes */ + 2 * inv_ptr->count_isotopes * inv_ptr->count_phases + /* epsilon for isotopes in phases */ + 2; /* work space */ + + row_mb = inv_ptr->count_solns * inv_ptr->count_elts + + carbon * inv_ptr->count_solns + 1 + + inv_ptr->count_solns * inv_ptr->count_isotope_unknowns + + inv_ptr->count_isotopes * inv_ptr->count_phases; + row_fract = row_mb + inv_ptr->count_elts; + row_charge = row_fract + 2; + row_carbon = row_charge + inv_ptr->count_solns; + row_isotopes = row_carbon + carbon * inv_ptr->count_solns; + row_epsilon = row_isotopes + inv_ptr->count_isotopes; +/* The next three are not right, some rows of epsilon are deleted */ +/* + row_ph_epsilon = row_epsilon + 2 * inv_ptr->count_solns * inv_ptr->count_elts; + row_water_epsilon = row_ph + 2 * carbon * inv_ptr->count_solns; + row_isotope_epsilon + row_isotope_phase_epsilon + */ +/* + * Malloc space for arrays + */ + array = (LDBLE *) free_check_null(array); + array = + (LDBLE *) PHRQ_malloc((size_t) max_column_count * max_row_count * + sizeof(LDBLE)); + if (array == NULL) + malloc_error(); + + array1 = + (LDBLE *) PHRQ_malloc((size_t) max_column_count * max_row_count * + sizeof(LDBLE)); + if (array1 == NULL) + malloc_error(); + + col_name = + (const char **) PHRQ_malloc((size_t) max_column_count * sizeof(char *)); + if (col_name == NULL) + malloc_error(); + + row_name = (const char **) PHRQ_malloc((size_t) max_row_count * sizeof(char *)); + if (row_name == NULL) + malloc_error(); + + delta = (LDBLE *) free_check_null(delta); + delta = (LDBLE *) PHRQ_malloc((size_t) max_column_count * sizeof(LDBLE)); + if (delta == NULL) + malloc_error(); + + inv_delta1 = (LDBLE *) PHRQ_malloc((size_t) max_column_count * sizeof(LDBLE)); + if (inv_delta1 == NULL) + malloc_error(); + + delta2 = (LDBLE *) PHRQ_malloc((size_t) max_column_count * sizeof(LDBLE)); + if (delta2 == NULL) + malloc_error(); + + delta3 = (LDBLE *) PHRQ_malloc((size_t) max_column_count * sizeof(LDBLE)); + if (delta3 == NULL) + malloc_error(); + + delta_save = + (LDBLE *) PHRQ_malloc((size_t) max_column_count * sizeof(LDBLE)); + if (delta_save == NULL) + malloc_error(); + + min_delta = + (LDBLE *) PHRQ_malloc((size_t) max_column_count * sizeof(LDBLE)); + if (min_delta == NULL) + malloc_error(); + + max_delta = + (LDBLE *) PHRQ_malloc((size_t) max_column_count * sizeof(LDBLE)); + if (max_delta == NULL) + malloc_error(); + + inv_res = (LDBLE *) PHRQ_malloc((size_t) max_row_count * sizeof(LDBLE)); + if (inv_res == NULL) + malloc_error(); + + if (max_column_count < max_row_count) + { + max = max_row_count; + } + else + { + max = max_column_count; + } + inv_zero = (LDBLE *) PHRQ_malloc((size_t) max * sizeof(LDBLE)); + if (inv_zero == NULL) + malloc_error(); +/* + * Define inv_zero and inv_zero array, delta + */ + for (i = 0; i < max; i++) + inv_zero[i] = 0.0; + + memcpy((void *) &(delta[0]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + memcpy((void *) &(min_delta[0]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + memcpy((void *) &(max_delta[0]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + for (i = 0; i < max_row_count; i++) + { + memcpy((void *) &(array[i * max_column_count]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + } +/* + * begin filling array + */ + count_rows = 0; +/* + * optimization + */ + count_optimize = inv_ptr->count_solns * inv_ptr->count_elts + /* optimize */ + carbon * inv_ptr->count_solns + /* optimize ph */ + 1 + /* optimize water */ + inv_ptr->count_solns * inv_ptr->count_isotope_unknowns + /* optimize isotopes */ + inv_ptr->count_isotopes * inv_ptr->count_phases; /* optimize phase isotopes */ + + for (i = 0; i < count_optimize; i++) + { + row_name[count_rows] = string_hsave("optimize"); + count_rows++; + } + write_optimize_names(inv_ptr); +/* + * equalities + */ + +/* + * Mass_balance: solution data + */ + + /* initialize master species */ + for (i = 0; i < count_master; i++) + { + master[i]->in = -1; + if (strstr(master[i]->elt->name, "Alk") == master[i]->elt->name) + { + master_alk = master[i]; + } + } + /* mark master species included in model, write row names */ + count_rows_t = count_rows; + i_alk = -1; + i_carb = -1; + for (i = 0; i < inv_ptr->count_elts; i++) + { + master_ptr = inv_ptr->elts[i].master; + if (master_ptr == master_alk) + i_alk = i; + if (strcmp(master_ptr->elt->name, "C(4)") == 0) + i_carb = i; + inv_ptr->elts[i].master->in = count_rows_t; + row_name[count_rows_t] = inv_ptr->elts[i].master->elt->name; + count_rows_t++; + } + /* put concentrations in array */ + for (i = 0; i < inv_ptr->count_solns; i++) + { + xsolution_zero(); + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, inv_ptr->solns[i]); + if (solution_ptr == NULL) + { + error_string = sformatf( "Solution number %d not found.", + inv_ptr->solns[i]); + error_msg(error_string, STOP); + } + /* write master species concentrations */ + cxxNameDouble::iterator jit = solution_ptr->Get_totals().begin(); + for ( ; jit != solution_ptr->Get_totals().end(); jit++) + { + master_ptr = master_bsearch(jit->first.c_str()); + master_ptr->total += jit->second; + /* List elements not included in model */ + if (master_ptr->in < 0) + { + error_string = sformatf( + "%s is included in solution %d, but is not included as a mass-balance constraint.", + jit->first.c_str(), + inv_ptr->solns[i]); + warning_msg(error_string); + } + } + master_alk->total = solution_ptr->Get_total_alkalinity(); + f = 1.0; + if (i == (inv_ptr->count_solns - 1)) + { + f = -1.0; + } + column = i; + sprintf(token, "soln %d", i); + col_name[column] = string_hsave(token); + for (j = 0; j < count_master; j++) + { + if (master[j]->in >= 0) + { + array[master[j]->in * max_column_count + i] = + f * master[j]->total; + if (master[j]->s == s_eminus) + { + array[master[j]->in * max_column_count + i] = 0.0; + } + } + } + /* calculate charge balance for elements in model */ + cb = 0; + for (j = 0; j < count_master; j++) + { + if (master[j]->in >= 0) + { + if (master[j]->s == s_eminus) + { + coef = 0.0; + } + else if (master[j] == master_alk) + { + coef = -1.0; + } + else + { + coef = master[j]->s->z + master[j]->s->alk; + } + cb += coef * master[j]->total; + } + } + if (fabs(cb) < toler) + cb = 0.0; + array[(row_charge + i) * max_column_count + i] = cb; + } + +/* mass_balance: phase data */ + + for (i = 0; i < inv_ptr->count_phases; i++) + { + phase_ptr = inv_ptr->phases[i].phase; + rxn_ptr = phase_ptr->rxn_s; + column = col_phases + i; + col_name[column] = phase_ptr->name; + for (j = 1; rxn_ptr->token[j].s != NULL; j++) + { + if (rxn_ptr->token[j].s->secondary != NULL) + { + master_ptr = rxn_ptr->token[j].s->secondary; + } + else + { + master_ptr = rxn_ptr->token[j].s->primary; + } + if (master_ptr == NULL) + { + error_string = sformatf( + "Setup_inverse, reaction for phase, %s.", + phase_ptr->name); + error_msg(error_string, STOP); + } + if (master_ptr->s == s_hplus) + continue; + if (master_ptr->s == s_h2o) + { + row = row_fract; +/* turn off h2o from minerals in water mass balance */ + if (inv_ptr->mineral_water != TRUE) + continue; + + } + else + { + row = master_ptr->in; + } + /* e- has coef of 0 for some reason */ + coef = master_ptr->coef; + if (coef <= 0) + coef = 1.0; + array[row * max_column_count + column] = + rxn_ptr->token[j].coef * coef; + } + row = master_alk->in; /* include alkalinity for phase */ + array[row * max_column_count + column] = calc_alk(rxn_ptr); + } + +/* mass balance: redox reaction data */ + + k = 0; + for (i = 0; i < inv_ptr->count_elts; i++) + { + if (inv_ptr->elts[i].master->s->primary == NULL) + { + coef = inv_ptr->elts[i].master->coef; + rxn_ptr = inv_ptr->elts[i].master->rxn_primary; + column = col_redox + k; + col_name[column] = inv_ptr->elts[i].master->elt->name; + k++; + for (j = 0; rxn_ptr->token[j].s != NULL; j++) + { + if (rxn_ptr->token[j].s->secondary != NULL) + { + master_ptr = rxn_ptr->token[j].s->secondary; + } + else + { + master_ptr = rxn_ptr->token[j].s->primary; + } + if (master_ptr == NULL) + { + error_string = sformatf( + "Subroutine setup_inverse, element not found, %s.", + rxn_ptr->token[j].s->name); + error_msg(error_string, STOP); + } + if (master_ptr->s == s_hplus) + continue; + if (master_ptr->s == s_h2o) + { + row = row_fract; +/* turn off h2o from minerals in water mass balance */ + if (inv_ptr->mineral_water != TRUE) + continue; + } + else + { + row = master_ptr->in; + } + assert(row * max_column_count + column < max_column_count * max_row_count); + assert(row >= 0); + assert(column >= 0); + array[row * max_column_count + column] = + rxn_ptr->token[j].coef; + /* if coefficient of element is not 1.0 in master species */ + if (j != 0) + array[row * max_column_count + column] /= coef; + } + row = master_alk->in; /* include alkalinity for redox reaction */ + array[row * max_column_count + column] = + (calc_alk(rxn_ptr) - inv_ptr->elts[i].master->s->alk) / coef; + } + } + +/* mass-balance: epsilons */ + + column = col_epsilon; + for (i = 0; i < inv_ptr->count_elts; i++) + { + row = inv_ptr->elts[i].master->in; + for (j = 0; j < inv_ptr->count_solns; j++) + { + if (j < (inv_ptr->count_solns - 1)) + { + array[row * max_column_count + column] = 1.0; + } + else + { + array[row * max_column_count + column] = -1.0; + } + if (inv_ptr->elts[i].master->s == s_eminus) + { + array[row * max_column_count + column] = 0.0; + } + sprintf(token, "%s %d", row_name[row], j); + col_name[column] = string_hsave(token); + column++; + } + } + count_rows += inv_ptr->count_elts; + +/* put names in col_name for ph */ + + for (i = 0; i < inv_ptr->count_solns; i++) + { + sprintf(token, "ph %d", i); + col_name[column] = string_hsave(token); + column++; + } +/* put names in col_name for water */ + + sprintf(token, "water"); + col_name[column] = string_hsave(token); + column++; + +/* put names of isotopes in col_name */ + for (i = 0; i < inv_ptr->count_solns; i++) + { + for (j = 0; j < inv_ptr->count_isotope_unknowns; j++) + { + sprintf(token, "%d%s %d", + (int) inv_ptr->isotope_unknowns[j].isotope_number, + inv_ptr->isotope_unknowns[j].elt_name, i); + col_name[column] = string_hsave(token); + column++; + } + } + +/* put phase isotopes in col_name */ + + if (inv_ptr->count_isotopes > 0) + { + /* isotopes of phases phases */ + for (i = 0; i < inv_ptr->count_phases; i++) + { + for (j = 0; j < inv_ptr->count_isotopes; j++) + { + sprintf(token, "%d%s %s", + (int) inv_ptr->isotopes[j].isotope_number, + inv_ptr->isotopes[j].elt_name, + inv_ptr->phases[i].phase->name); + col_name[column] = string_hsave(token); + column++; + } + } + } +/* + * Initial solution mixing fractions or water mass balance + */ + for (i = 0; i < inv_ptr->count_solns; i++) + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, inv_ptr->solns[i]); + if (i < inv_ptr->count_solns - 1) + { + array[count_rows * max_column_count + i] = + 1.0 / gfw_water * solution_ptr->Get_mass_water(); + } + else + { + array[count_rows * max_column_count + inv_ptr->count_solns - 1] = + -1.0 / gfw_water * solution_ptr->Get_mass_water(); + } + } + /* coefficient for water uncertainty */ + if (inv_ptr->water_uncertainty > 0) + { + array[count_rows * max_column_count + col_water] = 1.0; + } + row_name[count_rows] = string_hsave("H2O"); + row_water = count_rows; + count_rows++; + +/* + * Final solution fraction equals 1.0 + */ + + array[count_rows * max_column_count + inv_ptr->count_solns - 1] = 1.0; + array[count_rows * max_column_count + count_unknowns] = 1.0; + row_name[count_rows] = string_hsave("fract, final"); + count_rows++; + +/* + * Charge balance: + */ + + for (i = 0; i < inv_ptr->count_solns; i++) + { +/* solution_ptr = solution_bsearch(inv_ptr->solns[i], &j, TRUE); */ +/* array[count_rows * max_column_count + i] = solution_ptr->cb; */ + for (j = 0; j < inv_ptr->count_elts; j++) + { + column = col_epsilon + j * inv_ptr->count_solns + i; + coef = + inv_ptr->elts[j].master->s->z + + inv_ptr->elts[j].master->s->alk; + if (inv_ptr->elts[j].master == master_alk) + { + coef = -1.0; + } + array[count_rows * max_column_count + column] = coef; + if (inv_ptr->elts[j].master->s == s_eminus) + { + array[count_rows * max_column_count + column] = 0.0; + } + } + sprintf(token, "%s %d", "charge", i); + row_name[count_rows] = string_hsave(token); + count_rows++; + } +/* + * dC = (dC/dph)*dph + (dC/dAlk)*dAlk for each solution + */ +/* + * dAlk = (dAlk/dC)*dC + (dAlk/dpH)*dpH for each solution + */ + for (i = 0; i < inv_ptr->count_solns; i++) + { + if (inv_ptr->dalk_dph[i] != 0 || inv_ptr->dalk_dc[i] != 0) + { + column = col_ph + i; + array[count_rows * max_column_count + column] = + inv_ptr->dalk_dph[i]; + column = col_epsilon + i_alk * inv_ptr->count_solns + i; + array[count_rows * max_column_count + column] = -1.0; + column = col_epsilon + i_carb * inv_ptr->count_solns + i; + array[count_rows * max_column_count + column] = + inv_ptr->dalk_dc[i]; + } + sprintf(token, "%s %d", "dAlk", i); + row_name[count_rows] = string_hsave(token); + count_rows++; + } +/* + * Isotope mass balances + */ + if (get_input_errors() > 0) + { + error_msg("Stopping because of input errors.", STOP); + } + if (inv_ptr->count_isotopes != 0) + { + for (j = 0; j < inv_ptr->count_isotopes; j++) + { + isotope_balance_equation(inv_ptr, count_rows, j); + sprintf(token, "%d%s", (int) inv_ptr->isotopes[j].isotope_number, + inv_ptr->isotopes[j].elt_name); + row_name[count_rows] = string_hsave(token); + count_rows++; + } + } +/* + * inequalities + */ + row_epsilon = count_rows; + for (i = 0; i < inv_ptr->count_solns; i++) + { + for (j = 0; j < inv_ptr->count_elts; j++) + { + if (inv_ptr->elts[j].master->s == s_eminus) + continue; + column = col_epsilon + j * inv_ptr->count_solns + i; + +/* calculate magnitude of bound */ + + coef = inv_ptr->elts[j].uncertainties[i]; + if (coef <= 0.0) + { + coef = -coef; + } + else + { + coef = + array[inv_ptr->elts[j].master->in * max_column_count + + i] * coef; + coef = fabs(coef); + } + + if (coef < toler) + coef = 0; + +/* zero column if uncertainty is zero */ + if (coef == 0.0) + { + for (k = 0; k < count_rows; k++) + { + array[k * max_column_count + column] = 0.0; + } + continue; + } + +/* this statement probably obviates some of the following logic. */ +/* coef += toler; */ + +/* scale epsilon optimization equation */ + + if (coef < toler) + { + array[(column - col_epsilon) * max_column_count + column] = + SCALE_EPSILON / toler; + } + else + { + array[(column - col_epsilon) * max_column_count + column] = + SCALE_EPSILON / coef; + } + +/* set upper limit of change in positive direction */ + if (coef < toler) + { + coef = toler; + f = 10; + } + else + { + f = 1.0; + } + array[count_rows * max_column_count + column] = 1.0 * f; + array[count_rows * max_column_count + i] = -coef * f; + sprintf(token, "%s %s", inv_ptr->elts[j].master->elt->name, + "eps+"); + row_name[count_rows] = string_hsave(token); + count_rows++; + +/* set lower limit of change in negative direction */ + conc = array[inv_ptr->elts[j].master->in * max_column_count + i]; + + /* if concentration is zero, only positive direction allowed */ + if (conc == 0.0) + { + delta[column] = 1.0; + continue; + } + /* if uncertainty is less than tolerance, set uncertainty to toler */ + if (coef <= toler) + { +/* f = 10 * toler / coef; */ + coef = toler; + f = 10; + } + else + { + f = 1.0; + } + /* if uncertainty is greater than concentration, + maximum negative is equal to concentrations, + except alkalinity */ + if (coef > fabs(conc) && + (strstr(inv_ptr->elts[j].master->elt->name, "Alkalinity") != + inv_ptr->elts[j].master->elt->name)) + coef = fabs(conc) + toler; + + array[count_rows * max_column_count + i] = -coef * f; + array[count_rows * max_column_count + column] = -1.0 * f; + sprintf(token, "%s %s", inv_ptr->elts[j].master->elt->name, + "eps-"); + row_name[count_rows] = string_hsave(token); + count_rows++; + } + } +/* + * inequalities for pH + */ + /* row_ph_epsilon = count_rows; */ + if (inv_ptr->carbon == TRUE) + { + for (i = 0; i < inv_ptr->count_solns; i++) + { + column = col_ph + i; + coef = inv_ptr->ph_uncertainties[i]; + +/* scale epsilon in optimization equation */ + + array[(column - col_epsilon) * max_column_count + column] = + SCALE_EPSILON / coef; + +/* set upper limit of change in positive direction */ + + array[count_rows * max_column_count + column] = 1.0; + array[count_rows * max_column_count + i] = -coef; + sprintf(token, "%s %s", "pH", "eps+"); + row_name[count_rows] = string_hsave(token); + count_rows++; + +/* set lower limit of change in negative direction */ + + array[count_rows * max_column_count + column] = -1.0; + array[count_rows * max_column_count + i] = -coef; + sprintf(token, "%s %s", "pH", "eps-"); + row_name[count_rows] = string_hsave(token); + count_rows++; + } + } +/* + * inequalities for water + */ + column = col_water; + coef = inv_ptr->water_uncertainty; + /* row_water_epsilon = count_rows; */ + if (coef > 0.0) + { +/* set upper limit of change in positive direction */ + array[count_rows * max_column_count + column] = 1.0; + array[count_rows * max_column_count + count_unknowns] = coef; + sprintf(token, "%s %s", "water", "eps+"); + row_name[count_rows] = string_hsave(token); + count_rows++; + +/* set lower limit of change in negative direction */ + + array[count_rows * max_column_count + column] = -1.0; + array[count_rows * max_column_count + count_unknowns] = coef; + sprintf(token, "%s %s", "water", "eps-"); + row_name[count_rows] = string_hsave(token); + count_rows++; + } +/* + * inequalities for isotopes + */ + row_isotope_epsilon = count_rows; + if (inv_ptr->count_isotopes > 0) + { + for (i = 0; i < inv_ptr->count_solns; i++) + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, inv_ptr->solns[i]); + for (j = 0; j < inv_ptr->count_isotope_unknowns; j++) + { + column = + col_isotopes + (i * inv_ptr->count_isotope_unknowns) + j; + master_ptr = inv_ptr->isotope_unknowns[j].master; + isotope_number = inv_ptr->isotope_unknowns[j].isotope_number; + std::map < std::string, cxxSolutionIsotope >::iterator kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + struct master *master_kit = master_bsearch(kit->second.Get_elt_name().c_str()); + if (master_kit == master_ptr && + kit->second.Get_isotope_number() == + isotope_number) + { + coef = kit->second.Get_x_ratio_uncertainty(); + +/* scale epsilon in optimization equation */ + + array[(column - col_epsilon) * max_column_count + + column] = SCALE_EPSILON / coef; + +/* set upper limit of change in positive direction */ + array[count_rows * max_column_count + column] = 1.0; + array[count_rows * max_column_count + i] = -coef; + sprintf(token, "%d%s %s", + (int) kit->second.Get_isotope_number(), + kit->second.Get_elt_name().c_str(), "eps+"); + row_name[count_rows] = string_hsave(token); + count_rows++; + +/* set lower limit of change in negative direction */ + + array[count_rows * max_column_count + column] = -1.0; + array[count_rows * max_column_count + i] = -coef; + sprintf(token, "%d%s %s", + (int) kit->second.Get_isotope_number(), + kit->second.Get_elt_name().c_str(), "eps-"); + row_name[count_rows] = string_hsave(token); + count_rows++; + break; + } + } + } + } + } +/* + * inequalities for isotopes in phases + */ + /* row_isotope_phase_epsilon = count_rows; */ + phase_isotope_inequalities(inv_ptr); + if (get_input_errors() > 0) + { + error_msg("Stopping because of input errors.", STOP); + } +/* + * Set non-negativity constraints + */ + + for (i = 0; i < inv_ptr->count_phases; i++) + { + if (inv_ptr->phases[i].constraint == PRECIPITATE) + { + delta[col_phases + i] = -1.0; + } + else if (inv_ptr->phases[i].constraint == DISSOLVE) + { + delta[col_phases + i] = 1.0; + } + } + for (i = 0; i < (inv_ptr->count_solns - 1); i++) + { + delta[i] = 1.0; + } +/* + * Scale water equation + */ + for (i = 0; i < max_column_count; i++) + { + array[row_water * max_column_count + i] *= SCALE_WATER; + } +/* + * Arrays are complete + */ + if (debug_inverse == TRUE) + { + for (i = 0; i < count_unknowns; i++) + { + output_msg(sformatf( "%d\t%s\n", i, col_name[i])); + } + for (i = 0; i < count_rows; i++) + { + k = 0; + output_msg(sformatf( "%d\t%s\n", i, row_name[i])); + for (j = 0; j < count_unknowns + 1; j++) + { + if (k > 7) + { + output_msg(sformatf( "\n")); + k = 0; + } + output_msg(sformatf( "%11.2e", + (double) array[i * max_column_count + j])); + k++; + } + if (k != 0) + { + output_msg(sformatf( "\n")); + } + output_msg(sformatf( "\n")); + } + output_msg(sformatf( "row_mb %d\n", row_mb)); + output_msg(sformatf( "row_fract %d\n", row_fract)); + output_msg(sformatf( "row_charge %d\n", row_charge)); + output_msg(sformatf( "row_carbon %d\n", row_carbon)); + output_msg(sformatf( "row_isotopes %d\n", row_isotopes)); + output_msg(sformatf( "row_epsilon %d\n", row_epsilon)); + + output_msg(sformatf( "col_phases %d\n", col_phases)); + output_msg(sformatf( "col_redox %d\n", col_redox)); + output_msg(sformatf( "col_epsilon %d\n", col_epsilon)); + output_msg(sformatf( "col_ph %d\n", col_ph)); + output_msg(sformatf( "col_water %d\n", col_water)); + output_msg(sformatf( "col_isotopes %d\n", col_isotopes)); + output_msg(sformatf( "col_phase_isotopes %d\n", + col_phase_isotopes)); + output_msg(sformatf( "count_unknowns %d\n", count_unknowns)); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +solve_inverse(struct inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Exhaustively search for mass-balance models with two options + * -minimal on or off + * -range on or off + * + */ + int i, j, n; + int quit, print, first; + int first_of_model_size, model_size; + unsigned long minimal_bits, good_bits; + char token[MAX_LENGTH]; + + n = count_unknowns; /* columns in A, C, E */ + klmd = max_row_count - 2; + nklmd = n + klmd; + n2d = n + 2; + + max_good = MAX_MODELS; + max_bad = MAX_MODELS; + max_minimal = MAX_MODELS; + + good = + (unsigned long *) PHRQ_malloc((size_t) max_good * + sizeof(unsigned long)); + if (good == NULL) + malloc_error(); + count_good = 0; + + bad = + (unsigned long *) PHRQ_malloc((size_t) max_bad * + sizeof(unsigned long)); + if (bad == NULL) + malloc_error(); + count_bad = 0; + + minimal = + (unsigned long *) PHRQ_malloc((size_t) max_minimal * + sizeof(unsigned long)); + if (minimal == NULL) + malloc_error(); + count_minimal = 0; + + col_back = (int *) PHRQ_malloc((size_t) max_column_count * sizeof(int)); + if (col_back == NULL) + malloc_error(); + + row_back = (int *) PHRQ_malloc((size_t) max_row_count * sizeof(int)); + if (row_back == NULL) + malloc_error(); + +/* + * Allocate space for arrays + */ + inv_cu = (LDBLE *) PHRQ_malloc((size_t) 2 * nklmd * sizeof(LDBLE)); + if (inv_cu == NULL) + malloc_error(); + memset(inv_cu, 0, ((size_t) (2 * nklmd * sizeof(LDBLE)))); + inv_iu = (int *) PHRQ_malloc((size_t) 2 * nklmd * sizeof(int)); + if (inv_iu == NULL) + malloc_error(); + inv_is = (int *) PHRQ_malloc((size_t) klmd * sizeof(int)); + if (inv_is == NULL) + malloc_error(); + + for (i = 0; i < 79; i++) + token[i] = '='; + token[79] = '\0'; +/* + * Set solutions, largest bit is final solution, smallest bit is initial solution 1 + * Set phases, largest bit is last phase, smallest bit is first phase + * Set current bits to complete list. + */ + soln_bits = 0; + if (inv_ptr->count_solns + inv_ptr->count_phases > 32) + { + error_msg + ("For inverse modeling, sum of initial solutions and phases must be <= 32.\n\tFor all reasonable calculations, the sum should be much less than 32.", + STOP); + } + for (i = inv_ptr->count_solns; i > 0; i--) + { + temp_bits = 1 << (i - 1); + soln_bits += temp_bits; + } + if (check_solns(inv_ptr) == ERROR) + { + error_msg("Calculations terminating.", STOP); + } +/* + * solutions are in highest bits, phases are in lower bits; + */ +/* + * All combinations of solutions + */ + first = TRUE; + for (; + get_bits(soln_bits, inv_ptr->count_solns - 2, + inv_ptr->count_solns - 1) > 0; soln_bits--) + { +/* + * Loop through all models of of descending size + */ + for (model_size = inv_ptr->count_phases; model_size >= 0; + model_size--) + { + first_of_model_size = TRUE; + quit = TRUE; + while (next_set_phases(inv_ptr, first_of_model_size, model_size) + == TRUE) + { + first_of_model_size = FALSE; + current_bits = + (soln_bits << inv_ptr->count_phases) + phase_bits; + + if (subset_bad(current_bits) == TRUE + || subset_minimal(current_bits) == TRUE) + continue; + quit = FALSE; +/* + * Switch for finding minimal models only + */ + if (inv_ptr->minimal == TRUE + && superset_minimal(current_bits) == TRUE) + continue; +/* + * Solve for minimum epsilons, continue if no solution found. + */ + if (solve_with_mask(inv_ptr, current_bits) == ERROR) + { + save_bad(current_bits); + if (first == TRUE) + { + post_mortem(); + quit = TRUE; + break; + } + else + { + continue; + } + } + first = FALSE; +/* + * Model has been found, set bits + */ + good_bits = current_bits; + for (i = 0; i < inv_ptr->count_phases; i++) + { + if (equal(inv_delta1[i + inv_ptr->count_solns], 0.0, TOL) == + TRUE) + { + good_bits = set_bit(good_bits, i, 0); + } + } + for (i = 0; i < inv_ptr->count_solns; i++) + { + if (equal(inv_delta1[i], 0.0, TOL) == TRUE) + { + good_bits = + set_bit(good_bits, i + inv_ptr->count_phases, 0); + } + } +/* + * Determine if model is new + */ + for (j = 0; j < count_good; j++) + { + if (good_bits == good[j]) + break; + } +/* + * Calculate ranges and print model only if NOT looking for minimal models + */ + print = FALSE; + if (j >= count_good && inv_ptr->minimal == FALSE) + { + print = TRUE; + save_good(good_bits); + if (inv_ptr->range == TRUE) + { + range(inv_ptr, good_bits); + } + print_model(inv_ptr); + punch_model(inv_ptr); + dump_netpath_pat(inv_ptr); + } +/* + * If superset of a minimal model continue + */ + minimal_bits = good_bits; + if (superset_minimal(minimal_bits) == TRUE) + { + if (print == TRUE) + { + if (pr.inverse == TRUE && pr.all == TRUE) + { + output_msg(sformatf( "%s\n\n", token)); + } + } + continue; + } +/* + * If not superset of minimal model, find minimal model + */ + minimal_bits = minimal_solve(inv_ptr, minimal_bits); + if (minimal_bits == good_bits && print == TRUE) + { + if (pr.inverse == TRUE && pr.all == TRUE) + { + output_msg(sformatf( + "\nModel contains minimum number of phases.\n")); + } + } + if (print == TRUE) + { + if (pr.inverse == TRUE && pr.all == TRUE) + { + output_msg(sformatf( "%s\n\n", token)); + } + } + for (j = 0; j < count_good; j++) + { + if (minimal_bits == good[j]) + break; + } + if (j >= count_good) + { + save_good(minimal_bits); + if (inv_ptr->range == TRUE) + { + range(inv_ptr, minimal_bits); + } + print_model(inv_ptr); + if (pr.inverse == TRUE && pr.all == TRUE) + { + output_msg(sformatf( + "\nModel contains minimum number of phases.\n")); + output_msg(sformatf( "%s\n\n", token)); + } + punch_model(inv_ptr); + dump_netpath_pat(inv_ptr); + } + save_minimal(minimal_bits); + } + if (quit == TRUE) + break; + } + } +/* + * Summary print + */ + if (pr.inverse == TRUE && pr.all == TRUE) + { + output_msg(sformatf( "\nSummary of inverse modeling:\n\n")); + output_msg(sformatf( "\tNumber of models found: %d\n", + count_good)); + output_msg(sformatf( "\tNumber of minimal models found: %d\n", + count_minimal)); + output_msg(sformatf( + "\tNumber of infeasible sets of phases saved: %d\n", + count_bad)); + output_msg(sformatf( "\tNumber of calls to cl1: %d\n", + count_calls)); + } + array = (LDBLE *) free_check_null(array); + delta = (LDBLE *) free_check_null(delta); + array1 = (LDBLE *) free_check_null(array1); + inv_zero = (LDBLE *) free_check_null(inv_zero); + inv_res = (LDBLE *) free_check_null(inv_res); + inv_delta1 = (LDBLE *) free_check_null(inv_delta1); + delta2 = (LDBLE *) free_check_null(delta2); + delta3 = (LDBLE *) free_check_null(delta3); + delta_save = (LDBLE *) free_check_null(delta_save); + inv_cu = (LDBLE *) free_check_null(inv_cu); + inv_iu = (int *) free_check_null(inv_iu); + inv_is = (int *) free_check_null(inv_is); + col_name = (const char **) free_check_null(col_name); + row_name = (const char **) free_check_null(row_name); + col_back = (int *) free_check_null(col_back); + row_back = (int *) free_check_null(row_back); + min_delta = (LDBLE *) free_check_null(min_delta); + max_delta = (LDBLE *) free_check_null(max_delta); + good = (unsigned long *) free_check_null(good); + bad = (unsigned long *) free_check_null(bad); + minimal = (unsigned long *) free_check_null(minimal); + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +unsigned long Phreeqc:: +minimal_solve(struct inverse *inv_ptr, unsigned long minimal_bits) +/* ---------------------------------------------------------------------- */ +{ +/* + * Starting with phases indicated in minimal bits, sequentially + * remove phases to find minimal solution + */ + int i; + unsigned long temp_bits_l; + if (debug_inverse == TRUE) + { + output_msg(sformatf( "Beginning minimal solve: \n")); + bit_print(minimal_bits, inv_ptr->count_phases + inv_ptr->count_solns); + } + for (i = 0; i < inv_ptr->count_phases + inv_ptr->count_solns - 1; i++) + { + if (get_bits(minimal_bits, i, 1) == 0) + continue; + temp_bits_l = 1 << i; /* 0's and one 1 */ + temp_bits_l = ~temp_bits_l; /* 1's and one 0 */ + minimal_bits = minimal_bits & temp_bits_l; + if (debug_inverse == TRUE) + { + output_msg(sformatf( "Solving for minimal\n")); + bit_print(minimal_bits, + inv_ptr->count_phases + inv_ptr->count_solns); + } + +/* + * minimal_bits cannot be superset of a minimal model, but + * could be subset of one of the sets of minerals with no feasible solution + * If it is a subset, then replace mineral and go on to next + */ + if (subset_bad(minimal_bits) == TRUE) + { + /* put bit back */ + minimal_bits = minimal_bits | ~temp_bits_l; /* 0's and one 1 */ + continue; + } + if (solve_with_mask(inv_ptr, minimal_bits) == ERROR) + { + save_bad(minimal_bits); + /* put bit back */ + minimal_bits = minimal_bits | ~temp_bits_l; /* 0's and one 1 */ + } + + } + if (debug_inverse == TRUE) + { + output_msg(sformatf( "\n\nMINIMAL MODEL\n\n")); + bit_print(minimal_bits, inv_ptr->count_phases + inv_ptr->count_solns); + } + + solve_with_mask(inv_ptr, minimal_bits); + unsigned long actual_bits = 0; + for (i = 0; i < inv_ptr->count_solns; i++) + { + if (equal(inv_delta1[i], 0.0, TOL) == FALSE) + { + actual_bits = set_bit(actual_bits, i + inv_ptr->count_phases, 1); + } + } + for (i = 0; i < inv_ptr->count_phases; i++) + { + if (equal(inv_delta1[i + inv_ptr->count_solns], 0.0, TOL) == FALSE) + { + actual_bits = set_bit(actual_bits, i, 1); + } + } + if (actual_bits != minimal_bits) + { + warning_msg("Roundoff errors in minimal calculation"); + } + return (actual_bits); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +solve_with_mask(struct inverse *inv_ptr, unsigned long cur_bits) +/* ---------------------------------------------------------------------- */ +{ +/* + * Uses cur_bits to zero out columns of the array and then solves. + */ + int i, k, l, m, n; + +/* + * Calculate dimensions + */ + k = row_mb; /* rows in A */ + l = row_epsilon - row_mb; /* rows in C */ + m = count_rows - row_epsilon; /* rows in E */ + n = count_unknowns; + + + + memcpy((void *) &(inv_res[0]), (void *) &(inv_zero[0]), + (size_t) max_row_count * sizeof(LDBLE)); + memcpy((void *) &(delta2[0]), (void *) &(delta[0]), + (size_t) max_column_count * sizeof(LDBLE)); + memcpy((void *) &(delta_save[0]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + + shrink(inv_ptr, array, array1, + &k, &l, &m, &n, cur_bits, delta2, col_back, row_back); + /* + * Save delta constraints + */ + for (i = 0; i < n; i++) + { + delta_save[col_back[i]] = delta2[i]; + } + + + if (debug_inverse == TRUE) + { + output_msg(sformatf( "\nColumns\n")); + for (i = 0; i < n; i++) + { + output_msg(sformatf( "\t%d\t%s\n", i, + col_name[col_back[i]])); + } + + output_msg(sformatf( "\nRows\n")); + for (i = 0; i < k + l + m; i++) + { + output_msg(sformatf( "\t%d\t%s\n", i, + row_name[row_back[i]])); + } + + output_msg(sformatf( "\nA and B arrays:\n\n")); + array_print(array1, k + l + m, n + 1, max_column_count); + + output_msg(sformatf( "\nInput delta vector:\n")); + for (i = 0; i < n; i++) + { + output_msg(sformatf( "%6d %-12.12s %10.2e", i, + col_name[col_back[i]], (double) delta2[i])); + output_msg(sformatf( "\n")); + } + + for (i = 0; i < k + l + m; i++) + { + if (inv_res[i] == 0) + continue; + output_msg(sformatf( "\nInput inv_res is non zero:\n")); + output_msg(sformatf( "%6d %-12.12s %10.2e", i, + row_name[row_back[i]], (double) inv_res[i])); + output_msg(sformatf( "\n")); + } + } +/* + * Call CL1 + */ + + if (debug_inverse == TRUE) + { + output_msg(sformatf( + "k, l, m, n, max_col, max_row\t%d\t%d\t%d\t%d\t%d\t%d\n", + k, l, m, n, max_column_count, max_row_count)); + } + + kode = 1; + iter = 1000; + count_calls++; + +#ifdef INVERSE_CL1MP + if (inv_ptr->mp == TRUE) + { + cl1mp(k, l, m, n, + nklmd, n2d, array1, + &kode, inv_ptr->mp_tolerance, &iter, + delta2, inv_res, &error, inv_cu, inv_iu, inv_is, TRUE, inv_ptr->mp_censor); + } + else + { + cl1(k, l, m, n, + nklmd, n2d, array1, + &kode, toler, &iter, delta2, inv_res, &error, inv_cu, inv_iu, inv_is, TRUE); + } +#else + cl1(k, l, m, n, + nklmd, n2d, array1, + &kode, toler, &iter, delta2, inv_res, &error, inv_cu, inv_iu, inv_is, TRUE); +#endif + if (kode == 3) + { + error_string = sformatf( + "Exceeded maximum iterations in inverse modeling: %d.\n" + "Recompile program with larger limit.", iter); + error_msg(error_string, STOP); + } + memcpy((void *) &(inv_delta1[0]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + for (i = 0; i < n; i++) + { + inv_delta1[col_back[i]] = delta2[i]; + } + +/* + * Debug, write results + */ + + if (debug_inverse == TRUE) + { + output_msg(sformatf( "kode: %d\titer: %d\terror: %e\n", kode, + iter, (double) error)); + output_msg(sformatf( "\nsolution vector:\n")); + for (i = 0; i < n; i++) + { + output_msg(sformatf( "%6d %-12.12s %10.2e", i, + col_name[col_back[i]], (double) delta2[i])); + output_msg(sformatf( "\n")); + } + + output_msg(sformatf( "\nresidual vector:\n")); + for (i = 0; i < (k + l + m); i++) + { + output_msg(sformatf( "%6d %-12.12s %10.2e\n", i, + row_name[row_back[i]], (double) inv_res[i])); + } + } + + if (kode != 0) + { + return (ERROR); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +unsigned long Phreeqc:: +get_bits(unsigned long bits, int position, int number) +/* ---------------------------------------------------------------------- */ +{ +/* + * Returns number of bits from position and below. + * position begins at 0. + */ + return ((bits >> (position + 1 - number)) & ~(~0ul << number)); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +save_minimal(unsigned long bits) +/* ---------------------------------------------------------------------- */ +{ +/* + * Keeps list of minimal models + */ + minimal[count_minimal] = bits; + count_minimal++; + if (count_minimal >= max_minimal) + { + max_minimal *= 2; + minimal = + (unsigned long *) PHRQ_realloc(minimal, + (size_t) max_minimal * + sizeof(unsigned long)); + if (minimal == NULL) + malloc_error(); + } + return (TRUE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +save_good(unsigned long bits) +/* ---------------------------------------------------------------------- */ +{ +/* + * Keeps list of good models, not necessarily minimal + */ + good[count_good] = bits; + count_good++; + if (count_good >= max_good) + { + max_good *= 2; + good = + (unsigned long *) PHRQ_realloc(good, + (size_t) max_good * + sizeof(unsigned long)); + if (good == NULL) + malloc_error(); + } + return (TRUE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +save_bad(unsigned long bits) +/* ---------------------------------------------------------------------- */ +{ +/* + * Keeps list of sets of phases with no feasible solution + */ + bad[count_bad] = bits; + count_bad++; + if (count_bad >= max_bad) + { + max_bad *= 2; + bad = + (unsigned long *) PHRQ_realloc(bad, + (size_t) max_bad * + sizeof(unsigned long)); + if (bad == NULL) + malloc_error(); + } + return (TRUE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +superset_minimal(unsigned long bits) +/* ---------------------------------------------------------------------- */ +{ +/* + * Checks whether bits is a superset of any of the minimal models + */ + int i; + unsigned long temp_bits_l; + for (i = 0; i < count_minimal; i++) + { + temp_bits_l = bits | minimal[i]; + if (temp_bits_l == bits) + { + return (TRUE); + } + } + return (FALSE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +subset_bad(unsigned long bits) +/* ---------------------------------------------------------------------- */ +{ +/* + * Checks whether bits is a superset of any of the bad models + */ + int i; + unsigned long temp_bits_l; + for (i = 0; i < count_bad; i++) + { + temp_bits_l = bits | bad[i]; + if (temp_bits_l == bad[i]) + { + return (TRUE); + } + } + return (FALSE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +subset_minimal(unsigned long bits) +/* ---------------------------------------------------------------------- */ +{ +/* + * Checks whether bits is a subset of any of the minimal models + */ + int i; + unsigned long temp_bits_l; + for (i = 0; i < count_minimal; i++) + { + temp_bits_l = bits | minimal[i]; + if (temp_bits_l == minimal[i]) + { + return (TRUE); + } + } + return (FALSE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +bit_print(unsigned long bits, int l) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints l bits of an unsigned long + */ + int i; + + for (i = l - 1; i >= 0; i--) + { + output_msg(sformatf( "%lu ", get_bits(bits, i, 1))); + } + output_msg(sformatf( "\n")); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_model(struct inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints model + */ + int i, j, k; + int column; + int print_msg; + cxxSolution *solution_ptr; + struct master *master_ptr; + struct isotope *isotope_ptr; + LDBLE d1, d2, d3, d4; + char token[MAX_LENGTH]; +/* + * Update screen + */ + status(count_good, NULL); +/* + * print solution data, epsilons, and revised data + */ + if (pr.inverse == FALSE || pr.all == FALSE) + return (OK); + max_pct = 0; + scaled_error = 0; + for (i = 0; i < inv_ptr->count_solns; i++) + { + if (equal(inv_delta1[i], 0.0, toler) == TRUE) + continue; + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, inv_ptr->solns[i]); + xsolution_zero(); + cxxNameDouble::iterator jit = solution_ptr->Get_totals().begin(); + for ( ; jit != solution_ptr->Get_totals().end(); jit++) + { + master_ptr = master_bsearch(jit->first.c_str()); + master_ptr->total = jit->second; + } + + output_msg(sformatf( "\nSolution %d: %s\n", inv_ptr->solns[i], + solution_ptr->Get_description().c_str())); + output_msg(sformatf( + "\n%15.15s %12.12s %12.12s %12.12s\n", " ", + "Input", "Delta", "Input+Delta")); + master_alk->total = solution_ptr->Get_total_alkalinity(); + if (inv_ptr->carbon == TRUE) + { + d1 = solution_ptr->Get_ph(); + d2 = inv_delta1[col_ph + i] / inv_delta1[i]; + d3 = d1 + d2; + if (equal(d1, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d1 = 0.0; + if (equal(d2, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d2 = 0.0; + if (equal(d3, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d3 = 0.0; + output_msg(sformatf( + "%15.15s %12.3e +%12.3e =%12.3e\n", "pH", + (double) d1, (double) d2, (double) d3)); + if (inv_ptr->ph_uncertainties[i] > 0) + { + scaled_error += fabs(d2) / inv_ptr->ph_uncertainties[i]; +/* debug + output_msg(sformatf( "%e\t%e\t%e\n", fabs(d2) / inv_ptr->ph_uncertainties[i], fabs(d2), inv_ptr->ph_uncertainties[i])); + */ + } + else if (d2 != 0.0) + { + error_msg("Computing delta pH/uncertainty", CONTINUE); + } + } + for (j = 0; j < inv_ptr->count_elts; j++) + { + if (inv_ptr->elts[j].master->s == s_eminus) + continue; + d1 = inv_ptr->elts[j].master->total; + d2 = inv_delta1[col_epsilon + j * inv_ptr->count_solns + + i] / inv_delta1[i]; + d3 = d1 + d2; + + if (equal(d1, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d1 = 0.0; + if (equal(d2, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d2 = 0.0; + if (equal(d3, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d3 = 0.0; + + output_msg(sformatf( + "%15.15s %12.3e +%12.3e =%12.3e\n", + inv_ptr->elts[j].master->elt->name, (double) d1, + (double) d2, (double) d3)); + if (equal(d1, 0.0, MIN_TOTAL_INVERSE) == FALSE) + { + d3 = fabs(d2 / d1); + if (d3 > max_pct) + max_pct = d3; + } + d4 = 0; + if (inv_ptr->elts[j].uncertainties[i] > 0) + { + d4 = fabs(inv_ptr->elts[j].uncertainties[i] * d1); + } + else if (inv_ptr->elts[j].uncertainties[i] < 0) + { + d4 = -inv_ptr->elts[j].uncertainties[i]; + } + if (d4 > 0) + { + scaled_error += fabs(d2) / d4; +/* debug + output_msg(sformatf( "%e\t%e\t%e\n", fabs(d2) / d4, fabs(d2), d4)); + */ + } + else if (d2 != 0.0) + { + error_msg("Computing delta element/uncertainty", CONTINUE); + } + } + if (inv_ptr->count_isotopes > 0) + { + /* adjustments to solution isotope composition */ + for (j = 0; j < inv_ptr->count_isotope_unknowns; j++) + { + std::map < std::string, cxxSolutionIsotope >::iterator kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + if (inv_ptr->isotope_unknowns[j].elt_name != + string_hsave(kit->second.Get_elt_name().c_str()) || + inv_ptr->isotope_unknowns[j].isotope_number != + kit->second.Get_isotope_number()) + continue; + d1 = kit->second.Get_ratio(); + d2 = inv_delta1[col_isotopes + + i * inv_ptr->count_isotope_unknowns + + j] / inv_delta1[i]; + d3 = d1 + d2; + + if (equal(d1, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d1 = 0.0; + if (equal(d2, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d2 = 0.0; + if (equal(d3, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d3 = 0.0; + sprintf(token, "%d%s", + (int) inv_ptr->isotope_unknowns[j]. + isotope_number, + inv_ptr->isotope_unknowns[j].elt_name); + output_msg(sformatf( + "%15.15s %12g +%12g =%12g\n", token, + (double) d1, (double) d2, (double) d3)); +/* + if (equal(d1, 0.0, MIN_TOTAL_INVERSE) == FALSE ) { + d3 = fabs(d2/d1); + if (d3 > max_pct) max_pct = d3; + } + */ + if (kit->second.Get_x_ratio_uncertainty() > 0) + { + scaled_error += + fabs(d2) / + kit->second.Get_x_ratio_uncertainty(); +/* debug + output_msg(sformatf( "%e\t%e\t%e\n", fabs(d2) / solution_ptr->isotopes[k].x_ratio_uncertainty , fabs(d2), solution_ptr->isotopes[k].x_ratio_uncertainty)); + */ + } + else if (d2 != 0.0) + { + error_msg + ("Computing delta solution isotope/uncertainty", + CONTINUE); + } + } + } + } + } + +/* + * Adjustments to phases + */ + print_msg = FALSE; + if (inv_ptr->count_isotopes > 0) + { + output_msg(sformatf( "\nIsotopic composition of phases:\n")); + for (i = 0; i < inv_ptr->count_phases; i++) + { + if (inv_ptr->phases[i].count_isotopes == 0) + continue; + j = col_phases + i; + if (equal(inv_delta1[j], 0.0, toler) == TRUE && + equal(min_delta[j], 0.0, toler) == TRUE && + equal(max_delta[j], 0.0, toler) == TRUE) + continue; + isotope_ptr = inv_ptr->phases[i].isotopes; + for (j = 0; j < inv_ptr->count_isotopes; j++) + { + for (k = 0; k < inv_ptr->phases[i].count_isotopes; k++) + { + if (inv_ptr->isotopes[j].elt_name != + isotope_ptr[k].elt_name || + inv_ptr->isotopes[j].isotope_number != + isotope_ptr[k].isotope_number) + continue; + d1 = isotope_ptr[k].ratio; + column = + col_phase_isotopes + i * inv_ptr->count_isotopes + j; + if (inv_delta1[col_phases + i] != 0.0) + { + d2 = inv_delta1[column] / inv_delta1[col_phases + i]; + } + else + { + continue; + } + d3 = d1 + d2; + if (equal(d1, 0.0, 1e-7) == TRUE) + d1 = 0.0; + if (equal(d2, 0.0, 1e-7) == TRUE) + d2 = 0.0; + if (equal(d3, 0.0, 1e-7) == TRUE) + d3 = 0.0; + sprintf(token, "%d%s %s", + (int) inv_ptr->isotopes[j].isotope_number, + inv_ptr->isotopes[j].elt_name, + inv_ptr->phases[i].phase->name); + output_msg(sformatf( + "%15.15s %12g +%12g =%12g", token, + (double) d1, (double) d2, (double) d3)); + if (fabs(d2) > (isotope_ptr[k].ratio_uncertainty + toler)) + { + output_msg(sformatf( " **")); + print_msg = TRUE; + } + output_msg(sformatf( "\n")); + if (isotope_ptr[k].ratio_uncertainty > 0) + { + scaled_error += + fabs(d2) / isotope_ptr[k].ratio_uncertainty; +/* debug + output_msg(sformatf( "%e\t%e\t%e\n", fabs(d2) / isotope_ptr[k].ratio_uncertainty, fabs(d2), isotope_ptr[k].ratio_uncertainty)); + */ + } + else if (d2 != 0.0) + { + error_msg + ("Computing delta phase isotope/uncertainty", + CONTINUE); + } + } + } + } + } + if (print_msg == TRUE) + { + output_msg(sformatf( + "\n**\tWARNING: The adjustment to at least one isotopic" + "\n\tcomposition of a phase exceeded the specified uncertainty" + "\n\tfor the phase. If the phase is not constrained to dissolve" + "\n\tor precipitate, then the isotopic composition of the phase" + "\n\tis also unconstrained.\n")); + } + output_msg(sformatf( "\n%-20.20s %7s %12.12s %12.12s\n", + "Solution fractions:", " ", "Minimum", "Maximum")); + for (i = 0; i < inv_ptr->count_solns; i++) + { + d1 = inv_delta1[i]; + d2 = min_delta[i]; + d3 = max_delta[i]; + if (equal(d1, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d1 = 0.0; + if (equal(d2, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d2 = 0.0; + if (equal(d3, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d3 = 0.0; + output_msg(sformatf( "%11s%4d %12.3e %12.3e %12.3e\n", + "Solution", inv_ptr->solns[i], (double) d1, (double) d2, + (double) d3)); + } + + output_msg(sformatf( "\n%-25.25s %2s %12.12s %12.12s\n", + "Phase mole transfers:", " ", "Minimum", "Maximum")); + for (i = col_phases; i < col_redox; i++) + { + if (equal(inv_delta1[i], 0.0, toler) == TRUE && + equal(min_delta[i], 0.0, toler) == TRUE && + equal(max_delta[i], 0.0, toler) == TRUE) + continue; + d1 = inv_delta1[i]; + d2 = min_delta[i]; + d3 = max_delta[i]; + if (equal(d1, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d1 = 0.0; + if (equal(d2, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d2 = 0.0; + if (equal(d3, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d3 = 0.0; + output_msg(sformatf( + "%15.15s %12.3e %12.3e %12.3e %s\n", col_name[i], + (double) d1, (double) d2, (double) d3, + inv_ptr->phases[i - col_phases].phase->formula)); + } + + output_msg(sformatf( "\n%-25.25s\n", "Redox mole transfers:")); + for (i = col_redox; i < col_epsilon; i++) + { + if (equal(inv_delta1[i], 0.0, toler) == TRUE) + continue; + output_msg(sformatf( "%15.15s %12.3e\n", col_name[i], + (double) inv_delta1[i])); + } + + output_msg(sformatf( + "\nSum of residuals (epsilons in documentation): %12.3e\n", + ((double) (error / SCALE_EPSILON)))); + output_msg(sformatf( + "Sum of delta/uncertainty limit: %12.3e\n", + (double) scaled_error)); + output_msg(sformatf( + "Maximum fractional error in element concentration: %12.3e\n", + (double) max_pct)); +/* + * Flush buffer after each model + */ + output_flush(); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_model_heading(struct inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ + /* + * Prints model headings to selected output file + */ + int i; + char token[MAX_LENGTH]; + + std::map < int, SelectedOutput >::iterator so_it = SelectedOutput_map.begin(); + for ( ; so_it != SelectedOutput_map.end(); so_it++) + { + // set punch file + current_selected_output = &(so_it->second); + if (pr.punch == FALSE || + current_selected_output == NULL || + !current_selected_output->Get_inverse() || + !current_selected_output->Get_active()) + continue; + phrq_io->Set_punch_ostream(current_selected_output->Get_punch_ostream()); + + int l = (!current_selected_output->Get_high_precision()) ? 15 : 20; + inverse_heading_names.clear(); + /* + * Print sum of residuals and maximum fractional error + */ + inverse_heading_names.push_back(sformatf("%*s\t", l, "Sum_resid")); + inverse_heading_names.push_back(sformatf("%*s\t", l, "Sum_Delta/U")); + inverse_heading_names.push_back(sformatf("%*s\t", l, "MaxFracErr")); + + /* + * Print solution numbers + */ + for (i = 0; i < inv_ptr->count_solns; i++) + { + sprintf(token, "Soln_%d", inv_ptr->solns[i]); + std::string tok1(token); + tok1.append("_min"); + std::string tok2(token); + tok2.append("_max"); + + inverse_heading_names.push_back(sformatf("%*s\t", l, token)); + inverse_heading_names.push_back(sformatf("%*s\t", l, tok1.c_str())); + inverse_heading_names.push_back(sformatf("%*s\t", l, tok2.c_str())); + } + /* + * Print phase names + */ + for (i = col_phases; i < col_redox; i++) + { + + std::string tok1(col_name[i]); + tok1.append("_min"); + std::string tok2(col_name[i]); + tok2.append("_max"); + + inverse_heading_names.push_back(sformatf("%*s\t", l, col_name[i])); + inverse_heading_names.push_back(sformatf("%*s\t", l, tok1.c_str())); + inverse_heading_names.push_back(sformatf("%*s\t", l, tok2.c_str())); + } + + size_t j; + for (j = 0; j < inverse_heading_names.size(); j++) + { + fpunchf_heading(inverse_heading_names[j].c_str()); + //user_punch_headings[j] = string_hsave(heading_names[j].c_str()); + } + fpunchf_heading("\n"); + } + current_selected_output = NULL; + phrq_io->Set_punch_ostream(NULL); + /* + * Flush buffer after each model + */ + punch_flush(); + return (OK); +} +#ifdef SKIP +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_model_heading(struct inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints model headings to selected output file + */ + int i; + char token[MAX_LENGTH]; + //if (/*punch.in == FALSE ||*/ pr.punch == FALSE || punch.inverse == FALSE) + // return (OK); + std::vector heading_names; + std::map < int, SelectedOutput >::iterator so_it = SelectedOutput_map.begin(); + for ( ; so_it != SelectedOutput_map.end(); so_it++) + { + // set punch file + current_selected_output = &(so_it->second); + if (pr.punch == FALSE || + current_selected_output == NULL || + current_selected_output->punch_ostream == NULL || + !current_selected_output->Get_inverse() || + !current_selected_output->Get_active()) + continue; + phrq_io->Set_punch_ostream(current_selected_output->punch_ostream); + + int l = (!current_selected_output->Get_high_precision()) ? 15 : 20; + heading_names.clear(); + /* + * Print sum of residuals and maximum fractional error + */ + heading_names.push_back(sformatf("%*s\t", l, "Sum_resid")); + heading_names.push_back(sformatf("%*s\t", l, "Sum_Delta/U")); + heading_names.push_back(sformatf("%*s\t", l, "MaxFracErr")); + + /* + * Print solution numbers + */ + for (i = 0; i < inv_ptr->count_solns; i++) + { + sprintf(token, "Soln_%d", inv_ptr->solns[i]); + std::string tok1(token); + tok1.append("_min"); + std::string tok2(token); + tok2.append("_max"); + + heading_names.push_back(sformatf("%*s\t", l, token)); + heading_names.push_back(sformatf("%*s\t", l, tok1.c_str())); + heading_names.push_back(sformatf("%*s\t", l, tok2.c_str())); + } + /* + * Print phase names + */ + for (i = col_phases; i < col_redox; i++) + { + + std::string tok1(col_name[i]); + tok1.append("_max"); + std::string tok2(col_name[i]); + tok2.append("_max"); + + heading_names.push_back(sformatf("%*s\t", l, col_name[i])); + heading_names.push_back(sformatf("%*s\t", l, tok1.c_str())); + heading_names.push_back(sformatf("%*s\t", l, tok2.c_str())); + + } + + size_t j; + + // punch headings + //user_punch_count_headings = (int) heading_names.size(); + //user_punch_headings = (const char **) PHRQ_realloc(user_punch_headings, + // (size_t) (user_punch_count_headings + 1) * sizeof(char *)); + //if (user_punch_headings == NULL) + // malloc_error(); + + for (j = 0; j < heading_names.size(); j++) + { + fpunchf_heading(heading_names[j].c_str()); + //user_punch_headings[j] = string_hsave(heading_names[j].c_str()); + } + fpunchf_heading("\n"); + } + current_selected_output = NULL; + phrq_io->Set_punch_ostream(NULL); + inverse_heading_names = heading_names; +/* + * Flush buffer after each model + */ + punch_flush(); + return (OK); +} +#endif +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_model(struct inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints model to selected output file + */ + int i; + LDBLE d1, d2, d3; + //if (punch.in == FALSE || pr.punch == FALSE || punch.inverse == FALSE) + // return (OK); + + UserPunch temp_user_punch; + current_user_punch = & temp_user_punch; + temp_user_punch.Set_headings(inverse_heading_names); + + std::map < int, SelectedOutput >::iterator so_it = SelectedOutput_map.begin(); + for ( ; so_it != SelectedOutput_map.end(); so_it++) + { + current_selected_output = &(so_it->second); + if (pr.punch == FALSE || + current_selected_output == NULL || + !current_selected_output->Get_inverse() || + !current_selected_output->Get_active()) + continue; + phrq_io->Set_punch_ostream(current_selected_output->Get_punch_ostream()); + + n_user_punch_index = 0; + /* + * write residual info + */ + if (!current_selected_output->Get_high_precision()) + { + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%15.4e\t", (double) (error / SCALE_EPSILON)); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%15.4e\t", (double) scaled_error); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%15.4e\t", (double) max_pct); + } + else + { + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%20.12e\t", (double) (error / SCALE_EPSILON)); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%20.12e\t", (double) scaled_error); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%20.12e\t", (double) max_pct); + } + /* + * write solution fractions + */ + for (i = 0; i < inv_ptr->count_solns; i++) + { + d1 = inv_delta1[i]; + d2 = min_delta[i]; + d3 = max_delta[i]; + if (equal(d1, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d1 = 0.0; + if (equal(d2, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d2 = 0.0; + if (equal(d3, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d3 = 0.0; + if (!current_selected_output->Get_high_precision()) + { + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%15.4e\t", (double) d1); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%15.4e\t", (double) d2); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%15.4e\t", (double) d3); + } + else + { + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%20.12e\t", (double) d1); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%20.12e\t", (double) d2); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%20.12e\t", (double) d3); + } + } + /* + * write phase transfers + */ + for (i = col_phases; i < col_redox; i++) + { + d1 = inv_delta1[i]; + d2 = min_delta[i]; + d3 = max_delta[i]; + if (equal(d1, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d1 = 0.0; + if (equal(d2, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d2 = 0.0; + if (equal(d3, 0.0, MIN_TOTAL_INVERSE) == TRUE) + d3 = 0.0; + if (!current_selected_output->Get_high_precision()) + { + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%15.4e\t", (double) d1); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%15.4e\t", (double) d2); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%15.4e\t", (double) d3); + } + else + { + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%20.12e\t", (double) d1); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%20.12e\t", (double) d2); + fpunchf(trim(inverse_heading_names[n_user_punch_index++]).c_str(), "%20.12e\t", (double) d3); + } + } + punch_msg("\n"); + + /* + * Flush buffer after each model + */ + punch_flush(); + } + current_selected_output = NULL; + phrq_io->Set_punch_ostream(NULL); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +unsigned long Phreeqc:: +set_bit(unsigned long bits, int position, int value) +/* ---------------------------------------------------------------------- */ +{ +/* + * Sets a single bit + */ + unsigned long temp_bits_l; + + temp_bits_l = 1 << position; + if (value == 0) + { + temp_bits_l = ~temp_bits_l; + temp_bits_l = bits & temp_bits_l; + } + else + { + temp_bits_l = bits | temp_bits_l; + } + return (temp_bits_l); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +next_set_phases(struct inverse *inv_ptr, + int first_of_model_size, int model_size) +/* ---------------------------------------------------------------------- */ +{ + int i, j, k; + unsigned long temp_bits_l; + +/* + * min_ and max_position are arrays, logically with length + * of model_size, that contain minimum and maximum + * phase numbers that can be in that model position. + * + * now contains a list of phase numbers to mark as in for this + * model + */ + +/* + * Initialize for a given model_size + */ + if (first_of_model_size == TRUE) + { + for (i = 0; i < model_size; i++) + { + min_position[i] = i; + now[i] = i; + max_position[i] = inv_ptr->count_phases - model_size + i; + } + } + else + { +/* + * Determine next combination of phases for fixed model_size + */ + for (i = (model_size - 1); i >= 0; i--) + { + if (now[i] < max_position[i]) + { + now[i]++; + if (i < (model_size - 1)) + { + k = now[i]; + for (j = (i + 1); j < model_size; j++) + { + k++; + now[j] = k; + } + } + break; + } + } + if (i < 0) + return (FALSE); + } +/* + * Set bits which switch in phases + */ + temp_bits_l = 0; + for (j = 0; j < model_size; j++) + { + temp_bits_l += (1 << now[j]); + } + phase_bits = temp_bits_l; + return (TRUE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +range(struct inverse *inv_ptr, unsigned long cur_bits) +/* ---------------------------------------------------------------------- */ +{ +/* + * Takes the model from cur_bits and sequentially determines the + * minimum and maximum values for each solution fraction and + * each phase mass transfer. + */ + int i, j; + int k, l, m, n; + int f; + unsigned long bits; + LDBLE error2; +/* + * Include forced solutions and phases in range calculation + */ + for (i = 0; i < inv_ptr->count_solns + inv_ptr->count_phases; i++) + { + if (i < inv_ptr->count_phases) + { + if (inv_ptr->phases[i].force == TRUE) + { + cur_bits = set_bit(cur_bits, i, 1); + } + } + else + { + if (inv_ptr->force_solns[i - inv_ptr->count_phases] == TRUE) + { + cur_bits = set_bit(cur_bits, i, 1); + } + } + } + + memcpy((void *) &(min_delta[0]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + memcpy((void *) &(max_delta[0]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); +/* + * Switch bits so that phases are high and solutions are low + */ + bits = + get_bits(cur_bits, inv_ptr->count_phases + inv_ptr->count_solns - 1, + inv_ptr->count_solns); + bits += + (get_bits(cur_bits, inv_ptr->count_phases - 1, inv_ptr->count_phases) + << inv_ptr->count_solns); +/* + * Do range calculation + */ + for (i = 0; i < inv_ptr->count_solns + inv_ptr->count_phases; i++) + { + if (inv_ptr->count_solns == i + 1) + { + min_delta[i] = 1.0; + max_delta[i] = 1.0; + continue; + } + if (get_bits(bits, i, 1) == 0) + continue; +/* + * Calculate min and max + */ + for (f = -1; f < 2; f += 2) + { + k = row_mb; /* rows in A */ + l = row_epsilon - row_mb; /* rows in C */ + m = count_rows - row_epsilon; /* rows in E */ + n = count_unknowns; /* number of variables */ +/* + * Copy equations + */ + memcpy((void *) &(array1[0]), (void *) &(array[0]), + (size_t) max_column_count * max_row_count * sizeof(LDBLE)); + memcpy((void *) &(delta2[0]), (void *) &(delta[0]), + (size_t) max_column_count * sizeof(LDBLE)); + memcpy((void *) &(delta3[0]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + memcpy((void *) &(delta_save[0]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + memcpy((void *) &(inv_res[0]), (void *) &(inv_zero[0]), + (size_t) max_row_count * sizeof(LDBLE)); + +/* + * Change optimization + */ + for (j = 0; j < k; j++) + { + memcpy((void *) &(array1[j * max_column_count]), + (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + } + array1[i] = 1.0; + if (f < 1) + { + array1[n] = -fabs(inv_ptr->range_max); + } + else + { + array1[n] = fabs(inv_ptr->range_max); + } + shrink(inv_ptr, array1, array1, + &k, &l, &m, &n, cur_bits, delta2, col_back, row_back); + /* + * Save delta constraints + */ + for (j = 0; j < n; j++) + { + delta_save[col_back[j]] = delta2[j]; + } + if (debug_inverse == TRUE) + { + output_msg(sformatf( "\nInput delta:\n\n")); + for (j = 0; j < n; j++) + { + output_msg(sformatf( "\t%d %s\t%g\n", j, + col_name[col_back[j]], (double) delta2[j])); + } + output_msg(sformatf( "\nA and B arrays:\n\n")); + array_print(array1, k + l + m, n + 1, max_column_count); + } + kode = 1; + iter = 200; + count_calls++; +#ifdef INVERSE_CL1MP + if (inv_ptr->mp == TRUE) + { + cl1mp(k, l, m, n, + nklmd, n2d, array1, + &kode, inv_ptr->mp_tolerance, &iter, + delta2, inv_res, &error2, inv_cu, inv_iu, inv_is, TRUE, + inv_ptr->mp_censor); + } + else + { + cl1(k, l, m, n, + nklmd, n2d, array1, + &kode, toler, &iter, delta2, inv_res, &error2, inv_cu, inv_iu, inv_is, + TRUE); + } +#else + cl1(k, l, m, n, + nklmd, n2d, array1, + &kode, toler, &iter, delta2, inv_res, &error2, inv_cu, inv_iu, inv_is, TRUE); +#endif + if (kode != 0) + { + output_msg(sformatf( + "Error in subroutine range. Kode = %d\n", kode)); + } + + if (debug_inverse == TRUE) + { + output_msg(sformatf( "kode: %d\titer: %d\terror: %e\n", + kode, iter, (double) error2)); + output_msg(sformatf( "k, l, m, n: %d\t%d\t%d\t%d\n", k, + l, m, n)); + output_msg(sformatf( "\nsolution vector %s\n", + col_name[i])); + for (j = 0; j < n; j++) + { + output_msg(sformatf( "%6d %-12.12s %10.2e", j, + col_name[col_back[j]], (double) delta2[j])); + output_msg(sformatf( "\n")); + } + + output_msg(sformatf( "\nresidual vector:\n")); + for (j = 0; j < (k + l + m); j++) + { + output_msg(sformatf( "%6d %-12.12s %10.2e\n", j, + row_name[row_back[j]], (double) inv_res[j])); + } + } + for (j = 0; j < n; j++) + { + if (col_back[j] == i) + break; + } + if (f < 0) + { + min_delta[i] = delta2[j]; + } + else + { + max_delta[i] = delta2[j]; + } + for (j = 0; j < n; j++) + { + delta3[col_back[j]] = delta2[j]; + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +shrink(struct inverse *inv_ptr, LDBLE * array_in, LDBLE * array_out, + int *k, int *l, int *m, int *n, + unsigned long cur_bits, + LDBLE * delta_l, int *col_back_l, int *row_back_l) +/* ---------------------------------------------------------------------- */ +{ +/* + * Shrink eliminates any rows that are all zeros and any columns + * that are not in cur_bits, result is put in array_out + * + * k, l, m, n return the new sizes of the array. + * delta is remapped to retain any non-negativity constraints + * Col_back maps columns that remain back to original columns + * Row_back maps rows that remain back to original rows + */ + int i, j, row; + int k1, l1, m1; + int cur_col, column; + int nonzero; +/* + * Copy array_in to array_out + */ + if (array_in != array_out) + { + for (i = 0; i < (*k + *l + *m); i++) + { + memcpy(&(array_out[i * max_column_count]), + &(array_in[i * max_column_count]), + (size_t) max_column_count * sizeof(LDBLE)); + } + } +/* + * Determine columns to eliminate + */ + for (i = 0; i < (*n + 1); i++) + col_back_l[i] = i; + +/* + * Drop phases not in model + */ + for (i = 0; i < inv_ptr->count_phases; i++) + { + if (get_bits(cur_bits, i, 1) == 0) + { + col_back_l[col_phases + i] = -1; + /* drop isotopes */ + if (inv_ptr->count_isotopes > 0) + { + for (j = 0; j < inv_ptr->count_isotopes; j++) + { + column = + col_phase_isotopes + i * inv_ptr->count_isotopes + j; + col_back_l[column] = -1; + } + } + } + } +/* + * Drop solutions not in model + */ + for (i = 0; i < (inv_ptr->count_solns - 1); i++) + { + if (get_bits(cur_bits, inv_ptr->count_phases + i, 1) == 0) + { + col_back_l[i] = -1; + /* drop all epsilons for the solution */ + for (j = 0; j < inv_ptr->count_elts; j++) + { + column = col_epsilon + j * inv_ptr->count_solns + i; + col_back_l[column] = -1; + } + /* drop pH for the solution */ + if (inv_ptr->carbon == TRUE) + { + column = col_ph + i; + col_back_l[column] = -1; + } + /* drop isotopes */ + if (inv_ptr->count_isotopes > 0) + { + for (j = 0; j < inv_ptr->count_isotope_unknowns; j++) + { + column = + col_isotopes + i * inv_ptr->count_isotope_unknowns + + j; + col_back_l[column] = -1; + } + } + } + } + +/* + * Drop epsilons not used + */ + for (i = col_epsilon; i < *n; i++) + { + if (col_back_l[i] < 0) + continue; + for (j = 0; j < (*k + *l + *m); j++) + { + if (array_out[j * max_column_count + i] != 0) + break; + } + if (j == (*k + *l + *m)) + { + col_back_l[i] = -1; + } + } +/* + * rewrite array_out + */ + cur_col = 0; + for (i = 0; i < (*n + 1); i++) + { + if (col_back_l[i] < 0) + continue; + if (cur_col == col_back_l[i]) + { + cur_col++; + continue; + } + for (j = 0; j < (*k + *l + *m); j++) + { + array_out[j * max_column_count + cur_col] = + array_out[j * max_column_count + i]; + } + col_back_l[cur_col] = col_back_l[i]; + delta_l[cur_col] = delta_l[i]; + cur_col++; + } + *n = cur_col - 1; +/* + * Eliminate unnecessary optimization eqns + */ + row = 0; + k1 = 0; + for (i = 0; i < *k; i++) + { + if (memcmp(&(array_out[i * max_column_count]), &(inv_zero[0]), + (size_t) (*n) * sizeof(LDBLE)) == 0) + { + continue; + } +/* + memcpy(&(array_out[row * max_column_count]), &(array_out[i * max_column_count]), + (size_t) max_column_count * sizeof(LDBLE)); + */ + if (i > row) + { + if ((row*max_column_count + (*n + 1)) >= (i * max_column_count)) + { + assert(false); + } + + + memcpy(&(array_out[row * max_column_count]), + &(array_out[i * max_column_count]), + (size_t) (*n + 1) * sizeof(LDBLE)); + } + row_back_l[row] = i; + row++; + k1++; + } + +/* + * Eliminate unnecessary equality eqns + */ + l1 = 0; + for (i = *k; i < (*k + *l); i++) + { + nonzero = FALSE; + for (j = 0; j < *n; j++) + { + if (equal(array_out[i * max_column_count + j], 0.0, toler) == + FALSE) + { + nonzero = TRUE; + break; + } + } + if (nonzero == FALSE) + continue; +/* + if (memcmp(&(array_out[i * max_column_count]), &(zero[0]), + (size_t) (*n) * sizeof(LDBLE)) == 0) { + continue; + } + */ +/* + memcpy(&(array_out[row * max_column_count]), &(array_out[i * max_column_count]), + (size_t) max_column_count * sizeof(LDBLE)); + */ + + if (i > row) + { + if ((row*max_column_count + (*n + 1)) >= (i * max_column_count)) + { + assert(false); + } + memcpy(&(array_out[row * max_column_count]), + &(array_out[i * max_column_count]), + (size_t) (*n + 1) * sizeof(LDBLE)); + } + row_back_l[row] = i; + row++; + l1++; + } +/* + * Eliminate unnecessary inequality eqns + */ + m1 = 0; + for (i = (*k + *l); i < (*k + *l + *m); i++) + { + nonzero = FALSE; + for (j = 0; j < *n; j++) + { + if (equal(array_out[i * max_column_count + j], 0.0, toler) == + FALSE) + { + nonzero = TRUE; + break; + } + } + if (nonzero == FALSE) + continue; +/* + if (memcmp(&(array_out[i * max_column_count]), &(zero[0]), + (size_t) (*n) * sizeof(LDBLE)) == 0) { + continue; + } + */ +/* + memcpy(&(array_out[row * max_column_count]), &(array_out[i * max_column_count]), + (size_t) max_column_count * sizeof(LDBLE)); + */ + + if (i > row) + { + if ((row*max_column_count + (*n + 1)) >= (i * max_column_count)) + { + assert(false); + } + memcpy(&(array_out[row * max_column_count]), + &(array_out[i * max_column_count]), + (size_t) (*n + 1) * sizeof(LDBLE)); + } + row_back_l[row] = i; + row++; + m1++; + } + + *k = k1; + *l = l1; + *m = m1; +/* + * Scale all inequality rows + */ + + for (i = *k + *l; i < *k + *l + *m; i++) + { + for (j = 0; j < *n + 1; j++) + { + array_out[i * max_column_count + j] *= SCALE_ALL; + } + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_solns(struct inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Check_solns checks that each solution can be charge balanced within + * the given constraints. If not, it is an error and the program will + * terminate. + */ + int i, j; + int k, l, m, n; + int return_value; + unsigned long bits; + LDBLE error2; + + memcpy((void *) &(min_delta[0]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + memcpy((void *) &(max_delta[0]), (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + +/* + * Switch bits so that phases are high and solutions are low + */ + return_value = OK; + for (i = 0; i < inv_ptr->count_solns; i++) + { + bits = 0; + bits += 1 << (inv_ptr->count_phases + i); +/* + * Check for feasibility of charge balance with given uncertainties + */ + k = row_mb; /* rows in A */ + l = row_epsilon - row_mb; /* rows in C */ + m = count_rows - row_epsilon; /* rows in E */ + n = count_unknowns; /* number of variables */ +/* debug + output_msg(sformatf( "\nColumns\n")); + for (j = 0; j < n; j++) { + output_msg(sformatf( "\t%d\t%s\n", j, col_name[j])); + } + + output_msg(sformatf( "\nRows\n")); + for (j = 0; j < k + l + m; j++) { + output_msg(sformatf( "\t%d\t%s\n", j, row_name[j])); + } + + output_msg(sformatf( "\nA and B arrays:\n\n")); + array_print(array, k + l + m, + n + 1, max_column_count); + */ +/* + * Copy equations + */ + memcpy((void *) &(array1[0]), (void *) &(array[0]), + (size_t) max_column_count * max_row_count * sizeof(LDBLE)); + memcpy((void *) &(delta2[0]), (void *) &(delta[0]), + (size_t) max_column_count * sizeof(LDBLE)); + memcpy((void *) &(inv_res[0]), (void *) &(inv_zero[0]), + (size_t) max_row_count * sizeof(LDBLE)); + +/* + * Keep optimization + */ +/* + * Zero out mass balance rows and fraction rows + */ + for (j = row_mb; j < row_charge; j++) + { + memcpy((void *) &(array1[j * max_column_count]), + (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + } +/* + * Set fraction of solution to 1.0 + */ + array1[(row_charge - 1) * max_column_count + i] = 1.0; + array1[(row_charge - 1) * max_column_count + n] = 1.0; + +/* + * Zero out charge balance rows for other solutions + */ + for (j = 0; j < inv_ptr->count_solns; j++) + { + if (j == i) + continue; + memcpy((void *) &(array1[(row_charge + j) * max_column_count]), + (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + } + +/* + * Zero out isotope mole balance + */ + for (j = row_isotopes; j < row_epsilon; j++) + { + memcpy((void *) &(array1[j * max_column_count]), + (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + } + +/* + * Zero out isotope uncertainties + */ + for (j = row_isotope_epsilon; j < count_rows; j++) + { + memcpy((void *) &(array1[j * max_column_count]), + (void *) &(inv_zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); + } +/* + * Can`t Zero out epsilon constraint rows for other solutions because not sure which + * are which + */ + + shrink(inv_ptr, array1, array1, + &k, &l, &m, &n, bits, delta2, col_back, row_back); +/* Debug + + output_msg(sformatf( "\nColumns\n")); + for (j = 0; j < n; j++) { + output_msg(sformatf( "\t%d\t%s\n", j, col_name[col_back[j]])); + } + + output_msg(sformatf( "\nRows\n")); + for (j = 0; j < k + l + m; j++) { + output_msg(sformatf( "\t%d\t%s\n", j, row_name[row_back[j]])); + } + + output_msg(sformatf( "\nA and B arrays:\n\n")); + array_print(array1, k + l + m, + n + 1, max_column_count); + + output_msg(sformatf( "\nInput delta vector:\n")); + for (j=0; j < n; j++) { + output_msg(sformatf( "%6d %-12.12s %10.2e", j, col_name[col_back[j]], delta2[j])); + output_msg(sformatf( "\n")); + } + */ + + kode = 1; + iter = 200; + count_calls++; + cl1(k, l, m, n, + nklmd, n2d, array1, + &kode, toler, &iter, delta2, inv_res, &error2, inv_cu, inv_iu, inv_is, TRUE); + + if (kode != 0) + { + error_string = sformatf( + "Not possible to balance solution %d with input uncertainties.", + inv_ptr->solns[i]); + error_msg(error_string, CONTINUE); + return_value = ERROR; + } + +/* Debug + output_msg(sformatf( "kode: %d\titer: %d\terror: %e\n", kode, iter, error)); + output_msg(sformatf( "k, l, m, n: %d\t%d\t%d\t%d\n", k, l, m, n)); + + output_msg(sformatf( "\nsolution vector %s\n", col_name[i])); + for (j = 0; j < n; j++) { + output_msg(sformatf( "%6d %-12.12s %10.2e", j, col_name[col_back[j]], delta2[j])); + output_msg(sformatf( "\n")); + } + + output_msg(sformatf( "\nresidual vector:\n")); + for (j = 0; j < (k + l + m); j++) { + output_msg(sformatf( "%6d %-12.12s %10.2e\n", j, row_name[row_back[j]], inv_res[j])); + } + */ + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +post_mortem(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Post_mortem simply identifies which equality and inequality of the + * array have not been satisfied. + * + */ + int i, j; + LDBLE sum; +/* + * Check equalities + */ + output_msg(sformatf( + "\nPost_mortem examination of inverse modeling:\n\n")); + for (i = row_mb; i < row_epsilon; i++) + { + sum = 0; + for (j = 0; j < count_unknowns; j++) + { + sum += inv_delta1[j] * array[i * max_column_count + j]; + } + + if (equal(sum, array[(i * max_column_count) + count_unknowns], toler) + == FALSE) + { + output_msg(sformatf( + "\tERROR: equality not satisfied for %s, %e.\n", + row_name[i], + (double) (sum - array[(i * max_column_count) + count_unknowns]))); + } + } +/* + * Check inequalities + */ + for (i = row_epsilon; i < count_rows; i++) + { + sum = 0; + for (j = 0; j < count_unknowns; j++) + { + sum += inv_delta1[j] * array[i * max_column_count + j]; + } + + if (sum > array[(i * max_column_count) + count_unknowns] + toler) + { + output_msg(sformatf( + "\tERROR: inequality not satisfied for %s, %e\n", + row_name[i], + (double) (sum - array[(i * max_column_count) + count_unknowns]))); + } + } +/* + * Check dissolution/precipitation constraints + */ + for (i = 0; i < count_unknowns; i++) + { + if (delta_save[i] > 0.5 && inv_delta1[i] < -toler) + { + output_msg(sformatf( + "\tERROR: Dissolution/precipitation constraint not satisfied for column %d, %s, %e.\n", + i, col_name[i], (double) inv_delta1[i])); + } + else if (delta_save[i] < -0.5 && inv_delta1[i] > toler) + { + output_msg(sformatf( + "\tERROR: Dissolution/precipitation constraint not satisfied for column %d, %s, %e.\n", + i, col_name[i], (double) inv_delta1[i])); + } + } + + return (OK); +} +/* ---------------------------------------------------------------------- */ +bool Phreeqc:: +test_cl1_solution(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * checks that equality and inequalities are satisfied + * + */ + int i, j; + LDBLE sum; +/* + * Check equalities + */ + bool rv = true; + if (debug_inverse) + { + output_msg(sformatf( + "\nTesting cl1 inverse modeling:\n\n")); + } + for (i = row_mb; i < row_epsilon; i++) + { + sum = 0; + for (j = 0; j < count_unknowns; j++) + { + sum += inv_delta1[j] * array[i * max_column_count + j]; + } + + if (equal(sum, array[(i * max_column_count) + count_unknowns], toler) == FALSE) + { + if (debug_inverse) + { + output_msg(sformatf("\tERROR: equality not satisfied for %s, %e.\n", row_name[i], + (double) (sum - array[(i * max_column_count) + count_unknowns]))); + } + rv = false; + } + } +/* + * Check inequalities + */ + for (i = row_epsilon; i < count_rows; i++) + { + sum = 0; + for (j = 0; j < count_unknowns; j++) + { + sum += inv_delta1[j] * array[i * max_column_count + j]; + } + + if (sum > array[(i * max_column_count) + count_unknowns] + toler) + { + if (debug_inverse) + { + output_msg(sformatf( + "\tERROR: inequality not satisfied for %s, %e\n", + row_name[i], + (double) (sum - array[(i * max_column_count) + count_unknowns]))); + } + rv = false; + } + } +/* + * Check dissolution/precipitation constraints + */ + for (i = 0; i < count_unknowns; i++) + { + if (delta_save[i] > 0.5 && inv_delta1[i] < -toler) + { + if (debug_inverse) + { + output_msg(sformatf( + "\tERROR: Dissolution/precipitation constraint not satisfied for column %d, %s, %e.\n", + i, col_name[i], (double) inv_delta1[i])); + } + rv = false; + + } + else if (delta_save[i] < -0.5 && inv_delta1[i] > toler) + { + if (debug_inverse) + { + output_msg(sformatf( + "\tERROR: Dissolution/precipitation constraint not satisfied for column %d, %s, %e.\n", + i, col_name[i], (double) inv_delta1[i])); + } + rv = false; + } + } + + return (rv); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +carbon_derivs(struct inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ + int i, j, temp; + LDBLE c_uncertainty, d_carbon, alk_plus, alk_minus; + cxxSolution *solution_ptr_orig, *solution_ptr; + + inv_ptr->dalk_dph = (LDBLE *) free_check_null(inv_ptr->dalk_dph); + inv_ptr->dalk_dph = + (LDBLE *) PHRQ_malloc((size_t) inv_ptr->count_solns * sizeof(LDBLE)); + if (inv_ptr->dalk_dph == NULL) + malloc_error(); + + inv_ptr->dalk_dc = (LDBLE *) free_check_null(inv_ptr->dalk_dc); + inv_ptr->dalk_dc = + (LDBLE *) PHRQ_malloc((size_t) inv_ptr->count_solns * sizeof(LDBLE)); + if (inv_ptr->dalk_dc == NULL) + malloc_error(); + + for (i = 0; i < inv_ptr->count_solns; i++) + { + solution_ptr_orig = Utilities::Rxn_find(Rxn_solution_map, inv_ptr->solns[i]); + if (solution_ptr_orig == NULL) + { + error_string = sformatf( "Solution %d for inverse " + "modeling not found.", inv_ptr->solns[i]); + error_msg(error_string, STOP); + } +/* + * Find carbon uncertainty + */ + c_uncertainty = 0; + d_carbon = 0; + for (j = 0; j < inv_ptr->count_elts; j++) + { + if (inv_ptr->elts[j].master == s_co3->secondary) + { + c_uncertainty = inv_ptr->elts[j].uncertainties[i]; + break; + } + } + if (c_uncertainty < 0.0) + { + d_carbon = -c_uncertainty; + } + else if (c_uncertainty > 0.0) + { + cxxNameDouble::iterator kit = solution_ptr_orig->Get_totals().begin(); + for ( ; kit != solution_ptr_orig->Get_totals().end(); kit++) + { + if (strcmp(kit->first.c_str(), "C(4)") == 0) + { + d_carbon = kit->second / + solution_ptr_orig->Get_mass_water() * c_uncertainty; + break; + } + + } + } + +/* + * Make four copies of solution + * Modify ph and carbon in solutions + */ + set_ph_c(inv_ptr, i, solution_ptr_orig, -5, 0.0, 1.0, 0.0); + set_ph_c(inv_ptr, i, solution_ptr_orig, -4, 0.0, -1.0, 0.0); + if (c_uncertainty != 0) + { + set_ph_c(inv_ptr, i, solution_ptr_orig, -3, d_carbon, 0.0, 1.0); + set_ph_c(inv_ptr, i, solution_ptr_orig, -2, d_carbon, 0.0, -1.0); + } +/* */ + temp = pr.all; + pr.all = FALSE; + initial_solutions(FALSE); + pr.all = temp; +/* + * dAlk/dpH + */ + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, -5); + alk_plus = solution_ptr->Get_total_alkalinity(); + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, -4); + alk_minus = solution_ptr->Get_total_alkalinity(); + inv_ptr->dalk_dph[i] = (alk_plus - alk_minus) / + (2.0 * inv_ptr->ph_uncertainties[i]); +/* + * dAlk/dC + */ + if (d_carbon != 0) + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, -3); + alk_plus = solution_ptr->Get_total_alkalinity(); + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, -2); + alk_minus = solution_ptr->Get_total_alkalinity(); + inv_ptr->dalk_dc[i] = (alk_plus - alk_minus) / (2.0 * d_carbon); + } + else + { + inv_ptr->dalk_dc[i] = 0.0; + } + if (debug_inverse == TRUE) + { + output_msg(sformatf( "dAlk/dph = %e\tdAlk/dC = %e\n", + (double) inv_ptr->dalk_dph[i], + (double) inv_ptr->dalk_dc[i])); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_ph_c(struct inverse *inv_ptr, + int i, + cxxSolution *solution_ptr_orig, + int n_user_new, LDBLE d_carbon, LDBLE ph_factor, LDBLE c_factor) +/* ---------------------------------------------------------------------- */ +{ + int n_user_orig; + cxxSolution *solution_ptr; + + n_user_orig = inv_ptr->solns[i]; + Utilities::Rxn_copy(Rxn_solution_map, n_user_orig, n_user_new); + + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, n_user_new); + solution_ptr->Set_new_def(true); + solution_ptr->Create_initial_data(); + solution_ptr->Set_n_user_end(n_user_new); + LDBLE ph = solution_ptr->Get_ph(); + ph += inv_ptr->ph_uncertainties[i] * ph_factor; + solution_ptr->Set_ph(ph); + cxxNameDouble::iterator jit = solution_ptr->Get_totals().begin(); + for ( ; jit != solution_ptr->Get_totals().end(); jit++) + { + cxxISolutionComp temp_comp; + temp_comp.Set_description(jit->first.c_str()); + temp_comp.Set_input_conc(jit->second / solution_ptr_orig->Get_mass_water()); + temp_comp.Set_units("Mol/kgw"); + if (strcmp(jit->first.c_str(), "C(4)") == 0) + { + LDBLE c = temp_comp.Get_input_conc(); + c += d_carbon * c_factor; + temp_comp.Set_input_conc(c); + } + solution_ptr->Get_initial_data()->Get_comps()[jit->first] = temp_comp; + } + solution_ptr->Get_totals().clear(); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +isotope_balance_equation(struct inverse *inv_ptr, int row, int n) +/* ---------------------------------------------------------------------- */ +/* + * routine fills in an isotope balance equation + * + * row is the row in array that needs to be filled + * n is the isotope number in inv_ptr + */ +{ + int i, j, k; + LDBLE isotope_number; + int column; + LDBLE f; + struct master *primary_ptr; + cxxSolution *solution_ptr; +/* + * Determine primary master species and isotope number for + * isotope mass-balance equation + */ + column = 0; + primary_ptr = master_bsearch_primary(inv_ptr->isotopes[n].elt_name); + isotope_number = inv_ptr->isotopes[n].isotope_number; + /* isotope element must be defined */ + if (primary_ptr == NULL) + { + error_string = sformatf( + "In isotope calculation: element not defined: %s.", + inv_ptr->isotopes[n].elt_name); + error_msg(error_string, CONTINUE); + input_error++; + } + + /* isotope element must be primary */ + if (primary_ptr->primary != TRUE) + { + error_string = sformatf( "Isotope mass-balance may only be used" + " for total element concentrations.\n" + "Secondary species not allowed: %s.", + inv_ptr->isotopes[n].elt_name); + error_msg(error_string, CONTINUE); + input_error++; + } + +/* + * Fill in terms for each solution + */ + for (i = 0; i < inv_ptr->count_solns; i++) + { + if (i == (inv_ptr->count_solns - 1)) + { + f = -1.0; + } + else + { + f = 1.0; + } + + /* mixing fraction term */ + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, inv_ptr->solns[i]); + std::map < std::string, cxxSolutionIsotope >::iterator jit = solution_ptr->Get_isotopes().begin(); + for ( ; jit != solution_ptr->Get_isotopes().end(); jit++) + { + struct master *primary_jit = master_bsearch_primary(jit->second.Get_elt_name().c_str()); + if (primary_jit == primary_ptr && + jit->second.Get_isotope_number() == isotope_number) + { + array[row * max_column_count + i] += + f * jit->second.Get_total() * jit->second.Get_ratio(); + } + } + + /* epsilon of total moles of element valence * ratio */ + jit = solution_ptr->Get_isotopes().begin(); + for ( ; jit != solution_ptr->Get_isotopes().end(); jit++) + { + /* What to do with H and O, skip for now ??? */ + if (primary_ptr == s_hplus->primary + || primary_ptr == s_h2o->primary) + continue; + struct master *master_jit = master_bsearch(jit->second.Get_elt_name().c_str()); + struct master *primary_jit = master_bsearch_primary(jit->second.Get_elt_name().c_str()); + if (primary_jit == primary_ptr && + jit->second.Get_isotope_number() == isotope_number) + { + /* find column of master for solution i */ + for (k = 0; k < inv_ptr->count_elts; k++) + { + if (master_jit == inv_ptr->elts[k].master) + break; + } + column = col_epsilon + (k * inv_ptr->count_solns) + i; + array[row * max_column_count + column] += + f * jit->second.Get_ratio(); + } + } + + /* epsilon of ratio * total of element valence */ + jit = solution_ptr->Get_isotopes().begin(); + for ( ; jit != solution_ptr->Get_isotopes().end(); jit++) + { + struct master *master_jit = master_bsearch(jit->second.Get_elt_name().c_str()); + struct master *primary_jit = master_bsearch_primary(jit->second.Get_elt_name().c_str()); + if (primary_jit == primary_ptr && + jit->second.Get_isotope_number() == isotope_number) + { + + /* find column of epsilon for ratio of valence */ + for (k = 0; k < inv_ptr->count_isotope_unknowns; k++) + { + if (master_jit == + inv_ptr->isotope_unknowns[k].master + && jit->second.Get_isotope_number() == + inv_ptr->isotope_unknowns[k].isotope_number) + { + column = + col_isotopes + + (i * inv_ptr->count_isotope_unknowns) + k; + } + } + array[row * max_column_count + column] += + f * jit->second.Get_total(); + } + } + } +/* + * Fill in terms for each phase + */ + for (i = 0; i < inv_ptr->count_phases; i++) + { + if (inv_ptr->phases[i].count_isotopes <= 0) + continue; + struct isotope *isotope_ptr = inv_ptr->phases[i].isotopes; + for (j = 0; j < inv_ptr->phases[i].count_isotopes; j++) + { + if (isotope_ptr[j].primary == primary_ptr && + isotope_ptr[j].isotope_number == isotope_number) + { + /* term for alpha phase unknowns */ + column = col_phases + i; + array[row * max_column_count + column] = + isotope_ptr[j].ratio * isotope_ptr[j].coef; + /* term for phase isotope uncertainty unknown */ + column = col_phase_isotopes + i * inv_ptr->count_isotopes + n; + array[row * max_column_count + column] = isotope_ptr[j].coef; + break; + } + } + + } + return OK; +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +count_isotope_unknowns(struct inverse *inv_ptr, + struct isotope **isotope_unknowns) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through elements for which isotope balances are requested + * and make a array of isotope structures + * return total number of isotope unknowns and structure array + */ + int i, k; + LDBLE isotope_number; + struct master *primary_ptr; + int count_isotopes; + struct isotope *isotopes; + + if (inv_ptr->count_isotopes == 0) + { + *isotope_unknowns = NULL; + return (0); + } + isotopes = + (struct isotope *) PHRQ_malloc((size_t) sizeof(struct isotope)); + if (isotopes == NULL) + { + malloc_error(); + return (0); + } + count_isotopes = 0; + + for (i = 0; i < inv_ptr->count_isotopes; i++) + { + primary_ptr = master_bsearch(inv_ptr->isotopes[i].elt_name); + isotope_number = inv_ptr->isotopes[i].isotope_number; + if (primary_ptr == NULL) + { + error_string = sformatf( + "Element not found for isotope calculation: %s.", + inv_ptr->isotopes[i].elt_name); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + if (primary_ptr->primary != TRUE) + { + error_string = sformatf( "Isotope mass-balance may only be used" + " for total element concentrations.\n" + "Secondary species not allowed: %s.", + inv_ptr->isotopes[i].elt_name); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + + /* nonredox element */ + if (primary_ptr->s->secondary == NULL) + { + isotopes = + (struct isotope *) PHRQ_realloc(isotopes, + (size_t) (count_isotopes + + 1) * + sizeof(struct isotope)); + if (isotopes == NULL) + { + malloc_error(); + return (0); + } + isotopes[count_isotopes].primary = primary_ptr; + isotopes[count_isotopes].master = primary_ptr; + isotopes[count_isotopes].isotope_number = isotope_number; + isotopes[count_isotopes].elt_name = primary_ptr->elt->name; + count_isotopes++; + + /* redox element */ + } + else + { + + /* find master */ + for (k = 0; k < count_master; k++) + { + if (master[k] == primary_ptr) + break; + } + + /* sum all secondary for master */ + k++; + for (; k < count_master; k++) + { + if (master[k]->elt->primary != primary_ptr) + break; + isotopes = + (struct isotope *) PHRQ_realloc(isotopes, + (size_t) (count_isotopes + + + 1) * + sizeof(struct isotope)); + if (isotopes == NULL) + { + malloc_error(); + return (0); + } + isotopes[count_isotopes].primary = primary_ptr; + isotopes[count_isotopes].master = master[k]; + isotopes[count_isotopes].isotope_number = isotope_number; + isotopes[count_isotopes].elt_name = master[k]->elt->name; + count_isotopes++; + } + } + } + *isotope_unknowns = isotopes; + return (count_isotopes); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_isotopes(struct inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through elements for which isotope balances are requested + * and make sure each solution has isotope ratios defined + */ + int i, ii, j, k, l; + int err, found_isotope; + LDBLE isotope_number; + struct master *master_ptr, *primary_ptr; + cxxSolution *solution_ptr; + struct phase *phase_ptr; + char token[MAX_LENGTH]; + +/* + * Check solutions for necessary isotope data + */ + for (j = 0; j < inv_ptr->count_solns; j++) + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, inv_ptr->solns[j]); + xsolution_zero(); + add_solution(solution_ptr, 1.0, 1.0); +/* + * Go through inverse isotopes and make sure isotope data for each solution + * inv_ptr->isotopes has elements; inv_ptr->i_u has redox states and uncertainties + */ + for (i = 0; i < inv_ptr->count_isotopes; i++) + { + err = FALSE; + primary_ptr = master_bsearch(inv_ptr->isotopes[i].elt_name); + isotope_number = inv_ptr->isotopes[i].isotope_number; + found_isotope = FALSE; + std::map < std::string, cxxSolutionIsotope >::iterator kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + struct master *primary_kit = master_bsearch_primary(kit->second.Get_elt_name().c_str()); + if (primary_kit == primary_ptr && + kit->second.Get_isotope_number() == + isotope_number) + { + found_isotope = TRUE; + break; + } + } + if (found_isotope == TRUE) + continue; + + /* did not find isotope, which is ok if element not in solution */ + if (primary_ptr == s_h2o->primary + || primary_ptr == s_hplus->primary) + { + err = TRUE; + } + else if (primary_ptr->total > 0) + { + err = TRUE; + } + if (err == TRUE) + { + error_string = sformatf( + "In solution %d, isotope ratio(s) are needed for element: %g%s.", + solution_ptr->Get_n_user(), (double) isotope_number, + primary_ptr->elt->name); + error_msg(error_string, CONTINUE); + input_error++; + continue; + } + } +/* + * Go through solution isotopes and set uncertainties + */ + std::map < std::string, cxxSolutionIsotope >::iterator kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + struct master *master_kit = master_bsearch(kit->second.Get_elt_name().c_str()); + struct master *primary_kit = master_bsearch_primary(kit->second.Get_elt_name().c_str()); + kit->second.Set_x_ratio_uncertainty(NAN); +/* + * Search for secondary or primary master in inverse uncertainties + */ + ii = -1; + for (i = 0; i < inv_ptr->count_i_u; i++) + { + master_ptr = master_bsearch(inv_ptr->i_u[i].elt_name); + if (master_ptr == master_kit) + { + ii = i; + break; + } + if (master_ptr == primary_kit) + { + ii = i; + } + } + /* solution isotope data not being used in inverse */ + if (ii == -1) + continue; + + i = ii; + /* use inverse-defined uncertainties first */ +#ifdef NPP + if (j < inv_ptr->i_u[i].count_uncertainties + && !isnan(inv_ptr->i_u[i].uncertainties[j])) +#else + if (j < inv_ptr->i_u[i].count_uncertainties + && inv_ptr->i_u[i].uncertainties[j] != NAN) +#endif + { + kit->second.Set_x_ratio_uncertainty(inv_ptr->i_u[i].uncertainties[j]); + + /* use solution-defined uncertainties second */ + } +#ifdef NPP + else if (inv_ptr->i_u[i].count_uncertainties > 0 + && !isnan(inv_ptr->i_u[i].uncertainties[inv_ptr->i_u[i].count_uncertainties - 1])) +#else + else if (inv_ptr->i_u[i].count_uncertainties > 0 + && inv_ptr->i_u[i].uncertainties[inv_ptr->i_u[i].count_uncertainties - 1] != NAN) +#endif + { + kit->second.Set_x_ratio_uncertainty(inv_ptr->i_u[i].uncertainties[inv_ptr->i_u[i].count_uncertainties - 1]); + + /* use solution-defined uncertainties second */ + } +#ifdef NPP + else if (!isnan(kit->second.Get_ratio_uncertainty())) +#else + else if (kit->second.Get_ratio_uncertainty() != NAN) +#endif + { + kit->second.Set_x_ratio_uncertainty( + kit->second.Get_ratio_uncertainty()); + /* use isotope defaults third */ + } + else + { + sprintf(token, "%g%s", + (double) kit->second.Get_isotope_number(), + kit->second.Get_elt_name().c_str()); + for (l = 0; l < count_iso_defaults; l++) + { + if (strcmp(token, iso_defaults[l].name) == 0) + { + kit->second.Set_x_ratio_uncertainty( + iso_defaults[l].uncertainty); + error_string = sformatf( + "Solution %d, element %g%s: default isotope ratio uncertainty is used, %g.", + solution_ptr->Get_n_user(), + (double) kit->second.Get_isotope_number(), + kit->second.Get_elt_name().c_str(), + kit->second.Get_x_ratio_uncertainty()); + warning_msg(error_string); + break; + } + } + } +#ifdef NPP + if (isnan(kit->second.Get_x_ratio_uncertainty())) +#else + if (kit->second.Get_x_ratio_uncertainty() == NAN) +#endif + { + error_string = sformatf( + "In solution %d, isotope ratio uncertainty is needed for element: %g%s.", + solution_ptr->Get_n_user(), + (double) kit->second.Get_isotope_number(), + kit->second.Get_elt_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + } + } +/* + * Check phases for necessary isotope data + */ + for (j = 0; j < inv_ptr->count_phases; j++) + { + for (i = 0; i < inv_ptr->count_isotopes; i++) + { + primary_ptr = master_bsearch(inv_ptr->isotopes[i].elt_name); + isotope_number = inv_ptr->isotopes[i].isotope_number; + found_isotope = FALSE; + for (k = 0; k < inv_ptr->phases[j].count_isotopes; k++) + { + if (inv_ptr->phases[j].isotopes[k].primary == primary_ptr && + inv_ptr->phases[j].isotopes[k].isotope_number == + isotope_number) + { + found_isotope = TRUE; + break; + } + } + if (found_isotope == TRUE) + continue; + + /* did not find isotope, which is ok if element not in solution */ + phase_ptr = inv_ptr->phases[j].phase; + k = 0; + while (phase_ptr->next_elt[k].elt != NULL) + { + if (phase_ptr->next_elt[k].elt->primary == primary_ptr) + { + if (s_hplus->primary == primary_ptr || + s_h2o->primary == primary_ptr) + { + k++; + continue; + } + else + { + error_string = sformatf( + "In phase %s, isotope ratio(s) are needed for element: %g%s.", + phase_ptr->name, (double) isotope_number, + primary_ptr->elt->name); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + } + k++; + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +phase_isotope_inequalities(struct inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ + int i, j, k; + int column; + char token[MAX_LENGTH]; + if (inv_ptr->count_isotopes <= 0) + return OK; + for (i = 0; i < inv_ptr->count_phases; i++) + { + if (inv_ptr->phases[i].count_isotopes <= 0) + continue; + + for (j = 0; j < inv_ptr->phases[i].count_isotopes; j++) + { + /* find index number */ + for (k = 0; k < inv_ptr->count_isotopes; k++) + { + if (inv_ptr->phases[i].isotopes[j].elt_name == + inv_ptr->isotopes[k].elt_name + && inv_ptr->phases[i].isotopes[j].isotope_number == + inv_ptr->isotopes[k].isotope_number) + { + break; + } + } + if (k >= inv_ptr->count_isotopes) + break; + column = col_phase_isotopes + i * inv_ptr->count_isotopes + k; +/* + * zero column if uncertainty is zero + */ + if (inv_ptr->phases[i].isotopes[j].ratio_uncertainty == 0) + { + for (k = 0; k < count_rows; k++) + { + array[k * max_column_count + column] = 0.0; + } + continue; + } + +/* + * optimization + */ + array[(column - col_epsilon) * max_column_count + column] = + SCALE_EPSILON / + inv_ptr->phases[i].isotopes[j].ratio_uncertainty; +/* + * two inequalities to account for absolute value + */ + /* for phases constrained to precipitate */ + if (inv_ptr->phases[i].constraint == PRECIPITATE) + { + array[count_rows * max_column_count + col_phases + i] = + inv_ptr->phases[i].isotopes[j].ratio_uncertainty; + array[count_rows * max_column_count + column] = 1.0; + sprintf(token, "%s %s", inv_ptr->phases[i].phase->name, + "iso pos"); + row_name[count_rows] = string_hsave(token); + count_rows++; + + array[count_rows * max_column_count + col_phases + i] = + inv_ptr->phases[i].isotopes[j].ratio_uncertainty; + array[count_rows * max_column_count + column] = -1.0; + sprintf(token, "%s %s", inv_ptr->phases[i].phase->name, + "iso neg"); + row_name[count_rows] = string_hsave(token); + count_rows++; + + /* for phases constrained to dissolve */ + } + else if (inv_ptr->phases[i].constraint == DISSOLVE) + { + array[count_rows * max_column_count + col_phases + i] = + -inv_ptr->phases[i].isotopes[j].ratio_uncertainty; + array[count_rows * max_column_count + column] = -1.0; + sprintf(token, "%s %s", inv_ptr->phases[i].phase->name, + "iso pos"); + row_name[count_rows] = string_hsave(token); + count_rows++; + + array[count_rows * max_column_count + col_phases + i] = + -inv_ptr->phases[i].isotopes[j].ratio_uncertainty; + array[count_rows * max_column_count + column] = 1.0; + sprintf(token, "%s %s", inv_ptr->phases[i].phase->name, + "iso neg"); + row_name[count_rows] = string_hsave(token); + count_rows++; + + /* Error if phase is not constrained */ + } + else + { + error_string = sformatf( + "In isotope calculations, all phases containing isotopes must be" + " constrained.\nPhase %s is not constrained.\n", + inv_ptr->phases[i].phase->name); + error_msg(error_string, CONTINUE); + input_error++; + continue; + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +write_optimize_names(struct inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ + int i, j, row; + char token[MAX_LENGTH]; + row = 0; +/* + * epsilons for analytical data + */ + for (j = 0; j < inv_ptr->count_elts; j++) + { + for (i = 0; i < inv_ptr->count_solns; i++) + { + sprintf(token, "%s %s %d", "optimize", + inv_ptr->elts[j].master->elt->name, inv_ptr->solns[i]); + row_name[row] = string_hsave(token); + row++; + } + } +/* + * pH + */ + if (carbon > 0) + { + for (i = 0; i < inv_ptr->count_solns; i++) + { + sprintf(token, "%s %s %d", "optimize", "pH", inv_ptr->solns[i]); + row_name[row] = string_hsave(token); + row++; + } + } +/* + * water + */ + sprintf(token, "%s %s", "optimize", "water"); + row_name[row] = string_hsave(token); + row++; +/* + * solution isotopes + */ + for (i = 0; i < inv_ptr->count_solns; i++) + { + for (j = 0; j < inv_ptr->count_isotope_unknowns; j++) + { + sprintf(token, "%s %d%s %d", "optimize", + (int) inv_ptr->isotope_unknowns[j].isotope_number, + inv_ptr->isotope_unknowns[j].elt_name, inv_ptr->solns[i]); + row_name[row] = string_hsave(token); + row++; + } + } +/* + * phase isotopes + */ + + for (i = 0; i < inv_ptr->count_phases; i++) + { + for (j = 0; j < inv_ptr->count_isotopes; j++) + { + sprintf(token, "%s %s %d%s", "optimize", + inv_ptr->phases[i].phase->name, + (int) inv_ptr->isotopes[j].isotope_number, + inv_ptr->isotopes[j].elt_name); + row_name[row] = string_hsave(token); + row++; + } + } + return OK; +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +dump_netpath(struct inverse *inverse_ptr) +/* ---------------------------------------------------------------------- */ +{ + int j; + std::string string; + char *ptr; + + if (inverse_ptr->netpath == NULL) + return; + + /* open file */ + string = inverse_ptr->netpath; + if (replace(".lon", ".lon", string) != true) + { + string.append(".lon"); + } + netpath_file = fopen(string.c_str(), "w"); + if (netpath_file == NULL) + { + error_string = sformatf( "Can`t open file, %s.", inverse_ptr->netpath); + error_msg(error_string, STOP); +#if !defined(R_SO) + exit(4); +#endif + } + add_to_file("netpath.fil", inverse_ptr->netpath); + + /* Header */ + fprintf(netpath_file, + "2.14 # File format\n"); + + /* write out each solution */ + std::map::iterator it = Rxn_solution_map.begin(); + for ( ; it != Rxn_solution_map.end(); it++) + { + if (it->second.Get_n_user() < 0) + continue; + + /* flags and description */ + char * description = string_duplicate(it->second.Get_description().c_str()); + ptr = description; + j = copy_token(string, &ptr); + if (j != EMPTY) + { + string = sformatf("%s", description); + } + else + { + string = sformatf("Solution %d", it->second.Get_n_user()); + } + fprintf(netpath_file, "4020%s\n", string.c_str()); + description = (char *) free_check_null(description); + /* lat/lon */ + fprintf(netpath_file, + " # Lat/lon\n"); + + /* well number */ + fprintf(netpath_file, + "%15d # Well number\n", + it->second.Get_n_user()); + + /* total number of wells */ + fprintf(netpath_file, + "%15d # Total wells\n", + (int) Rxn_solution_map.size()); + + /* address */ + fprintf(netpath_file, + " # Address1\n"); + fprintf(netpath_file, + " # Address2\n"); + fprintf(netpath_file, + " # Address3\n"); + fprintf(netpath_file, + " # Address4\n"); + fprintf(netpath_file, + " # Address5\n"); + + /* temperature */ + fprintf(netpath_file, + "%15g # Temperature\n", + (double) it->second.Get_tc()); + + /* pH */ + fprintf(netpath_file, + "%15g # pH\n", + (double) it->second.Get_ph()); + + /* DO */ + print_total(netpath_file, &(it->second), "O(0)", "Dissolved Oxygen"); + + /* TDIC */ + print_total(netpath_file, &(it->second), "C(4)", "TDIC"); + + /* Tritium */ + print_isotope(netpath_file, &(it->second), "3H(1)", "Tritium"); + + /* H2S */ + print_total(netpath_file, &(it->second), "S(-2)", "H2S"); + + /* Calcium */ + print_total(netpath_file, &(it->second), "Ca", "Calcium"); + + /* Eh */ + fprintf(netpath_file, + "%15g # Eh\n", + (double) (0.059 * it->second.Get_pe())); + + /* Magnesium */ + print_total(netpath_file, &(it->second), "Mg", "Magnesium"); + + /* Sodium */ + print_total(netpath_file, &(it->second), "Na", "Sodium"); + + /* Potassium */ + print_total(netpath_file, &(it->second), "K", "Potassium"); + + /* Chloride */ + print_total(netpath_file, &(it->second), "Cl", "Chloride"); + + /* Sulfate */ + print_total(netpath_file, &(it->second), "S(6)", "Sulfate"); + + /* Fluoride */ + print_total(netpath_file, &(it->second), "F", "Fluoride"); + + /* Silica */ + print_total(netpath_file, &(it->second), "Si", "Silica"); + + /* Bromide */ + print_total(netpath_file, &(it->second), "Br", "Bromide"); + + /* Boron */ + print_total(netpath_file, &(it->second), "B", "Boron"); + + /* Barium */ + print_total(netpath_file, &(it->second), "Ba", "Barium"); + + /* Lithium */ + print_total(netpath_file, &(it->second), "Li", "Lithium"); + + /* Strontium */ + print_total(netpath_file, &(it->second), "Sr", "Strontium"); + + /* Iron */ + print_total_multi(netpath_file, &(it->second), "Iron", "Fe", "Fe(2)", + "Fe(3)", "", ""); + + + /* Manganese */ + print_total_multi(netpath_file, &(it->second), "Manganese", "Mn", + "Mn(2)", "Mn(3)", "Mn(6)", "Mn(7)"); + + /* Nitrate */ + print_total(netpath_file, &(it->second), "N(5)", "Nitrate"); + + /* Ammonium */ + print_total_multi(netpath_file, &(it->second), "Ammonium", "N(-3)", + "Amm", "", "", ""); + + /* Phosphate */ + print_total(netpath_file, &(it->second), "P", "Phosphate"); + + /* DOC */ + print_total_multi(netpath_file, &(it->second), "DOC", "Fulvate", + "Humate", "", "", ""); + + /* Sp. Cond. */ + fprintf(netpath_file, + " # Sp. Cond.\n"); + + /* Density */ + fprintf(netpath_file, + " # Density\n"); + + /* Delta C-13 TDIC */ + print_isotope(netpath_file, &(it->second), "13C(4)", "Delta C-13 TDIC"); + + /* C-14 TDIC */ + print_isotope(netpath_file, &(it->second), "14C(4)", "C-14 TDIC"); + + /* Delta S-34 (SO4) */ + print_isotope(netpath_file, &(it->second), "34S(6)", + "Delta S-34 (SO4)"); + + /* Delta S-34 (H2S) */ + print_isotope(netpath_file, &(it->second), "34S(-2)", + "Delta S-34 (H2S)"); + + /* Delta Deuterium */ + print_isotope(netpath_file, &(it->second), "2H(1)", "Delta Deuterium"); + + /* Delta O-18 */ + print_isotope(netpath_file, &(it->second), "18O(-2)", "Delta O-18"); + + /* CH4 (aq) */ + print_total(netpath_file, &(it->second), "C(-4)", "CH4 (aq)"); + + /* Sr 87/86 */ + print_isotope(netpath_file, &(it->second), "87Sr", "Sr 87/86"); + + /* Al */ + print_total(netpath_file, &(it->second), "Al", "Alumninum"); + + /* N2 (aq) */ + print_total(netpath_file, &(it->second), "N(0)", "N2 (aq)"); + + /* N-15 of N2 (aq) */ + print_isotope(netpath_file, &(it->second), "15N(0)", "N-15 of N2 (aq)"); + + /* N-15 of Nitrate */ + print_isotope(netpath_file, &(it->second), "15N(5)", "N-15 of Nitrate"); + + /* N-15 of Ammonium */ + print_isotope(netpath_file, &(it->second), "15N(-3)", + "N-15 of Ammonium"); + + /* Formation */ + fprintf(netpath_file, + " # Formation\n"); + + } + if (netpath_file != NULL) + { + fclose(netpath_file); + netpath_file = NULL; + } + return; +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +get_inv_total(cxxSolution *solution_ptr, const char *elt) +/* ---------------------------------------------------------------------- */ +{ + cxxNameDouble::iterator jit = solution_ptr->Get_totals().begin(); + for ( ; jit != solution_ptr->Get_totals().end(); jit++) + { + if (strcmp(elt, jit->first.c_str()) == 0) + return jit->second; + } + return (0); + +} +/* ---------------------------------------------------------------------- */ +cxxSolutionIsotope *Phreeqc::get_isotope(cxxSolution *solution_ptr, const char *elt) +/* ---------------------------------------------------------------------- */ +{ + std::string str_elt = elt; + std::map::iterator it; + it = solution_ptr->Get_isotopes().find(str_elt); + if (it != solution_ptr->Get_isotopes().end()) + { + return &(it->second); + } + return (NULL); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +print_total(FILE * l_netpath_file, cxxSolution *solution_ptr, + const char *elt, const char *string) +/* ---------------------------------------------------------------------- */ +{ + LDBLE moles = get_inv_total(solution_ptr, elt); + if (moles == 0) + { + fprintf(l_netpath_file, + " # %s\n", + string); + } + else + { + fprintf(l_netpath_file, + "%15g # %s\n", + (double) (1000 * moles / solution_ptr->Get_mass_water()), + string); + } +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +print_isotope(FILE * l_netpath_file, cxxSolution *solution_ptr, + const char *elt, const char *string) +/* ---------------------------------------------------------------------- */ +{ + cxxSolutionIsotope *iso_ptr; + iso_ptr = get_isotope(solution_ptr, elt); + if (iso_ptr == NULL) + { + fprintf(l_netpath_file, + " # %s\n", + string); + } + else + { + fprintf(l_netpath_file, + "%15g # %s\n", + (double) iso_ptr->Get_ratio(), string); + } +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +print_total_multi(FILE * l_netpath_file, cxxSolution *solution_ptr, + const char *string, const char *elt0, const char *elt1, + const char *elt2, const char *elt3, const char *elt4) +/* ---------------------------------------------------------------------- */ +{ + char elts[5][MAX_LENGTH]; + LDBLE moles; + LDBLE sum; + int i, found; + + strcpy(elts[0], elt0); + strcpy(elts[1], elt1); + strcpy(elts[2], elt2); + strcpy(elts[3], elt3); + strcpy(elts[4], elt4); + + + sum = 0; + found = FALSE; + for (i = 0; i < 5; i++) + { + moles = get_inv_total(solution_ptr, elts[i]); + if (moles == 0) + { + continue; + } + else + { + sum += moles; + found = TRUE; + } + } + if (found != TRUE) + { + fprintf(l_netpath_file, + " # %s\n", + string); + } + else + { + fprintf(l_netpath_file, + "%15g # %s\n", + (double) (1000 * sum / solution_ptr->Get_mass_water()), string); + } + return; +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +dump_netpath_pat(struct inverse *inv_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints model + */ + int i, j, k; + cxxSolution *solution_ptr, *solution_ptr_orig; + struct master *master_ptr; + LDBLE d1, d2, d3; + char *ptr; + LDBLE sum, sum1, sum_iso, d; + LDBLE *array_save, *l_delta_save; + int count_unknowns_save, max_row_count_save, max_column_count_save, temp, + count_current_solutions, temp_punch; + int solnmap[10][2]; + struct isotope *isotope_ptr; + FILE *model_file; + struct elt_list *next_elt; + int exch, column; + LDBLE f; + struct rxn_token *rxn_ptr; +/* + * print solution data, epsilons, and revised data + */ + if (inv_ptr->pat == NULL) + return (OK); + + array_save = array; + l_delta_save = delta; + count_unknowns_save = count_unknowns; + max_row_count_save = max_row_count; + max_column_count_save = max_column_count; + + array = NULL; + delta = NULL; + count_unknowns = 0; + max_row_count = 0; + max_column_count = 0; + + count_current_solutions = 0; + count_inverse_models++; + + for (i = 0; i < inv_ptr->count_solns; i++) + { + if (equal(inv_delta1[i], 0.0, toler) == TRUE) + continue; + solution_ptr_orig = Utilities::Rxn_find(Rxn_solution_map, inv_ptr->solns[i]); + Utilities::Rxn_copy(Rxn_solution_map, solution_ptr_orig->Get_n_user(), -6); + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, -6); + xsolution_zero(); + + /* Adjust pH */ + if (inv_ptr->carbon == TRUE) + { + d1 = solution_ptr->Get_ph(); + d2 = inv_delta1[col_ph + i] / inv_delta1[i]; + d3 = d1 + d2; + solution_ptr->Set_ph(d3); + } + + /* put original totals in master */ + cxxNameDouble::iterator jit = solution_ptr->Get_totals().begin(); + for ( ; jit != solution_ptr->Get_totals().end(); jit++) + { + master_ptr = master_bsearch(jit->first.c_str()); + master_ptr->total = jit->second; + } + + /* ignore alkalinity */ + /*master_alk->total = solution_ptr->total_alkalinity; */ + + /* update total in master */ + for (j = 0; j < inv_ptr->count_elts; j++) + { + if (inv_ptr->elts[j].master->s == s_eminus) + continue; + d1 = inv_ptr->elts[j].master->total; + d2 = inv_delta1[col_epsilon + j * inv_ptr->count_solns + + i] / inv_delta1[i]; + d3 = d1 + d2; + inv_ptr->elts[j].master->total = d3; + } + + /* put updated total back in solution */ + cxxNameDouble nd; + jit = solution_ptr->Get_totals().begin(); + for ( ; jit != solution_ptr->Get_totals().end(); jit++) + { + master_ptr = master_bsearch(jit->first.c_str()); + nd[jit->first] = master_ptr->total; + } + solution_ptr->Set_totals(nd); + + /* update isotopes in solution */ + if (inv_ptr->count_isotopes > 0) + { + /* adjustments to solution isotope composition */ + for (j = 0; j < inv_ptr->count_isotope_unknowns; j++) + { + std::map < std::string, cxxSolutionIsotope >::iterator kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + if (inv_ptr->isotope_unknowns[j].elt_name != + string_hsave(kit->second.Get_elt_name().c_str()) || + inv_ptr->isotope_unknowns[j].isotope_number != + kit->second.Get_isotope_number()) + continue; + d1 = kit->second.Get_ratio(); + d2 = inv_delta1[col_isotopes + + i * inv_ptr->count_isotope_unknowns + + j] / inv_delta1[i]; + d3 = d1 + d2; + kit->second.Set_ratio(d3); + } + } + } + + set_initial_solution(-6, -7); + temp = pr.all; + pr.all = FALSE; + temp_punch = pr.punch; + pr.punch = FALSE; + phrq_io->Set_punch_on(false); + initial_solutions(FALSE); + pr.all = temp; + pr.punch = temp_punch; + phrq_io->Set_punch_on(pr.punch == TRUE); + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, -7); + + /* Header */ + char * description = string_duplicate(solution_ptr_orig->Get_description().c_str()); + ptr = description; + std::string string; + if (copy_token(string, &ptr) != EMPTY) + { + fprintf(netpath_file, "%d. %s\n", count_inverse_models, + solution_ptr_orig->Get_description().c_str()); + } + else + { + fprintf(netpath_file, "%d. Solution %d\n", count_inverse_models, + solution_ptr_orig->Get_n_user()); + } + description = (char *) free_check_null(description); + + /* bookkeeping */ + count_pat_solutions++; + solnmap[count_current_solutions][0] = solution_ptr_orig->Get_n_user(); + solnmap[count_current_solutions][1] = count_pat_solutions; + count_current_solutions++; + + /* Dump info to .pat file */ + print_total_pat(netpath_file, "C", "C"); + print_total_pat(netpath_file, "S", "S"); + print_total_pat(netpath_file, "Ca", "CA"); + print_total_pat(netpath_file, "Al", "AL"); + print_total_pat(netpath_file, "Mg", "MG"); + print_total_pat(netpath_file, "Na", "NA"); + print_total_pat(netpath_file, "K", "K"); + print_total_pat(netpath_file, "Cl", "CL"); + print_total_pat(netpath_file, "F", "F"); + print_total_pat(netpath_file, "Si", "SI"); + print_total_pat(netpath_file, "Br", "BR"); + print_total_pat(netpath_file, "B", "B"); + print_total_pat(netpath_file, "Ba", "BA"); + print_total_pat(netpath_file, "Li", "LI"); + print_total_pat(netpath_file, "Sr", "SR"); + print_total_pat(netpath_file, "Fe", "FE"); + print_total_pat(netpath_file, "Mn", "MN"); + print_total_pat(netpath_file, "N", "N"); + print_total_pat(netpath_file, "P", "P"); + fprintf(netpath_file, "%14g # TEMP\n", (double) solution_ptr->Get_tc()); + print_total_pat(netpath_file, "S(-2)", "H2S"); + print_total_pat(netpath_file, "S(6)", "SO4"); + + /* N15 */ + sum_iso = 0; + sum = 0; + std::map < std::string, cxxSolutionIsotope >::iterator kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + if (strstr(kit->second.Get_isotope_name().c_str(), "15N") != NULL) + { + d = total(kit->second.Get_elt_name().c_str()); + sum_iso += kit->second.Get_ratio() * d; + sum += d; + } + } + if (sum == 0) + { + fprintf(netpath_file, "%14g* # N15\n", (double) sum); + } + else + { + fprintf(netpath_file, "%14g # N15\n", + (double) (sum_iso / sum)); + } + + /* RS of N */ + sum = 0; + sum = + total("N(-3)") * -3 + total("N(0)") * 0 + total("N(3)") * 3 + + total("N(5)") * 5; + sum1 = total("N(-3)") + total("N(0)") + total("N(3)") + total("N(5)"); + if (sum1 == 0) + { + fprintf(netpath_file, "%14g* # RS of N\n", (double) sum1); + } + else + { + fprintf(netpath_file, "%14g # RS of N\n", + (double) (sum / sum1)); + } + + /* DOX */ + print_total_pat(netpath_file, "O(0)", "DOX"); + + /*HCO3 */ + d = 1000 * sum_match_species("*HCO3*", "C"); + if (d == 0.0) + { + fprintf(netpath_file, "%14g* # HCO3\n", (double) d); + } + else + { + fprintf(netpath_file, "%14g # HCO3\n", (double) d); + } + + /* pH */ + fprintf(netpath_file, "%14g # PH\n", (double) solution_ptr->Get_ph()); + + /*H2CO3* */ + d = 1000 * (molality("H2CO3") + molality("CO2")); + if (d == 0.0) + { + fprintf(netpath_file, "%14g* # H2CO3\n", (double) d); + } + else + { + fprintf(netpath_file, "%14g # H2CO3\n", (double) d); + } + + /*CO3 */ + d = sum_match_species("*CO3*", "C"); + d -= sum_match_species("*HCO3*", "C"); + d *= 1000.0; + if (d == 0.0) + { + fprintf(netpath_file, "%14g* # CO3\n", (double) d); + } + else + { + fprintf(netpath_file, "%14g # CO3\n", (double) d); + } + + /* CARBONATES */ + print_total_pat(netpath_file, "C(4)", "CARBONATES"); + print_total_pat(netpath_file, "Fe(2)", "FE2+"); + print_total_pat(netpath_file, "Fe(3)", "FE3+"); + print_total_pat(netpath_file, "Mn(2)", "MN2+"); + print_total_pat(netpath_file, "Mn(3)", "MN3+"); + print_total_pat(netpath_file, "Mn(6)", "MN6+"); + print_total_pat(netpath_file, "Mn(7)", "MN7+"); + print_total_pat(netpath_file, "C(-4)", "CH4"); + print_total_pat(netpath_file, "Doc", "DOC"); + + /*RS OF DOC */ + fprintf(netpath_file, "%14g* # RS OF DOC\n", 0.0); + + /* Blank */ + print_total_pat(netpath_file, "Blank", "BLANK"); + + /*C13 */ + sum_iso = 0; + sum = 0; + kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + if (strstr(kit->second.Get_isotope_name().c_str(), "13C") != NULL) + { + d = total(kit->second.Get_elt_name().c_str()); + sum_iso += kit->second.Get_ratio() * d; + sum += d; + } + } + if (sum == 0) + { + fprintf(netpath_file, "%14g* # C13\n", (double) sum); + } + else + { + fprintf(netpath_file, "%14g # C13\n", + (double) (sum_iso / sum)); + } + + /*C14 */ + sum_iso = 0; + sum = 0; + kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + if (strstr(kit->second.Get_isotope_name().c_str(), "14C") != NULL) + { + d = total(kit->second.Get_elt_name().c_str()); + sum_iso += kit->second.Get_ratio() * d; + sum += d; + } + } + if (sum == 0) + { + fprintf(netpath_file, "%14g* # C14\n", (double) sum); + } + else + { + fprintf(netpath_file, "%14g # C14\n", + (double) (sum_iso / sum)); + } + + /*SR87 */ + sum_iso = 0; + sum = 0; + kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + if (strstr(kit->second.Get_isotope_name().c_str(), "87Sr") != + NULL) + { + d = total(kit->second.Get_elt_name().c_str()); + sum_iso += kit->second.Get_ratio() * d; + sum += d; + } + } + if (sum == 0) + { + fprintf(netpath_file, "%14g* # SR87\n", (double) sum); + } + else + { + fprintf(netpath_file, "%14g # SR87\n", + (double) (sum_iso / sum)); + } + + /*D*/ sum_iso = 0; + sum = 0; + kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + if (strstr(kit->second.Get_isotope_name().c_str(), "2H") != NULL) + { + d = total(kit->second.Get_elt_name().c_str()); + sum_iso += kit->second.Get_ratio() * d; + sum += d; + } + } + if (sum == 0) + { + fprintf(netpath_file, "%14g* # D\n", (double) sum); + } + else + { + fprintf(netpath_file, "%14g # D\n", (double) (sum_iso / sum)); + } + + /*O-18 */ + sum_iso = 0; + sum = 0; + kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + if (strstr(kit->second.Get_isotope_name().c_str(), "18O") != NULL) + { + if (strcmp(kit->second.Get_elt_name().c_str(), "O(-2)") == 0) + { + d = solution_ptr->Get_total_o() - total("O(0)"); + } + else if (strcmp(kit->second.Get_elt_name().c_str(), "H(1)") + == 0) + { + d = solution_ptr->Get_total_h() - total("H(0)"); + } + else + { + d = total(kit->second.Get_elt_name().c_str()); + } + sum_iso += kit->second.Get_ratio() * d; + sum += d; + } + } + if (sum == 0) + { + fprintf(netpath_file, "%14g* # O-18\n", (double) sum); + } + else + { + fprintf(netpath_file, "%14g # O-18\n", + (double) (sum_iso / sum)); + } + + /*TRITIUM*/ sum_iso = 0; + sum = 0; + kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + if (strstr(kit->second.Get_isotope_name().c_str(), "3H") != NULL) + { + d = total(kit->second.Get_elt_name().c_str()); + sum_iso += kit->second.Get_ratio() * d; + sum += d; + } + } + if (sum == 0) + { + fprintf(netpath_file, "%14g* # TRITIUM\n", (double) sum); + } + else + { + fprintf(netpath_file, "%14g # TRITIUM\n", + (double) (sum_iso / sum)); + } + + /*34SSO4 */ + sum_iso = 0; + sum = 0; + kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + if (strstr(kit->second.Get_isotope_name().c_str(), "34S(6)") != + NULL) + { + d = total(kit->second.Get_elt_name().c_str()); + sum_iso += kit->second.Get_ratio() * d; + sum += d; + } + } + if (sum == 0) + { + fprintf(netpath_file, "%14g* # 34SSO4\n", (double) sum); + } + else + { + fprintf(netpath_file, "%14g # 34SSO4\n", + (double) (sum_iso / sum)); + } + + /*34SH2S */ + sum_iso = 0; + sum = 0; + kit = solution_ptr->Get_isotopes().begin(); + for ( ; kit != solution_ptr->Get_isotopes().end(); kit++) + { + if (strstr(kit->second.Get_isotope_name().c_str(), "34S(-2)") != + NULL) + { + d = total(kit->second.Get_elt_name().c_str()); + sum_iso += kit->second.Get_ratio() * d; + sum += d; + } + } + if (sum == 0) + { + fprintf(netpath_file, "%14g* # 34SH2S\n", (double) sum); + } + else + { + fprintf(netpath_file, "%14g # 34SH2S\n", + (double) (sum_iso / sum)); + } + + /* Well number */ + fprintf(netpath_file, "%14d # Well number\n", + count_pat_solutions); + } + //free_model_allocs(); + array = (LDBLE *) free_check_null(array); + delta = (LDBLE *) free_check_null(delta); + array = array_save; + delta = l_delta_save; + count_unknowns = count_unknowns_save; + max_row_count = max_row_count_save; + max_column_count = max_column_count_save; + +/* + * Open model file + */ + std::string string; + string = inv_ptr->pat; + replace(".pat", "", string); + trim(string); + std::string string1 = sformatf("%s-%d.mod", string.c_str(), count_inverse_models); + model_file = fopen(string1.c_str(), "w"); + if (model_file == NULL) + { + error_string = sformatf( "Can`t open file, %s.", string.c_str()); + error_msg(error_string, STOP); + } + add_to_file("model.fil", string1.c_str()); + + +/* + * Write header + */ + fprintf(model_file, "%s\n", string.c_str()); + +/* + * Write well numbers + */ + for (i = 0; i < count_current_solutions; i++) + { + fprintf(model_file, "%3d", solnmap[i][1]); + } + fprintf(model_file, "\n"); +/* + * Write elements + */ + xsolution_zero(); + for (j = 0; j < count_master; j++) + { + master[j]->in = FALSE; + } + for (j = 0; j < inv_ptr->count_elts; j++) + { + master_ptr = inv_ptr->elts[j].master; + master_ptr = master_ptr->elt->primary; + if (strcmp(master_ptr->elt->name, "Alkalinity") == 0) + continue; + if (strcmp(master_ptr->elt->name, "H") == 0) + continue; + if (strcmp(master_ptr->elt->name, "O") == 0) + continue; + if (strcmp(master_ptr->elt->name, "X") == 0) + continue; + if (strcmp(master_ptr->elt->name, "E") == 0) + continue; + master_ptr->in = TRUE; + } + for (j = 0; j < count_master; j++) + { + if (master[j]->in == TRUE) + { + string = master[j]->elt->name; + Utilities::str_toupper(string); + fprintf(model_file, " %-2s", string.c_str()); + } + } + fprintf(model_file, " %-2s", "RS"); +/* + * Add isotope mole balance + */ + for (j = 0; j < inv_ptr->count_isotopes; j++) + { + string = sformatf("%d%s", (int) inv_ptr->isotopes[j].isotope_number, + inv_ptr->isotopes[j].elt_name); + if (strcmp(string.c_str(), "13C") == 0) + fprintf(model_file, " %-2s", "I1"); + if (strcmp(string.c_str(), "14C") == 0) + fprintf(model_file, " %-2s", "I2"); + if (strcmp(string.c_str(), "34S") == 0) + fprintf(model_file, " %-2s", "I3"); + if (strcmp(string.c_str(), "87Sr") == 0) + fprintf(model_file, " %-2s", "I4"); + if (strcmp(string.c_str(), "15N") == 0) + fprintf(model_file, " %-2s", "I9"); + if (strcmp(string.c_str(), "2H") == 0) + fprintf(model_file, " %-2s", "D "); + if (strcmp(string.c_str(), "3H") == 0) + fprintf(model_file, " %-2s", "TR"); + if (strcmp(string.c_str(), "18O") == 0) + fprintf(model_file, " %-2s", "18"); + } + + /* end of element line */ + fprintf(model_file, "\n"); + +/* + * Write phase information + */ + for (i = 0; i < inv_ptr->count_phases; i++) + { + j = col_phases + i; + /* skip if not in model */ +/* if (equal (inv_delta1[j], 0.0, toler) == TRUE) continue;*/ + + /* Do not include Na exchange phase */ + if (strcmp_nocase(inv_ptr->phases[i].name, "NaX") == 0) + continue; +/* + * Determine if exchange reaction + */ + exch = FALSE; + for (next_elt = inv_ptr->phases[i].phase->next_elt; + next_elt->elt != NULL; next_elt++) + { + if (strcmp(next_elt->elt->name, "X") == 0) + { + exch = TRUE; + break; + } + } +/* + * Write phase name and constraints + */ + string = inv_ptr->phases[i].name; + string = string.substr(0,8); + string = Utilities::pad_right(string, 8); + if (inv_ptr->phases[i].force == TRUE) + { + string += 'F'; + } + else + { + string += ' '; + } + switch (inv_ptr->phases[i].constraint) + { + case EITHER: + string += ' '; + break; + case PRECIPITATE: + if (exch == TRUE) + { + string += '+'; + } + else + { + string += '-'; + } + break; + case DISSOLVE: + if (exch == TRUE) + { + string += '-'; + } + else + { + string += '+'; + } + break; + } + fprintf(model_file, "%-10s", string.c_str()); +/* + * Write stoichiometry + */ + for (next_elt = inv_ptr->phases[i].phase->next_elt; + next_elt->elt != NULL; next_elt++) + { + f = 1.0; + if (exch == TRUE) + f = -1.0; + master_ptr = next_elt->elt->primary; + if (strcmp(master_ptr->elt->name, "Alkalinity") == 0) + continue; + if (strcmp(master_ptr->elt->name, "H") == 0) + continue; + if (strcmp(master_ptr->elt->name, "O") == 0) + continue; + if (strcmp(master_ptr->elt->name, "E") == 0) + continue; + string = master_ptr->elt->name; + + if (strcmp(master_ptr->elt->name, "X") == 0) + { + string = "Na"; + f = 1.0; + } + Utilities::str_toupper(string); + fprintf(model_file, " %-2s%12.7f", string.c_str(), + (double) (next_elt->coef * f)); + } +/* + * Calculate RS + */ + std::string token; + sum = 0; + for (rxn_ptr = inv_ptr->phases[i].phase->rxn_s->token + 1; + rxn_ptr->s != NULL; rxn_ptr++) + { + if (rxn_ptr->s == s_hplus) + continue; + if (rxn_ptr->s == s_h2o) + continue; + if (rxn_ptr->s->secondary == NULL && rxn_ptr->s != s_eminus) + continue; + if (rxn_ptr->s == s_o2) + { + sum += 4 * rxn_ptr->coef; + } + else if (rxn_ptr->s == s_h2) + { + sum += -2 * rxn_ptr->coef; + } + else if (rxn_ptr->s == s_eminus) + { + sum += -1 * rxn_ptr->coef; + } + else + { + string = rxn_ptr->s->secondary->elt->name; + replace("(", " ", string); + replace(")", " ", string); + std::string::iterator b = string.begin(); + std::string::iterator e = string.end(); + CParser::copy_token(token, b, e); + CParser::copy_token(string1, b, e); + sscanf(string1.c_str(), SCANFORMAT, &f); + sum += f * rxn_ptr->coef; + } + } + if (sum != 0.0) + fprintf(model_file, " %-2s%12.7f", "RS", (double) sum); +/* + * Add isotopes + */ + + for (k = 0; k < inv_ptr->phases[i].count_isotopes; k++) + { + isotope_ptr = inv_ptr->phases[i].isotopes; + d1 = isotope_ptr[k].ratio; + for (j = 0; j < inv_ptr->count_isotopes; j++) + { + if ((inv_ptr->isotopes[j].elt_name != isotope_ptr[k].elt_name) + || (inv_ptr->isotopes[j].isotope_number != + isotope_ptr[k].isotope_number)) + continue; + break; + } + d2 = 0.0; + if (j < inv_ptr->count_isotopes) + { + column = col_phase_isotopes + i * inv_ptr->count_isotopes + j; + if (inv_delta1[col_phases + i] != 0.0) + { + d2 = inv_delta1[column] / inv_delta1[col_phases + i]; + } + } + d3 = d1 + d2; + string = sformatf("%d%s", (int) isotope_ptr[k].isotope_number, + isotope_ptr[k].elt_name); + if (strcmp(string.c_str(), "13C") == 0) + fprintf(model_file, " %-2s%12.7f", "I1", (double) d3); + if (strcmp(string.c_str(), "14C") == 0) + fprintf(model_file, " %-2s%12.7f", "I2", (double) d3); + if (strcmp(string.c_str(), "34S") == 0) + { + fprintf(model_file, " %-2s%12.7f", "I3", (double) d3); + fprintf(model_file, " %-2s%12.7f", "I7", 0.0); + } + if (strcmp(string.c_str(), "87Sr") == 0) + fprintf(model_file, " %-2s%12.7f", "I4", (double) d3); + if (strcmp(string.c_str(), "15N") == 0) + fprintf(model_file, " %-2s%12.7f", "I9", (double) d3); + if (strcmp(string.c_str(), "2H") == 0) + fprintf(model_file, " %-2s%12.7f", "D ", (double) d3); + if (strcmp(string.c_str(), "3H") == 0) + fprintf(model_file, " %-2s%12.7f", "TR", (double) d3); + if (strcmp(string.c_str(), "18O") == 0) + fprintf(model_file, " %-2s%12.7f", "18", (double) d3); + } +/* + * Done with stoichiometry + */ + fprintf(model_file, "\n"); + } + fprintf(model_file, "\n"); +/* + * Write extra stuff at bottom + */ + /* + (Iflag(i),i=2,6), (P(i),i=1,2), (Isdocrs(i),i=0,5), Disalong, + (C14dat(i),i=1,5), (Usera(i),i=1,2), + (C14dat(i),i=8,9), i10, i11, (C14dat(i),i=12,13), + (Dbdata(Well(0),i),i=44,47), Dbdata(Well(0),49), + ((Dbdata(Well(iwell),i),i=44,47),Dbdata(Well(iwell),49),Usera(iwell),iwell=1,Iflag(1)+1) + 9030 FORMAT (5(I2),2(F8.4),6(I1),F6.3,/, + 7(F8.3),/, + 2(F8.3),2(F8.0), 2(F8.3),/, + 5(F8.3),/, + 7(6(F8.3),/)) + */ + /* iflags */ + /*fprintf(model_file,"%2d", i); */ /* not written, 1, mixing, number of mixing wells -1 */ + fprintf(model_file, "%2d", 3); /* 2, exchange */ + i = 0; + if (inv_ptr->count_isotopes > 0) + i = 1; + fprintf(model_file, "%2d", i); /* 3, Rayleigh */ + fprintf(model_file, "%2d", 1); /* 4, A0 model */ + fprintf(model_file, "%2d", 0); /* 5, Mook/Deines */ + fprintf(model_file, "%2d", 0); /* 6, Evaporation/Dilution */ + /* p */ + fprintf(model_file, "%8.4f%8.4f", 1.0, 0.0); /* P(1),(2) fraction of CO2, fraction of Ca in exch */ + fprintf(model_file, "000000"); /* isdocrs(0,5) doc, rs? */ + fprintf(model_file, "%6.3f\n", 1.0); /* disalong */ + fprintf(model_file, + " 0.000 100.000 0.000 0.000 -25.000 100.000 100.000\n"); + fprintf(model_file, + " 0.000 0.000 0. 0. 0.000 0.000 \n"); + /* Final well data */ + fprintf(model_file, " -40.000 -25.000 0.000 0.000 0.000\n"); + /* other wells */ + for (i = 0; i < count_current_solutions - 1; i++) + { + fprintf(model_file, + " -40.000 -25.000 0.000 0.000 0.000 100.000\n"); + } + fprintf(model_file, "\n"); + fclose(model_file); + state = INVERSE; + return (OK); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +print_total_pat(FILE * l_netpath_file, const char *elt, const char *string) +/* ---------------------------------------------------------------------- */ +{ + LDBLE d; + d = 1000.0 * total(elt); + if (strcmp(elt,"O(0)") == 0) + { + d = d/2.0; + } + if (d == 0) + { + fprintf(l_netpath_file, "%14g%1s # %s\n", (double) d, "*", string); + } + else + { + fprintf(l_netpath_file, "%14g%1s # %s\n", (double) d, " ", string); + } +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_initial_solution(int n_user_old, int n_user_new) +/* ---------------------------------------------------------------------- */ +{ + cxxSolution *solution_ptr; + Utilities::Rxn_copy(Rxn_solution_map, n_user_old, n_user_new); + Rxn_new_solution.insert(n_user_new); + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, n_user_new); + solution_ptr->Set_new_def(true); + if (solution_ptr->Get_initial_data() == NULL) + solution_ptr->Create_initial_data(); + solution_ptr->Set_n_user_end(n_user_new); + cxxNameDouble::iterator jit = solution_ptr->Get_totals().begin(); + for ( ; jit != solution_ptr->Get_totals().end(); jit++) + { + cxxISolutionComp temp_comp; + temp_comp.Set_description(jit->first.c_str()); + temp_comp.Set_input_conc(jit->second / solution_ptr->Get_mass_water()); + temp_comp.Set_units("Mol/kgw"); + solution_ptr->Get_initial_data()->Get_comps()[jit->first.c_str()] = temp_comp; + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_to_file(const char *filename, const char *string) +/* ---------------------------------------------------------------------- */ +{ + FILE *model_file; + char c; + int i; + char string_line[MAX_LINE]; + + model_file = fopen(filename, "r"); + if (model_file == NULL) + { + model_file = fopen(filename, "w"); + } + if (model_file == NULL) + { + error_string = sformatf( "Can`t open file, %s.", filename); + error_msg(error_string, STOP); +#if !defined(R_SO) + exit(4); +#endif + } + i = 0; + /* + * Read each line of file, check if equal to string; if not, append string to file + */ + for (;;) + { + c = getc(model_file); + if (c != EOF && c != '\n' && i != MAX_LINE) + { + string_line[i] = c; + i++; + continue; + } + if (i >= MAX_LINE) + { + string_line[MAX_LINE - 1] = '\0'; + error_string = sformatf( "File name in %s is greater than %d characters: %s\n", filename, MAX_LINE, string_line); + warning_msg(error_string); + } + else + { + string_line[i] = '\0'; + } + /* new line or eof */ + string_trim(string_line); + if (strcmp(string_line, string) == 0) + { + fclose(model_file); + return (OK); + } + /* eof, add line */ + if (c == EOF) + { + fclose(model_file); + model_file = fopen(filename, "a"); + if (model_file) + { + fprintf(model_file, "%s\n", string); + fclose(model_file); + } + else + { + error_string = sformatf("Could not open netpath model file: %s\n", filename); + error_msg(error_string, STOP); + } + return (OK); + } + /* new line */ + i = 0; + } +} + diff --git a/phreeqcpp/isotopes.cpp b/phreeqcpp/isotopes.cpp new file mode 100644 index 00000000..4ced82a5 --- /dev/null +++ b/phreeqcpp/isotopes.cpp @@ -0,0 +1,2122 @@ +#include "Phreeqc.h" +#include "phqalloc.h" +#include "Solution.h" + + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_isotopes(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads master species information for isotopes + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + + int l; + struct master_isotope *master_isotope_ptr; + char token[MAX_LENGTH]; + struct element *elt_ptr; + + int return_value, opt, opt_save; + char *next_char; + const char *opt_list[] = { + "isotope", /* 0 */ + "total_is_major" /* 1 */ + }; + int count_opt_list = 2; + + master_isotope_ptr = NULL; + elt_ptr = NULL; +/* + * Read name followed by options + */ + opt_save = OPTION_DEFAULT; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SPECIES keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* isotope */ + if (elt_ptr == NULL) + { + error_string = sformatf( + "The element of which this isotope is a minor isotope has not been defined, %s. ISOTOPES data block.", + line); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + /* + * Save an isotope + */ + master_isotope_ptr = NULL; + copy_token(token, &next_char, &l); + master_isotope_ptr = master_isotope_store(token, TRUE); + master_isotope_ptr->elt = elt_ptr; + master_isotope_ptr->minor_isotope = TRUE; + master_isotope_ptr->total_is_major = FALSE; + /* + * Read units + */ + if (copy_token(token, &next_char, &l) == EMPTY) + { + error_string = sformatf( + "Expecting units for isotopic values, %s. ISOTOPES data block.", + line); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + master_isotope_ptr->units = string_hsave(token); + /* + * Read standard + */ + if (copy_token(token, &next_char, &l) == EMPTY) + { + error_string = sformatf( + "Expecting isotope ratio of standard, %s. ISOTOPES data block.", + line); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + sscanf(token, SCANFORMAT, &(master_isotope_ptr->standard)); + opt_save = OPTION_DEFAULT; + break; + case 1: /* total_is_major_isotope */ + error_string = sformatf( + "Obsolete identifier. The total of the element must be the sum of all isotopes. ISOTOPES data block.\n%s", + line); + warning_msg(error_string); + break; + case OPTION_DEFAULT: +/* + * Read and element name + */ + if (copy_token(token, &next_char, &l) == EMPTY) + { + error_string = sformatf( + "Expecting an element name for isotope definition, %s. ISOTOPES data block.", + line); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + elt_ptr = element_store(token); + master_isotope_ptr = master_isotope_store(token, TRUE); + master_isotope_ptr->elt = elt_ptr; + master_isotope_ptr->minor_isotope = FALSE; + master_isotope_ptr->total_is_major = FALSE; + opt_save = OPTION_DEFAULT; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_calculate_values(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads basic code with which to calculate calculate_value + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + char *ptr; + int l, length, line_length; + int return_value, opt, opt_save; + char token[MAX_LENGTH]; + struct calculate_value *calculate_value_ptr; + char *description; + int n_user, n_user_end; + char *next_char; + const char *opt_list[] = { + "start", /* 0 */ + "end" /* 1 */ + }; + int count_opt_list = 2; +/* + * Read advection number (not currently used) + */ + ptr = line; + read_number_description(ptr, &n_user, &n_user_end, &description); + description = (char *) free_check_null(description); + opt_save = OPTION_DEFAULT; +/* + * Read lines + */ + return_value = UNKNOWN; + calculate_value_ptr = NULL; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in CALCULATE_VALUE keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* start */ + opt_save = OPT_1; + break; + case 1: /* end */ + opt_save = OPTION_DEFAULT; + break; + case OPTION_DEFAULT: /* read calculate_value name */ +/* + * Read calculate_value name + */ + if (copy_token(token, &next_char, &l) == EMPTY) + { + error_string = sformatf( + "Expecting a name for calculate_value definition, %s. CALCULATE_VALUES data block.", + line); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + calculate_value_ptr = calculate_value_store(token, TRUE); + calculate_value_ptr->new_def = TRUE; + calculate_value_ptr->commands = + (char *) PHRQ_malloc(sizeof(char)); + if (calculate_value_ptr->commands == NULL) + { + malloc_error(); + } + else + { + calculate_value_ptr->commands[0] = '\0'; + calculate_value_ptr->linebase = NULL; + calculate_value_ptr->varbase = NULL; + calculate_value_ptr->loopbase = NULL; + } + opt_save = OPT_1; + break; + + case OPT_1: /* read command */ + if (calculate_value_ptr) + { + length = (int) strlen(calculate_value_ptr->commands); + line_length = (int) strlen(line); + calculate_value_ptr->commands = + (char *) PHRQ_realloc(calculate_value_ptr->commands, + (size_t) (length + line_length + + 2) * sizeof(char)); + if (calculate_value_ptr->commands == NULL) + malloc_error(); + calculate_value_ptr->commands[length] = ';'; + calculate_value_ptr->commands[length + 1] = '\0'; + strcat((calculate_value_ptr->commands), line); + opt_save = OPT_1; + } + else + { + error_string = sformatf( + "Expecting a calculate_value definition, %s. CALCULATE_VALUES data block.", + line); + error_msg(error_string, CONTINUE); + input_error++; + } + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } +/* output_msg(sformatf( "%s", calculate_value[0].commands)); + */ return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_isotope_ratios(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads isotope_ratio info, ratios are calculated with + * Basic programs read in CALCULATE_VALUE data block + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + char *ptr; + int l; + int return_value, opt, opt_save; + char token[MAX_LENGTH]; + struct isotope_ratio *isotope_ratio_ptr; + char *description; + int n_user, n_user_end; + char *next_char; + const char *opt_list[] = { + "no_options" /* 0 */ + }; + int count_opt_list = 0; +/* + * Read number (not currently used) + */ + ptr = line; + read_number_description(ptr, &n_user, &n_user_end, &description); + description = (char *) free_check_null(description); + opt_save = OPTION_DEFAULT; +/* + * Read lines + */ + return_value = UNKNOWN; + isotope_ratio_ptr = NULL; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in ISOTOPE_RATIOS keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case OPTION_DEFAULT: /* read isotope_ratio name */ +/* + * Read isotope_ratio name + */ + if (copy_token(token, &next_char, &l) == EMPTY) + { + error_string = sformatf( + "Expecting a name for isotope_ratio definition, %s. ISOTOPE_RATIOS data block.", + line); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + isotope_ratio_ptr = isotope_ratio_store(token, TRUE); + /* + * Read isotope + */ + if (copy_token(token, &next_char, &l) == EMPTY) + { + error_string = sformatf( + "Expecting a name of isotope for an isotope_ratio definition, %s. ISOTOPE_RATIOS data block.", + line); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + isotope_ratio_ptr->isotope_name = string_hsave(token); + opt_save = OPTION_DEFAULT; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_isotope_alphas(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads isotope_alpha info, ratios are calculated with + * Basic programs read in CALCULATE_VALUE data block + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + char *ptr; + int l; + int return_value, opt, opt_save; + char token[MAX_LENGTH]; + struct isotope_alpha *isotope_alpha_ptr; + char *description; + int n_user, n_user_end; + char *next_char; + const char *opt_list[] = { + "no_options" /* 0 */ + }; + int count_opt_list = 0; +/* + * Read number (not currently used) + */ + ptr = line; + read_number_description(ptr, &n_user, &n_user_end, &description); + description = (char *) free_check_null(description); + opt_save = OPTION_DEFAULT; +/* + * Read lines + */ + return_value = UNKNOWN; + isotope_alpha_ptr = NULL; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in ISOTOPE_ALPHAS keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case OPTION_DEFAULT: /* read isotope_alpha name */ +/* + * Read isotope_alpha name + */ + if (copy_token(token, &next_char, &l) == EMPTY) + { + error_string = sformatf( + "Expecting a name for isotope_alpha definition, %s. ISOTOPE_ALPHAS data block.", + line); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + isotope_alpha_ptr = isotope_alpha_store(token, TRUE); + isotope_alpha_ptr->name = string_hsave(token); + if (copy_token(token, &next_char, &l) != EMPTY) + { + isotope_alpha_ptr->named_logk = string_hsave(token); + } + + + opt_save = OPTION_DEFAULT; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_isotopes(cxxSolution &solution_ref) +/* ---------------------------------------------------------------------- */ +{ + int i; + struct master_isotope *master_isotope_ptr; + LDBLE total_moles; + /* + * zero out isotopes + */ + for (i = 0; i < count_master_isotope; i++) + { + master_isotope[i]->moles = 0; + } + master_isotope_ptr = master_isotope_search("H"); + if (master_isotope_ptr != NULL) + { + total_moles = total_h_x; + calculate_isotope_moles(master_isotope_ptr->elt, &solution_ref, + total_moles); + } + master_isotope_ptr = master_isotope_search("O"); + if (master_isotope_ptr != NULL) + { + total_moles = total_o_x; + calculate_isotope_moles(master_isotope_ptr->elt, &solution_ref, + total_moles); + } + cxxNameDouble::iterator it = solution_ref.Get_totals().begin(); + for ( ; it != solution_ref.Get_totals().end(); it++) + { + master_isotope_ptr = master_isotope_search(it->first.c_str()); + if (master_isotope_ptr == NULL) + continue; + if (master_isotope_ptr->minor_isotope == FALSE) + { + total_moles = total(master_isotope_ptr->name) * mass_water_aq_x; + calculate_isotope_moles(master_isotope_ptr->elt, &solution_ref, + total_moles); + } + } + /* + * Set isotopes flag + */ + initial_solution_isotopes = FALSE; + for (i = 0; i < count_master_isotope; i++) + { + if (master_isotope[i]->minor_isotope == TRUE + && master_isotope[i]->moles > 0) + { + initial_solution_isotopes = TRUE; + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calculate_isotope_moles(struct element *elt_ptr, + cxxSolution *solution_ptr, LDBLE total_moles) +/* ---------------------------------------------------------------------- */ +{ + int i, j, l_iter; + int count_isotopes, total_is_major; + struct master_isotope *master_isotope_ptr, *master_isotope_ptr1; + struct master_isotope list[MAX_ELTS]; + LDBLE m_major, tot; + /* + * Get total concentration of elt_ptr + */ + if (total_moles <= 0) + { + error_string = sformatf( + "Cannot calculate molality of isotopes, molality of element is zero, %s", + elt_ptr->name); + warning_msg(error_string); + return (ERROR); + } + m_major = total_moles; + /* + * Make a list of isotopes + */ + count_isotopes = 0; + total_is_major = FALSE; + master_isotope_ptr = master_isotope_search("H"); + if ((master_isotope_ptr != NULL) && (master_isotope_ptr->elt == elt_ptr)) + { + memcpy(&(list[count_isotopes]), master_isotope_ptr, + sizeof(struct master_isotope)); + list[count_isotopes].ratio = 1.0; + if (list[count_isotopes].minor_isotope == FALSE) + { + total_is_major = list[count_isotopes].total_is_major; + } + count_isotopes++; + } + master_isotope_ptr = master_isotope_search("O"); + if ((master_isotope_ptr != NULL) && (master_isotope_ptr->elt == elt_ptr)) + { + memcpy(&(list[count_isotopes]), master_isotope_ptr, + sizeof(struct master_isotope)); + list[count_isotopes].ratio = 1.0; + if (list[count_isotopes].minor_isotope == FALSE) + { + total_is_major = list[count_isotopes].total_is_major; + } + count_isotopes++; + } + if (solution_ptr->Get_initial_data() != NULL) + { + std::map::iterator it = solution_ptr->Get_initial_data()->Get_comps().begin(); + for ( ; it != solution_ptr->Get_initial_data()->Get_comps().end(); it++) + { + master_isotope_ptr = master_isotope_search(it->first.c_str()); + if (master_isotope_ptr == NULL) + continue; + if (master_isotope_ptr->elt != elt_ptr) + continue; + memcpy(&(list[count_isotopes]), master_isotope_ptr, + sizeof(struct master_isotope)); + if (list[count_isotopes].minor_isotope == FALSE) + { + total_is_major = list[count_isotopes].total_is_major; + } + count_isotopes++; + } + } + /* + * Loop to calculate isotope molalities + */ + for (l_iter = 0; l_iter < itmax; l_iter++) + { + tot = 0; + for (i = 0; i < count_isotopes; i++) + { + if (list[i].minor_isotope == FALSE) + { + list[i].moles = m_major; + tot += m_major; + continue; + } + if (strcmp_nocase(list[i].units, "permil") == 0) + { + from_permil(&(list[i]), m_major); + tot += list[i].moles; + continue; + } + if (strcmp_nocase(list[i].units, "pct") == 0) + { + from_pct(&(list[i]), total_moles); + tot += list[i].moles; + continue; + } + if (strcmp_nocase(list[i].units, "pmc") == 0) + { + from_pct(&(list[i]), total_moles); + tot += list[i].moles; + continue; + } + if (strcmp_nocase(list[i].units, "tu") == 0) + { + from_tu(&(list[i])); + tot += list[i].moles; + continue; + } + if (strcmp_nocase(list[i].units, "pci/l") == 0) + { + from_pcil(&(list[i])); + tot += list[i].moles; + continue; + } + error_string = sformatf( "Isotope units not recognized, %s", + list[i].units); + input_error++; + error_msg(error_string, CONTINUE); + } + if (total_is_major == TRUE) + break; + if (fabs(total_moles - tot) < convergence_tolerance * total_moles) + { + break; + } + else + { + m_major = m_major * total_moles / tot; + } + } + if (l_iter >= itmax) + { + error_msg("Failed to converge in CALCULATE_ISOTOPE_MOLES.", STOP); + } + /* + * Update master_isotope + */ + for (j = 0; j < count_master_isotope; j++) + { + for (i = 0; i < count_isotopes; i++) + { + if (list[i].name == master_isotope[j]->name) + { + memcpy(master_isotope[j], &(list[i]), + sizeof(struct master_isotope)); + } + } + } + /* + * Update solution + */ + master_isotope_ptr1 = master_isotope_search("H"); + if (master_isotope_ptr1 != NULL && master_isotope_ptr1->elt == elt_ptr) + { + total_h_x = m_major; + } + master_isotope_ptr1 = master_isotope_search("O"); + if (master_isotope_ptr1 != NULL && master_isotope_ptr1->elt == elt_ptr) + { + total_o_x = m_major; + } + //cxxNameDouble nd(solution_ptr->Get_totals()); + //cxxNameDouble::iterator iit = solution_ptr->Get_totals().begin(); + //for ( ; iit != solution_ptr->Get_totals().end(); iit++) + //{ + // master_isotope_ptr = master_isotope_search(iit->first.c_str()); + // if (master_isotope_ptr == NULL) + // continue; + // if (master_isotope_ptr->elt != elt_ptr) + // continue; + // nd[iit->first] = master_isotope_ptr->moles; + //} + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +from_permil(struct master_isotope *master_isotope_ptr, LDBLE major_total) +/* ---------------------------------------------------------------------- */ +{ + LDBLE r; + + r = (master_isotope_ptr->ratio / 1000. + + 1.0) * master_isotope_ptr->standard; + master_isotope_ptr->moles = major_total * r; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +from_pct(struct master_isotope *master_isotope_ptr, LDBLE total_moles) +/* ---------------------------------------------------------------------- */ +{ + master_isotope_ptr->moles = + master_isotope_ptr->ratio / 100 * master_isotope_ptr->standard * + total_moles; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +from_tu(struct master_isotope *master_isotope_ptr) +/* ---------------------------------------------------------------------- */ +{ + master_isotope_ptr->moles = + master_isotope_ptr->ratio * master_isotope_ptr->standard * + mass_water_aq_x / gfw_water; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +from_pcil(struct master_isotope *master_isotope_ptr) +/* ---------------------------------------------------------------------- */ +{ + master_isotope_ptr->moles = + master_isotope_ptr->ratio * master_isotope_ptr->standard * + mass_water_aq_x / gfw_water; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_initial_solution_isotopes(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Print isotopes for initial solution + */ + int i, j; + int print_isotope; + + if (pr.initial_isotopes == FALSE || pr.all == FALSE) + return (OK); + if (state != INITIAL_SOLUTION) + return (OK); + if (initial_solution_isotopes == FALSE) + return (OK); +/* + * Print heading + */ + print_centered("Isotopes"); + output_msg(sformatf( "%10s\t%12s\t%12s\t%12s\t%12s\n\n", "Isotope", + "Molality", "Moles", "Ratio", "Units")); + for (i = 0; i < count_master_isotope; i++) + { + if (master_isotope[i]->minor_isotope == FALSE) + { + print_isotope = FALSE; + for (j = 0; j < count_master_isotope; j++) + { + if ((master_isotope[j]->elt == master_isotope[i]->elt) && + (master_isotope[j]->minor_isotope == TRUE) && + (master_isotope[j]->moles > 0)) + { + print_isotope = TRUE; + break; + } + } + if (print_isotope == FALSE) + continue; + /* + * Print isotope values + */ + output_msg(sformatf( "%10s\t%12.5e\t%12.5e\n", + master_isotope[i]->name, + (double) (master_isotope[i]->moles / mass_water_aq_x), + (double) master_isotope[i]->moles)); + for (j = 0; j < count_master_isotope; j++) + { + if (i == j) + continue; + if ((master_isotope[j]->elt == master_isotope[i]->elt) && + (master_isotope[j]->minor_isotope == TRUE)) + { + output_msg(sformatf( + "%10s\t%12.5e\t%12.5e\t%12.5e\t%12s\n", + master_isotope[j]->name, + (double) (master_isotope[j]->moles / + mass_water_aq_x), + (double) master_isotope[j]->moles, + (double) master_isotope[j]->ratio, + master_isotope[j]->units)); + } + } + output_msg(sformatf( "\n")); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_isotopes(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Punch isotope ratios relative to standards + */ + //int i; + LDBLE iso; + struct isotope_ratio *isotope_ratio_ptr; + struct master_isotope *master_isotope_ptr; + + //if (punch.in == FALSE || punch.isotopes == FALSE) + // return (OK); + if (current_selected_output->Get_isotopes().size() == 0) + return(OK); + //if (punch.count_isotopes == 0) + // return (OK); + //for (i = 0; i < punch.count_isotopes; i++) + for (size_t i = 0; i < current_selected_output->Get_isotopes().size(); i++) + { + iso = MISSING; + if (state == INITIAL_SOLUTION) + { + isotope_ratio_ptr = isotope_ratio_search(current_selected_output->Get_isotopes()[i].first.c_str()); + if (isotope_ratio_ptr != NULL) + { + master_isotope_ptr = + master_isotope_search(isotope_ratio_ptr->isotope_name); + if (master_isotope_ptr != NULL + && master_isotope_ptr->minor_isotope == TRUE) + { + iso = master_isotope_ptr->ratio; + } + } + } + else + { + isotope_ratio_ptr = isotope_ratio_search(current_selected_output->Get_isotopes()[i].first.c_str()); + if (isotope_ratio_ptr != NULL) + { + iso = isotope_ratio_ptr->converted_ratio; + } + } + if (!current_selected_output->Get_high_precision()) + { + fpunchf(sformatf("I_%s", current_selected_output->Get_isotopes()[i].first.c_str()), "%12.4e\t", + (double) iso); + } + else + { + fpunchf(sformatf("I_%s", current_selected_output->Get_isotopes()[i].first.c_str()), "%20.12e\t", + (double) iso); + } + + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_calculate_values(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Punch calculate values + */ + //int i; + LDBLE result; + struct calculate_value *calculate_value_ptr; + char l_command[] = "run"; + + if (current_selected_output->Get_calculate_values().size() == 0) + return OK; + //if (punch.in == FALSE || punch.calculate_values == FALSE) + // return (OK); + //if (punch.count_calculate_values == 0) + // return (OK); + //for (i = 0; i < punch.count_calculate_values; i++) + for (size_t i = 0; i < current_selected_output->Get_calculate_values().size(); i++) + { + result = MISSING; + calculate_value_ptr = + calculate_value_search(current_selected_output->Get_calculate_values()[i].first.c_str()); + if (!calculate_value_ptr) + { + error_string = sformatf( + "Definition not found for CALCULATE_VALUES %s.", + current_selected_output->Get_calculate_values()[i].first.c_str()); + error_msg(error_string, STOP); +#if !defined(R_SO) + exit(4); +#endif + } + + if (calculate_value_ptr->calculated == FALSE) + { + rate_moles = NAN; + if (calculate_value_ptr->new_def == TRUE) + { + if (basic_compile + (calculate_value_ptr->commands, &calculate_value_ptr->linebase, + &calculate_value_ptr->varbase, + &calculate_value_ptr->loopbase) != 0) + { + error_string = sformatf( + "Fatal Basic error in CALCULATE_VALUES %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } + calculate_value_ptr->new_def = FALSE; + } + if (basic_run + (l_command, calculate_value_ptr->linebase, + calculate_value_ptr->varbase, calculate_value_ptr->loopbase) != 0) + { + error_string = sformatf( "Fatal Basic error in calculate_value %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } +#ifdef NPP + if (isnan(rate_moles)) +#else + if (rate_moles == NAN) +#endif + { + error_string = sformatf( "Calculated value not SAVEed for %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } + else + { + calculate_value_ptr->calculated = TRUE; + calculate_value_ptr->value = rate_moles; + } + } + if (calculate_value_ptr != NULL) + { + result = calculate_value_ptr->value; + } + if (!current_selected_output->Get_high_precision()) + { + fpunchf(sformatf("V_%s", current_selected_output->Get_calculate_values()[i].first.c_str()), + "%12.4e\t", (double) result); + } + else + { + fpunchf(sformatf("V_%s", current_selected_output->Get_calculate_values()[i].first.c_str()), + "%20.12e\t", (double) result); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_isotope_ratios(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Print isotopes for initial solution + */ + int i, j; + int print_isotope; + struct master *master_ptr; + struct master_isotope *master_isotope_ptr; + char token[MAX_LENGTH]; + + + if (pr.isotope_ratios == FALSE || pr.all == FALSE) + return (OK); + if (state == INITIAL_SOLUTION) + return (OK); +/* + * Print heading + */ + print_isotope = FALSE; + for (i = 0; i < count_master_isotope; i++) + { + if (master_isotope[i]->minor_isotope == FALSE) + continue; + master_ptr = master_bsearch(master_isotope[i]->name); + if (master_ptr == NULL) + continue; + if (master_ptr->total > 0 || master_ptr->s->moles > 0) + { + print_isotope = TRUE; + break; + } + } + if (print_isotope == FALSE) + return (OK); + + print_centered("Isotope Ratios"); + output_msg(sformatf( "%25s\t%12s\t%15s\n\n", "Isotope Ratio", + "Ratio", "Input Units")); + + for (j = 0; j < count_isotope_ratio; j++) + { + if (isotope_ratio[j]->ratio == MISSING) + continue; + master_isotope_ptr = + master_isotope_search(isotope_ratio[j]->isotope_name); + /* + * Print isotope ratio + */ + strcpy(token, isotope_ratio[j]->name); + while (replace("_", " ", token) == TRUE); + output_msg(sformatf( " %-20s\t%12.5e\t%15.5g %-10s\n", + token, (double) isotope_ratio[j]->ratio, + (double) isotope_ratio[j]->converted_ratio, + master_isotope_ptr->units)); + } + output_msg(sformatf( "\n")); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_isotope_alphas(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Print isotopes for initial solution + */ + int i, j; + int print_isotope; + struct master *master_ptr; + char token[MAX_LENGTH]; + LDBLE log_alpha; + + if (pr.isotope_alphas == FALSE || pr.all == FALSE) + return (OK); + if (state == INITIAL_SOLUTION) + return (OK); +/* + * Print heading + */ + print_isotope = FALSE; + for (i = 0; i < count_master_isotope; i++) + { + if (master_isotope[i]->minor_isotope == FALSE) + continue; + master_ptr = master_bsearch(master_isotope[i]->name); + if (master_ptr == NULL) + continue; + if (master_ptr->total > 0 || master_ptr->s->moles > 0) + { + print_isotope = TRUE; + break; + } + } + if (print_isotope == FALSE) + return (OK); + + print_centered("Isotope Alphas"); + output_msg(sformatf( "%75s\n", "1000ln(Alpha)")); + output_msg(sformatf( "%79s\n", "----------------------")); + output_msg(sformatf( "%-37s%14s%14s%12.1f C\n\n", + " Isotope Ratio", "Solution alpha", "Solution", + (double) tc_x)); + + for (j = 0; j < count_isotope_alpha; j++) + { + if (isotope_alpha[j]->value == MISSING) + continue; + /* + * Print isotope ratio + */ + strcpy(token, isotope_alpha[j]->name); + while (replace("_", " ", token) == TRUE); + if (isotope_alpha[j]->named_logk != NULL) + { + if (isotope_alpha[j]->value <= 0) + { + log_alpha = -999.999; + } + else + { + log_alpha = 1000 * log(isotope_alpha[j]->value); + } + output_msg(sformatf( "%-37s%14.5g%14.5g%14.5g\n", token, + (double) isotope_alpha[j]->value, (double) log_alpha, + (double) (1000 * + calc_logk_n(isotope_alpha[j]->named_logk) * + LOG_10))); + } + else + { + output_msg(sformatf( "%-37s%14.5g%14.5g\n", token, + (double) isotope_alpha[j]->value, + (double) (1000 * log(isotope_alpha[j]->value)))); + } + + } + output_msg(sformatf( "\n")); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calculate_values(void) +/* ---------------------------------------------------------------------- */ +{ + int j; + struct calculate_value *calculate_value_ptr; + struct isotope_ratio *isotope_ratio_ptr; + struct isotope_alpha *isotope_alpha_ptr; + struct master_isotope *master_isotope_ptr; + char l_command[] = "run"; + + + /* + * initialize ratios as missing + */ + for (j = 0; j < count_calculate_value; j++) + { + calculate_value[j]->calculated = FALSE; + calculate_value[j]->value = MISSING; + } +#ifdef SKIP + for (j = 0; j < count_calculate_value; j++) + { + calculate_value_ptr = calculate_value[j]; + rate_moles = NAN; + if (calculate_value_ptr->new_def == TRUE) + { + if (basic_compile + (calculate_value[j]->commands, &calculate_value[j]->linebase, + &calculate_value[j]->varbase, + &calculate_value[j]->loopbase) != 0) + { + error_string = sformatf( + "Fatal Basic error in CALCULATE_VALUES %s.", + calculate_value[j]->name); + error_msg(error_string, STOP); + } + calculate_value_ptr->new_def = FALSE; + } + if (basic_run + (l_command, calculate_value[j]->linebase, + calculate_value[j]->varbase, calculate_value[j]->loopbase) != 0) + { + error_string = sformatf( "Fatal Basic error in calculate_value %s.", + calculate_value[j]->name); + error_msg(error_string, STOP); + } + if (rate_moles == NAN) + { + error_string = sformatf( "Calculated value not SAVEed for %s.", + calculate_value[j]->name); + error_msg(error_string, STOP); + } + else + { + calculate_value[j]->calculated = TRUE; + calculate_value[j]->value = rate_moles; + } + } +#endif + if (pr.isotope_ratios == TRUE) + { + for (j = 0; j < count_isotope_ratio; j++) + { + isotope_ratio_ptr = isotope_ratio[j]; + master_isotope_ptr = + master_isotope_search(isotope_ratio_ptr->isotope_name); + if (master_isotope_ptr->master->s->in == FALSE) continue; + calculate_value_ptr = calculate_value_search(isotope_ratio_ptr->name); + if (calculate_value_ptr->calculated == FALSE) + { + rate_moles = NAN; + if (calculate_value_ptr->new_def == TRUE) + { + if (basic_compile + (calculate_value_ptr->commands, &calculate_value_ptr->linebase, + &calculate_value_ptr->varbase, + &calculate_value_ptr->loopbase) != 0) + { + error_string = sformatf( + "Fatal Basic error in CALCULATE_VALUES %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } + calculate_value_ptr->new_def = FALSE; + } + if (basic_run + (l_command, calculate_value_ptr->linebase, + calculate_value_ptr->varbase, calculate_value_ptr->loopbase) != 0) + { + error_string = sformatf( "Fatal Basic error in calculate_value %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } +#ifdef NPP + if (isnan(rate_moles)) +#else + if (rate_moles == NAN) +#endif + { + error_string = sformatf( "Calculated value not SAVEed for %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } + else + { + calculate_value_ptr->calculated = TRUE; + calculate_value_ptr->value = rate_moles; + } + } + /* + * Calculate converted isotope ratio + */ + if (calculate_value_ptr->value == MISSING) + { + isotope_ratio_ptr->ratio = MISSING; + isotope_ratio_ptr->converted_ratio = MISSING; + } + else + { + isotope_ratio_ptr->ratio = calculate_value_ptr->value; + isotope_ratio_ptr->converted_ratio = + convert_isotope(master_isotope_ptr, + calculate_value_ptr->value); + } + } + } + if (pr.isotope_alphas == TRUE) + { + for (j = 0; j < count_isotope_alpha; j++) + { + isotope_alpha_ptr = isotope_alpha[j]; + calculate_value_ptr = calculate_value_search(isotope_alpha_ptr->name); + /* + * Calculate converted isotope ratio + */ + if (calculate_value_ptr->calculated == FALSE) + { + rate_moles = NAN; + if (calculate_value_ptr->new_def == TRUE) + { + if (basic_compile + (calculate_value_ptr->commands, &calculate_value_ptr->linebase, + &calculate_value_ptr->varbase, + &calculate_value_ptr->loopbase) != 0) + { + error_string = sformatf( + "Fatal Basic error in CALCULATE_VALUES %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } + calculate_value_ptr->new_def = FALSE; + } + if (basic_run + (l_command, calculate_value_ptr->linebase, + calculate_value_ptr->varbase, calculate_value_ptr->loopbase) != 0) + { + error_string = sformatf( "Fatal Basic error in calculate_value %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } +#ifdef NPP + if (isnan(rate_moles)) +#else + if (rate_moles == NAN) +#endif + { + error_string = sformatf( "Calculated value not SAVEed for %s.", + calculate_value_ptr->name); + error_msg(error_string, STOP); + } + else + { + calculate_value_ptr->calculated = TRUE; + calculate_value_ptr->value = rate_moles; + } + } + if (calculate_value_ptr->value == MISSING) + { + isotope_alpha_ptr->value = MISSING; + } + else + { + isotope_alpha_ptr->value = calculate_value_ptr->value; + } + } + } + return (OK); +} +#ifdef SKIP +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calculate_values(void) +/* ---------------------------------------------------------------------- */ +{ + int j; + struct calculate_value *calculate_value_ptr; + struct isotope_ratio *isotope_ratio_ptr; + struct isotope_alpha *isotope_alpha_ptr; + struct master_isotope *master_isotope_ptr; + char l_command[] = "run"; + + + /* + * initialize ratios as missing + */ + for (j = 0; j < count_calculate_value; j++) + { + calculate_value[j]->calculated = FALSE; + calculate_value[j]->value = MISSING; + } + for (j = 0; j < count_calculate_value; j++) + { + calculate_value_ptr = calculate_value[j]; + rate_moles = NAN; + if (calculate_value_ptr->new_def == TRUE) + { + if (basic_compile + (calculate_value[j]->commands, &calculate_value[j]->linebase, + &calculate_value[j]->varbase, + &calculate_value[j]->loopbase) != 0) + { + error_string = sformatf( + "Fatal Basic error in CALCULATE_VALUES %s.", + calculate_value[j]->name); + error_msg(error_string, STOP); + } + calculate_value_ptr->new_def = FALSE; + } + if (basic_run + (l_command, calculate_value[j]->linebase, + calculate_value[j]->varbase, calculate_value[j]->loopbase) != 0) + { + error_string = sformatf( "Fatal Basic error in calculate_value %s.", + calculate_value[j]->name); + error_msg(error_string, STOP); + } + if (rate_moles == NAN) + { + error_string = sformatf( "Calculated value not SAVEed for %s.", + calculate_value[j]->name); + error_msg(error_string, STOP); + } + else + { + calculate_value[j]->calculated = TRUE; + calculate_value[j]->value = rate_moles; + } + } + for (j = 0; j < count_isotope_ratio; j++) + { + isotope_ratio_ptr = isotope_ratio[j]; + master_isotope_ptr = + master_isotope_search(isotope_ratio_ptr->isotope_name); + calculate_value_ptr = calculate_value_search(isotope_ratio_ptr->name); + /* + * Calculate converted isotope ratio + */ + if (calculate_value_ptr->value == MISSING) + { + isotope_ratio_ptr->ratio = MISSING; + isotope_ratio_ptr->converted_ratio = MISSING; + } + else + { + isotope_ratio_ptr->ratio = calculate_value_ptr->value; + isotope_ratio_ptr->converted_ratio = + convert_isotope(master_isotope_ptr, + calculate_value_ptr->value); + } + } + for (j = 0; j < count_isotope_alpha; j++) + { + isotope_alpha_ptr = isotope_alpha[j]; + calculate_value_ptr = calculate_value_search(isotope_alpha_ptr->name); + /* + * Calculate converted isotope ratio + */ + if (calculate_value_ptr->value == MISSING) + { + isotope_alpha_ptr->value = MISSING; + } + else + { + isotope_alpha_ptr->value = calculate_value_ptr->value; + } + } + return (OK); +} +#endif +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +convert_isotope(struct master_isotope * master_isotope_ptr, LDBLE ratio) +/* ---------------------------------------------------------------------- */ +{ + const char *units; + units = master_isotope_ptr->units; + + if (strcmp_nocase(units, "permil") == 0) + { + return ((ratio / master_isotope_ptr->standard - 1) * 1000); + } + if (strcmp_nocase(units, "pct") == 0) + { + return (ratio / master_isotope_ptr->standard * 100.); + } + if (strcmp_nocase(units, "pmc") == 0) + { + return (ratio / master_isotope_ptr->standard * 100.); + } + if (strcmp_nocase(units, "tu") == 0) + { + return (ratio / master_isotope_ptr->standard); + } + if (strcmp_nocase(units, "pci/l") == 0) + { + return (ratio / master_isotope_ptr->standard); + } + error_string = sformatf( + "Did not recognize isotope units in convert_isotope, %s", units); + error_msg(error_string, STOP); + return (-99.0); +} + +/* + * Utility routines for master_isotope + */ + +/* ---------------------------------------------------------------------- */ +struct master_isotope * Phreeqc:: +master_isotope_store(const char *name, int replace_if_found) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the hash table for master_isotope. + * + * Pointer to a master_isotope structure is always returned. + * + * If the string is not found, a new entry is made in the hash table. Pointer to + * the new structure is returned. + * If "name" is found and replace is true, pointers in old master_isotope structure + * are freed and replaced with additional input. + * If "name" is found and replace is false, the old master_isotope structure is not + * modified and a pointer to it is returned. + * + * Arguments: + * name input, character string to be found in "master_isotope". + * replace_if_found input, TRUE means reinitialize master_isotope structure if found + * FALSE means just return pointer if found. + * + * Returns: + * pointer to master_isotope structure "master_isotope" where "name" can be found. + */ + int n; + struct master_isotope *master_isotope_ptr; + ENTRY item, *found_item; + char token[MAX_LENGTH]; +/* + * Search list + */ + strcpy(token, name); + + item.key = token; + item.data = NULL; + found_item = hsearch_multi(master_isotope_hash_table, item, FIND); + + if (found_item != NULL && replace_if_found == FALSE) + { + master_isotope_ptr = (struct master_isotope *) (found_item->data); + return (master_isotope_ptr); + } + else if (found_item != NULL && replace_if_found == TRUE) + { + master_isotope_ptr = (struct master_isotope *) (found_item->data); + master_isotope_init(master_isotope_ptr); + } + else + { + n = count_master_isotope++; + /* make sure there is space in s */ + if (count_master_isotope >= max_master_isotope) + { + space((void **) ((void *) &master_isotope), count_master_isotope, + &max_master_isotope, sizeof(struct master_isotope *)); + } + /* Make new master_isotope structure */ + master_isotope[n] = master_isotope_alloc(); + master_isotope_ptr = master_isotope[n]; + } + /* set name and z in pointer in master_isotope structure */ + master_isotope_ptr->name = string_hsave(token); +/* + * Update hash table + */ + item.key = master_isotope_ptr->name; + item.data = (void *) master_isotope_ptr; + found_item = hsearch_multi(master_isotope_hash_table, item, ENTER); + if (found_item == NULL) + { + error_string = sformatf( "Hash table error in master_isotope_store."); + error_msg(error_string, CONTINUE); + } + + return (master_isotope_ptr); +} + +/* ---------------------------------------------------------------------- */ +struct master_isotope * Phreeqc:: +master_isotope_alloc(void) +/* ---------------------------------------------------------------------- */ +/* + * Allocates space to a master_isotope structure, initializes + * arguments: void + * return: pointer to a master_isotope structure + */ +{ + struct master_isotope *master_isotope_ptr; + master_isotope_ptr = + (struct master_isotope *) PHRQ_malloc(sizeof(struct master_isotope)); + if (master_isotope_ptr == NULL) + malloc_error(); +/* + * set pointers in structure to NULL, variables to zero + */ + master_isotope_init(master_isotope_ptr); + + return (master_isotope_ptr); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +master_isotope_init(struct master_isotope *master_isotope_ptr) +/* ---------------------------------------------------------------------- */ +/* + * return: pointer to a master_isotope structure + */ +{ +/* + * set pointers in structure to NULL + */ + if (master_isotope_ptr) + { + master_isotope_ptr->name = NULL; + master_isotope_ptr->master = NULL; + master_isotope_ptr->elt = NULL; + master_isotope_ptr->units = NULL; + master_isotope_ptr->standard = 0; + master_isotope_ptr->ratio = 0; + master_isotope_ptr->moles = 0; + master_isotope_ptr->total_is_major = 0; + master_isotope_ptr->minor_isotope = 1; + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +struct master_isotope * Phreeqc:: +master_isotope_search(const char *name) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the hash table for master_isotope. + * + * Arguments: + * name input, character string to be found in "master_isotope". + * + * Returns: + * pointer to master_isotope structure "master_isotope" where "name" can be found. + * or NULL if not found. + */ + struct master_isotope *master_isotope_ptr; + ENTRY item, *found_item; + char token[MAX_LENGTH]; +/* + * Search list + */ + strcpy(token, name); + + item.key = token; + item.data = NULL; + found_item = hsearch_multi(master_isotope_hash_table, item, FIND); + + if (found_item != NULL) + { + master_isotope_ptr = (struct master_isotope *) (found_item->data); + return (master_isotope_ptr); + } + return (NULL); +} + +/* + * Utility routines for calculate_value + */ + +/* ---------------------------------------------------------------------- */ +struct calculate_value * Phreeqc:: +calculate_value_store(const char *name, int replace_if_found) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the hash table for calculate_value. + * + * Pointer to a calculate_value structure is always returned. + * + * If the string is not found, a new entry is made in the hash table. Pointer to + * the new structure is returned. + * If "name" is found and replace is true, pointers in old calculate_value structure + * are freed and replaced with additional input. + * If "name" is found and replace is false, the old calculate_value structure is not + * modified and a pointer to it is returned. + * + * Arguments: + * name input, character string to be found in "calculate_value". + * replace_if_found input, TRUE means reinitialize calculate_value structure if found + * FALSE means just return pointer if found. + * + * Returns: + * pointer to calculate_value structure "calculate_value" where "name" can be found. + */ + int n; + struct calculate_value *calculate_value_ptr; + char token[MAX_LENGTH]; + ENTRY item, *found_item; +/* + * Search list + */ + strcpy(token, name); + str_tolower(token); + item.key = token; + item.data = NULL; + found_item = hsearch_multi(calculate_value_hash_table, item, FIND); + + if (found_item != NULL && replace_if_found == FALSE) + { + calculate_value_ptr = (struct calculate_value *) (found_item->data); + return (calculate_value_ptr); + } + else if (found_item != NULL && replace_if_found == TRUE) + { + calculate_value_ptr = (struct calculate_value *) (found_item->data); + calculate_value_free(calculate_value_ptr); + calculate_value_init(calculate_value_ptr); + } + else + { + n = count_calculate_value++; + /* make sure there is space in s */ + if (count_calculate_value >= max_calculate_value) + { + space((void **) ((void *) &calculate_value), + count_calculate_value, &max_calculate_value, + sizeof(struct calculate_value *)); + } + /* Make new calculate_value structure */ + calculate_value[n] = calculate_value_alloc(); + calculate_value_ptr = calculate_value[n]; + } + /* set name and z in pointer in calculate_value structure */ + calculate_value_ptr->name = string_hsave(name); +/* + * Update hash table + */ + item.key = string_hsave(token); + item.data = (void *) calculate_value_ptr; + found_item = hsearch_multi(calculate_value_hash_table, item, ENTER); + if (found_item == NULL) + { + error_string = sformatf( "Hash table error in calculate_value_store."); + error_msg(error_string, CONTINUE); + } + + return (calculate_value_ptr); +} + +/* ---------------------------------------------------------------------- */ +struct calculate_value * Phreeqc:: +calculate_value_alloc(void) +/* ---------------------------------------------------------------------- */ +/* + * Allocates space to a calculate_value structure, initializes + * arguments: void + * return: pointer to a calculate_value structure + */ +{ + struct calculate_value *calculate_value_ptr; + calculate_value_ptr = + (struct calculate_value *) + PHRQ_malloc(sizeof(struct calculate_value)); + if (calculate_value_ptr == NULL) + malloc_error(); +/* + * set pointers in structure to NULL, variables to zero + */ + calculate_value_init(calculate_value_ptr); + + return (calculate_value_ptr); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +calculate_value_init(struct calculate_value *calculate_value_ptr) +/* ---------------------------------------------------------------------- */ +/* + * return: pointer to a calculate_value structure + */ +{ +/* + * set pointers in structure to NULL + */ + if (calculate_value_ptr) + { + calculate_value_ptr->name = NULL; + calculate_value_ptr->commands = NULL; + calculate_value_ptr->linebase = NULL; + calculate_value_ptr->varbase = NULL; + calculate_value_ptr->loopbase = NULL; + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +struct calculate_value * Phreeqc:: +calculate_value_search(const char *name) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the hash table for calculate_value. + * + * Arguments: + * name input, character string to be found in "calculate_value". + * + * Returns: + * pointer to calculate_value structure "calculate_value" where "name" can be found. + * or NULL if not found. + */ + struct calculate_value *calculate_value_ptr; + char token[MAX_LENGTH]; + ENTRY item, *found_item; +/* + * Search list + */ + strcpy(token, name); + str_tolower(token); + item.key = token; + item.data = NULL; + found_item = hsearch_multi(calculate_value_hash_table, item, FIND); + + if (found_item != NULL) + { + calculate_value_ptr = (struct calculate_value *) (found_item->data); + return (calculate_value_ptr); + } + return (NULL); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calculate_value_free(struct calculate_value *calculate_value_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Frees memory allocated within calculate_value[i], does not free calculate_value structure + * Input: i, number of calculate_value + * Return: OK + */ + char cmd[] = "new; quit"; + + if (calculate_value_ptr == NULL) + return (ERROR); + calculate_value_ptr->commands = + (char *) free_check_null(calculate_value_ptr->commands); + basic_run(cmd, calculate_value_ptr->linebase, + calculate_value_ptr->varbase, calculate_value_ptr->loopbase); + calculate_value_ptr->linebase = NULL; + calculate_value_ptr->varbase = NULL; + calculate_value_ptr->loopbase = NULL; + return (OK); +} + +/* + * Utility routines for isotope_ratio + */ + +/* ---------------------------------------------------------------------- */ +struct isotope_ratio * Phreeqc:: +isotope_ratio_store(const char *name, int replace_if_found) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the hash table for isotope_ratio. + * + * Pointer to a isotope_ratio structure is always returned. + * + * If the string is not found, a new entry is made in the hash table. Pointer to + * the new structure is returned. + * If "name" is found and replace is true, pointers in old isotope_ratio structure + * are freed and replaced with additional input. + * If "name" is found and replace is false, the old isotope_ratio structure is not + * modified and a pointer to it is returned. + * + * Arguments: + * name input, character string to be found in "isotope_ratio". + * replace_if_found input, TRUE means reinitialize isotope_ratio structure if found + * FALSE means just return pointer if found. + * + * Returns: + * pointer to isotope_ratio structure "isotope_ratio" where "name" can be found. + */ + int n; + struct isotope_ratio *isotope_ratio_ptr; + char token[MAX_LENGTH]; + ENTRY item, *found_item; +/* + * Search list + */ + strcpy(token, name); + str_tolower(token); + item.key = token; + item.data = NULL; + found_item = hsearch_multi(isotope_ratio_hash_table, item, FIND); + + if (found_item != NULL && replace_if_found == FALSE) + { + isotope_ratio_ptr = (struct isotope_ratio *) (found_item->data); + return (isotope_ratio_ptr); + } + else if (found_item != NULL && replace_if_found == TRUE) + { + isotope_ratio_ptr = (struct isotope_ratio *) (found_item->data); + isotope_ratio_init(isotope_ratio_ptr); + } + else + { + n = count_isotope_ratio++; + /* make sure there is space in s */ + if (count_isotope_ratio >= max_isotope_ratio) + { + space((void **) ((void *) &isotope_ratio), count_isotope_ratio, + &max_isotope_ratio, sizeof(struct isotope_ratio *)); + } + /* Make new isotope_ratio structure */ + isotope_ratio[n] = isotope_ratio_alloc(); + isotope_ratio_ptr = isotope_ratio[n]; + } + /* set name and z in pointer in isotope_ratio structure */ + isotope_ratio_ptr->name = string_hsave(name); +/* + * Update hash table + */ + item.key = string_hsave(token); + item.data = (void *) isotope_ratio_ptr; + found_item = hsearch_multi(isotope_ratio_hash_table, item, ENTER); + if (found_item == NULL) + { + error_string = sformatf( "Hash table error in isotope_ratio_store."); + error_msg(error_string, CONTINUE); + } + + return (isotope_ratio_ptr); +} + +/* ---------------------------------------------------------------------- */ +struct isotope_ratio * Phreeqc:: +isotope_ratio_alloc(void) +/* ---------------------------------------------------------------------- */ +/* + * Allocates space to a isotope_ratio structure, initializes + * arguments: void + * return: pointer to a isotope_ratio structure + */ +{ + struct isotope_ratio *isotope_ratio_ptr; + isotope_ratio_ptr = + (struct isotope_ratio *) PHRQ_malloc(sizeof(struct isotope_ratio)); + if (isotope_ratio_ptr == NULL) + malloc_error(); +/* + * set pointers in structure to NULL, variables to zero + */ + isotope_ratio_init(isotope_ratio_ptr); + + return (isotope_ratio_ptr); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +isotope_ratio_init(struct isotope_ratio *isotope_ratio_ptr) +/* ---------------------------------------------------------------------- */ +/* + * return: pointer to a isotope_ratio structure + */ +{ +/* + * set pointers in structure to NULL + */ + if (isotope_ratio_ptr) + { + isotope_ratio_ptr->name = NULL; + isotope_ratio_ptr->isotope_name = NULL; + isotope_ratio_ptr->ratio = MISSING; + isotope_ratio_ptr->converted_ratio = MISSING; + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +struct isotope_ratio * Phreeqc:: +isotope_ratio_search(const char *name) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the hash table for isotope_ratio. + * + * Arguments: + * name input, character string to be found in "isotope_ratio". + * + * Returns: + * pointer to isotope_ratio structure "isotope_ratio" where "name" can be found. + * or NULL if not found. + */ + struct isotope_ratio *isotope_ratio_ptr; + char token[MAX_LENGTH]; + ENTRY item, *found_item; +/* + * Search list + */ + strcpy(token, name); + str_tolower(token); + item.key = token; + item.data = NULL; + found_item = hsearch_multi(isotope_ratio_hash_table, item, FIND); + + if (found_item != NULL) + { + isotope_ratio_ptr = (struct isotope_ratio *) (found_item->data); + return (isotope_ratio_ptr); + } + return (NULL); +} + +/* + * Utility routines for isotope_alpha + */ + +/* ---------------------------------------------------------------------- */ +struct isotope_alpha * Phreeqc:: +isotope_alpha_store(const char *name, int replace_if_found) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the hash table for isotope_alpha. + * + * Pointer to a isotope_alpha structure is always returned. + * + * If the string is not found, a new entry is made in the hash table. Pointer to + * the new structure is returned. + * If "name" is found and replace is true, pointers in old isotope_alpha structure + * are freed and replaced with additional input. + * If "name" is found and replace is false, the old isotope_alpha structure is not + * modified and a pointer to it is returned. + * + * Arguments: + * name input, character string to be found in "isotope_alpha". + * replace_if_found input, TRUE means reinitialize isotope_alpha structure if found + * FALSE means just return pointer if found. + * + * Returns: + * pointer to isotope_alpha structure "isotope_alpha" where "name" can be found. + */ + int n; + struct isotope_alpha *isotope_alpha_ptr; + char token[MAX_LENGTH]; + ENTRY item, *found_item; +/* + * Search list + */ + strcpy(token, name); + str_tolower(token); + item.key = token; + item.data = NULL; + found_item = hsearch_multi(isotope_alpha_hash_table, item, FIND); + + if (found_item != NULL && replace_if_found == FALSE) + { + isotope_alpha_ptr = (struct isotope_alpha *) (found_item->data); + return (isotope_alpha_ptr); + } + else if (found_item != NULL && replace_if_found == TRUE) + { + isotope_alpha_ptr = (struct isotope_alpha *) (found_item->data); + isotope_alpha_init(isotope_alpha_ptr); + } + else + { + n = count_isotope_alpha++; + /* make sure there is space in s */ + if (count_isotope_alpha >= max_isotope_alpha) + { + space((void **) ((void *) &isotope_alpha), count_isotope_alpha, + &max_isotope_alpha, sizeof(struct isotope_alpha *)); + } + /* Make new isotope_alpha structure */ + isotope_alpha[n] = isotope_alpha_alloc(); + isotope_alpha_ptr = isotope_alpha[n]; + } + /* set name and z in pointer in isotope_alpha structure */ + isotope_alpha_ptr->name = string_hsave(name); +/* + * Update hash table + */ + item.key = string_hsave(token); + item.data = (void *) isotope_alpha_ptr; + found_item = hsearch_multi(isotope_alpha_hash_table, item, ENTER); + if (found_item == NULL) + { + error_string = sformatf( "Hash table error in isotope_alpha_store."); + error_msg(error_string, CONTINUE); + } + + return (isotope_alpha_ptr); +} + +/* ---------------------------------------------------------------------- */ +struct isotope_alpha * Phreeqc:: +isotope_alpha_alloc(void) +/* ---------------------------------------------------------------------- */ +/* + * Allocates space to a isotope_alpha structure, initializes + * arguments: void + * return: pointer to a isotope_alpha structure + */ +{ + struct isotope_alpha *isotope_alpha_ptr; + isotope_alpha_ptr = + (struct isotope_alpha *) PHRQ_malloc(sizeof(struct isotope_alpha)); + if (isotope_alpha_ptr == NULL) + malloc_error(); +/* + * set pointers in structure to NULL, variables to zero + */ + isotope_alpha_init(isotope_alpha_ptr); + + return (isotope_alpha_ptr); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +isotope_alpha_init(struct isotope_alpha *isotope_alpha_ptr) +/* ---------------------------------------------------------------------- */ +/* + * return: pointer to a isotope_alpha structure + */ +{ +/* + * set pointers in structure to NULL + */ + if (isotope_alpha_ptr) + { + isotope_alpha_ptr->name = NULL; + isotope_alpha_ptr->named_logk = NULL; + isotope_alpha_ptr->value = MISSING; + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +struct isotope_alpha * Phreeqc:: +isotope_alpha_search(const char *name) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the hash table for isotope_alpha. + * + * Arguments: + * name input, character string to be found in "isotope_alpha". + * + * Returns: + * pointer to isotope_alpha structure "isotope_alpha" where "name" can be found. + * or NULL if not found. + */ + struct isotope_alpha *isotope_alpha_ptr; + char token[MAX_LENGTH]; + ENTRY item, *found_item; +/* + * Search list + */ + strcpy(token, name); + str_tolower(token); + item.key = token; + item.data = NULL; + found_item = hsearch_multi(isotope_alpha_hash_table, item, FIND); + + if (found_item != NULL) + { + isotope_alpha_ptr = (struct isotope_alpha *) (found_item->data); + return (isotope_alpha_ptr); + } + return (NULL); +} diff --git a/phreeqcpp/kinetics.cpp b/phreeqcpp/kinetics.cpp new file mode 100644 index 00000000..9b50c43e --- /dev/null +++ b/phreeqcpp/kinetics.cpp @@ -0,0 +1,3613 @@ +#include "Utils.h" +#include "Phreeqc.h" +#include "phqalloc.h" + +#include + +#include "StorageBin.h" +#include "Reaction.h" +#include "cxxKinetics.h" +#include "Solution.h" +#include "cxxMix.h" +#include "PPassemblage.h" +#include "Surface.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "SSassemblage.h" +#include "Temperature.h" +#include "cxxKinetics.h" +#include +#include +#include +#include "nvector_serial.h" /* definitions of type N_Vector and macro */ + /* NV_Ith_S, prototypes for N_VNew, N_VFree */ +/* These macros are defined in order to write code which exactly matches + the mathematical problem description given above. + + Ith(v,i) references the ith component of the vector v, where i is in + the range [1..NEQ] and NEQ is defined below. The Ith macro is defined + using the N_VIth macro in nvector.h. N_VIth numbers the components of + a vector starting from 0. + + IJth(A,i,j) references the (i,j)th element of the dense matrix A, where + i and j are in the range [1..NEQ]. The IJth macro is defined using the + DENSE_ELEM macro in dense.h. DENSE_ELEM numbers rows and columns of a + dense matrix starting from 0. */ + +#define Ith(v,i) NV_Ith_S(v,i-1) /* Ith numbers components 1..NEQ */ +#define IJth(A,i,j) DENSE_ELEM(A,i-1,j-1) /* IJth numbers rows,cols 1..NEQ */ + +#define MAX_DIVIDE 2 +#define KINETICS_TOL 1e-8; +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_kinetic_reaction(cxxKinetics *kinetics_ptr, LDBLE time_step) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through kinetic components to + * determine rates and + * a list of elements and amounts in + * the reaction. + */ + int j, return_value; + LDBLE coef; + char l_command[] = "run"; + struct rate *rate_ptr; +/* + * Go through list and generate list of elements and + * coefficient of elements in reaction + */ + return_value = OK; + count_elts = 0; + paren_count = 0; + rate_time = time_step; + +/* t1 = clock(); */ + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + coef = 0.0; +/* + * Send command to basic interpreter + */ + rate_ptr = rate_search(kinetics_comp_ptr->Get_rate_name().c_str(), &j); + if (rate_ptr == NULL) + { + error_string = sformatf( "Rate not found for %s", + kinetics_comp_ptr->Get_rate_name().c_str()); + error_msg(error_string, STOP); + } + else + { + rate_moles = NAN; + rate_m = kinetics_comp_ptr->Get_m(); + rate_m0 = kinetics_comp_ptr->Get_m0(); + rate_p = kinetics_comp_ptr->Get_d_params(); + count_rate_p = (int) kinetics_comp_ptr->Get_d_params().size(); + if (rate_ptr->new_def == TRUE) + { + if (basic_compile + (rates[j].commands, &rates[j].linebase, &rates[j].varbase, + &rates[j].loopbase) != 0) + { + error_string = sformatf( "Fatal Basic error in rate %s.", + kinetics_comp_ptr->Get_rate_name().c_str()); + error_msg(error_string, STOP); + } + + rate_ptr->new_def = FALSE; + } + if (basic_run + (l_command, rates[j].linebase, rates[j].varbase, + rates[j].loopbase) != 0) + { + error_string = sformatf( "Fatal Basic error in rate %s.", + kinetics_comp_ptr->Get_rate_name().c_str()); + error_msg(error_string, STOP); + } +#ifdef NPP + if (isnan(rate_moles)) +#else + if (rate_moles == NAN) +#endif + { + error_string = sformatf( "Moles of reaction not SAVEed for %s.", + kinetics_comp_ptr->Get_rate_name().c_str()); + error_msg(error_string, STOP); + } + else + { + + coef = rate_moles; + } + } +/* + * Accumulate moles of reaction for component + */ + kinetics_comp_ptr->Set_moles(kinetics_comp_ptr->Get_moles() + coef); + if (coef == 0.0) + continue; + } +/* t2=clock(); + printf("secs in reac %e, t2 %e\n", t2-t1, t1); + */ + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_final_kinetic_reaction(cxxKinetics *kinetics_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through kinetic components to + * using extrapolated values, which were + * stored in moles in run_kinetics + */ + LDBLE coef; + struct phase *phase_ptr; + struct master *master_ptr; + int count= 0; +/* + * Go through list and generate list of elements and + * coefficient of elements in reaction + */ +RESTART: // if limiting rates, jump to here + count++; + kinetics_ptr->Get_totals().clear(); + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + count_elts = 0; + paren_count = 0; + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + if (kinetics_comp_ptr->Get_moles() > m_temp[i]) + { + kinetics_comp_ptr->Set_moles(m_temp[i]); + kinetics_comp_ptr->Set_m(0); + } + coef = kinetics_comp_ptr->Get_moles(); + if (coef == 0.0) + continue; +/* + * Reactant is a pure phase, copy formula into token + */ + cxxNameDouble::iterator it = kinetics_comp_ptr->Get_namecoef().begin(); + for ( ; it != kinetics_comp_ptr->Get_namecoef().end(); it++) + { + std::string name = it->first; + LDBLE coef1 = it->second; + phase_ptr = NULL; + int k; + phase_ptr = phase_bsearch(name.c_str(), &k, FALSE); + if (phase_ptr != NULL) + { + add_elt_list(phase_ptr->next_elt, + coef *coef1); + } + else + { + char * temp_name = string_duplicate(name.c_str()); + char * ptr = temp_name; + get_elts_in_species(&ptr, coef * coef1); + free_check_null(temp_name); + } + } + if (use.Get_exchange_ptr() != NULL + && use.Get_exchange_ptr()->Get_related_rate()) + { + cxxExchange * exchange_ptr = use.Get_exchange_ptr(); + for(size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++) + { + std::string name(exchange_ptr->Get_exchange_comps()[j].Get_rate_name()); + if (name.size() > 0) + { + if (strcmp_nocase + (kinetics_comp_ptr->Get_rate_name().c_str(), + name.c_str()) == 0) + { + /* found kinetics component */ + char * formula = string_duplicate(exchange_ptr->Get_exchange_comps()[j].Get_formula().c_str()); + char * ptr = formula; + get_elts_in_species(&ptr, -coef*exchange_ptr->Get_exchange_comps()[j].Get_phase_proportion()); + free_check_null(formula); + } + } + } + + } + if (use.Get_surface_ptr() != NULL && use.Get_surface_ptr()->Get_related_rate()) + { + for (size_t j = 0; j < use.Get_surface_ptr()->Get_surface_comps().size(); j++) + { + cxxSurfaceComp *surface_comp_ptr = &(use.Get_surface_ptr()->Get_surface_comps()[j]); + if (surface_comp_ptr->Get_rate_name().size() > 0) + { + if (strcmp_nocase + (kinetics_comp_ptr->Get_rate_name().c_str(), + surface_comp_ptr->Get_rate_name().c_str()) == 0) + { + /* found kinetics component */ + char * temp_formula = string_duplicate(surface_comp_ptr->Get_formula().c_str()); + char *ptr = temp_formula; + /* Surface = 0 when m becomes low ... + */ + if (0.9 * surface_comp_ptr->Get_phase_proportion() * + (kinetics_comp_ptr->Get_m()) < MIN_RELATED_SURFACE) + { + //master_ptr = master_bsearch(ptr); + master_ptr = master_bsearch(surface_comp_ptr->Get_master_element().c_str()); + if (master_ptr != NULL) + { + master_ptr->total = 0.0; + } + } + else + { + get_elts_in_species(&ptr, -coef * surface_comp_ptr->Get_phase_proportion()); + } + free_check_null(temp_formula); + } + } + } + } + kinetics_comp_ptr->Set_moles_of_reaction(elt_list_NameDouble()); + kinetics_ptr->Get_totals().add_extensive(kinetics_comp_ptr->Get_moles_of_reaction(), 1.0); + } + if (count > 2) + { +#if !defined(R_SO) + fprintf(stderr, "Too many limit_rates-.\n"); +#else + error_msg("Too many limit_rates-.\n"); +#endif + } + else + { + if (limit_rates(kinetics_ptr)) + goto RESTART; + } + if (count > 2) + { +#if !defined(R_SO) + fprintf(stderr, "Too many limit_rates+.\n"); +#else + error_msg("Too many limit_rates+.\n"); +#endif + } + return (OK); +} +#ifdef SKIP +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_final_kinetic_reaction(cxxKinetics *kinetics_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through kinetic components to + * using extrapolated values, which were + * stored in moles in run_kinetics + */ + LDBLE coef; + struct phase *phase_ptr; + struct master *master_ptr; +/* + * Go through list and generate list of elements and + * coefficient of elements in reaction + */ + kinetics_ptr->Get_totals().clear(); + count_elts = 0; + paren_count = 0; + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + if (kinetics_comp_ptr->Get_moles() > m_temp[i]) + { + kinetics_comp_ptr->Set_moles(m_temp[i]); + kinetics_comp_ptr->Set_m(0); + } + coef = kinetics_comp_ptr->Get_moles(); + if (coef == 0.0) + continue; +/* + * Reactant is a pure phase, copy formula into token + */ + cxxNameDouble::iterator it = kinetics_comp_ptr->Get_namecoef().begin(); + for ( ; it != kinetics_comp_ptr->Get_namecoef().end(); it++) + { + std::string name = it->first; + LDBLE coef1 = it->second; + phase_ptr = NULL; + int k; + phase_ptr = phase_bsearch(name.c_str(), &k, FALSE); + if (phase_ptr != NULL) + { + add_elt_list(phase_ptr->next_elt, + coef *coef1); + } + else + { + char * temp_name = string_duplicate(name.c_str()); + char * ptr = temp_name; + get_elts_in_species(&ptr, coef * coef1); + free_check_null(temp_name); + } + } + if (use.Get_exchange_ptr() != NULL + && use.Get_exchange_ptr()->Get_related_rate()) + { + cxxExchange * exchange_ptr = use.Get_exchange_ptr(); + for(size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++) + { + std::string name(exchange_ptr->Get_exchange_comps()[j].Get_rate_name()); + if (name.size() > 0) + { + if (strcmp_nocase + (kinetics_comp_ptr->Get_rate_name().c_str(), + name.c_str()) == 0) + { + /* found kinetics component */ + char * formula = string_duplicate(exchange_ptr->Get_exchange_comps()[j].Get_formula().c_str()); + char * ptr = formula; + get_elts_in_species(&ptr, -coef*exchange_ptr->Get_exchange_comps()[j].Get_phase_proportion()); + free_check_null(formula); + } + } + } + + } + if (use.Get_surface_ptr() != NULL && use.Get_surface_ptr()->Get_related_rate()) + { + for (size_t j = 0; j < use.Get_surface_ptr()->Get_surface_comps().size(); j++) + { + cxxSurfaceComp *surface_comp_ptr = &(use.Get_surface_ptr()->Get_surface_comps()[j]); + if (surface_comp_ptr->Get_rate_name().size() > 0) + { + if (strcmp_nocase + (kinetics_comp_ptr->Get_rate_name().c_str(), + surface_comp_ptr->Get_rate_name().c_str()) == 0) + { + /* found kinetics component */ + char * temp_formula = string_duplicate(surface_comp_ptr->Get_formula().c_str()); + char *ptr = temp_formula; +/* Surface = 0 when m becomes low ... + */ + if (0.9 * surface_comp_ptr->Get_phase_proportion() * + (kinetics_comp_ptr->Get_m()) < MIN_RELATED_SURFACE) + { + master_ptr = master_bsearch(ptr); + master_ptr->total = 0.0; + } + else + { + get_elts_in_species(&ptr, -coef * surface_comp_ptr->Get_phase_proportion()); + } + free_check_null(temp_formula); + } + } + } + } + } + kinetics_ptr->Set_totals(elt_list_NameDouble()); + return (OK); +} +#endif +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +rk_kinetics(int i, LDBLE kin_time, int use_mix, int nsaver, + LDBLE step_fraction) +/* ---------------------------------------------------------------------- */ +{ +/* + * Runge-Kutta-Fehlberg method; 6 evaluations of the derivative + * give O(h^5) global error and error estimate + * calc_kinetic_reaction(.., ..) calculates moles of intermediate derivatives; + * these are calc'd for the whole step h. + * calc_final_kinetic reaction(..) translates moles to PHREEQC reaction. + */ + int k, save_old; + int l_bad, step_bad, step_ok; + int n_reactions; + LDBLE h, h_old, h_sum; + LDBLE l_error, error_max, safety, moles_max, moles_reduction; + cxxKinetics *kinetics_ptr; + int equal_rate, zero_rate; + + cxxPPassemblage *pp_assemblage_save = NULL; + cxxSSassemblage *ss_assemblage_save = NULL; + + LDBLE b31 = 3. / 40., b32 = 9. / 40., + b51 = -11. / 54., b53 = -70. / 27., b54 = 35. / 27., + b61 = 1631. / 55296., b62 = 175. / 512., b63 = 575. / 13824., b64 = + 44275. / 110592., b65 = 253. / 4096., c1 = 37. / 378., c3 = + 250. / 621., c4 = 125. / 594., c6 = 512. / 1771., dc5 = + -277. / 14336.; + LDBLE dc1 = c1 - 2825. / 27648., dc3 = c3 - 18575. / 48384., dc4 = + c4 - 13525. / 55296., dc6 = c6 - 0.25; +/* + * Save kinetics i and solution i, if necessary + */ + save_old = -2 - (count_cells * (1 + stag_data->count_stag) + 2); + Utilities::Rxn_copy(Rxn_kinetics_map, i, save_old); + if (nsaver != i) + { + Utilities::Rxn_copy(Rxn_solution_map, i, save_old); + } + +/* + * Malloc some space + */ + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i); + if (kinetics_ptr == NULL) + return (OK); + n_reactions = (int) kinetics_ptr->Get_kinetics_comps().size(); + rk_moles = (LDBLE *) free_check_null(rk_moles); + rk_moles = (LDBLE *) PHRQ_malloc((size_t) 6 * n_reactions * sizeof(LDBLE)); + if (rk_moles == NULL) malloc_error(); + + /*if (use_mix != NOMIX) last_model.force_prep = TRUE; */ + set_and_run_wrapper(i, use_mix, FALSE, i, step_fraction); + run_reactions_iterations += iterations; + + saver(); + if (state == TRANSPORT || state == PHAST) + { + set_transport(i, NOMIX, TRUE, i); + } + else if (state == ADVECTION) + { + set_advection(i, NOMIX, TRUE, i); + } + else if (state == REACTION) + { + set_reaction(i, NOMIX, TRUE); + } + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i); + + step_bad = step_ok = 0; + l_bad = FALSE; + h_sum = 0.; + h = h_old = kin_time; + moles_max = 0.1; + moles_reduction = 1.0; + safety = 0.7; + if (kinetics_ptr->Get_rk() < 1) + kinetics_ptr->Set_rk(1); + else if (kinetics_ptr->Get_rk() > 3) + kinetics_ptr->Set_rk(6); + + if (kinetics_ptr->Get_rk() == 6) + equal_rate = FALSE; + else + equal_rate = TRUE; +/* + * if step_divide > 1, initial timestep is divided + * if < 1, step_divide indicates maximal reaction... + */ + if (kinetics_ptr->Get_step_divide() > 1.0) + { + h = h_old = kin_time / kinetics_ptr->Get_step_divide(); + equal_rate = FALSE; + } + else if (kinetics_ptr->Get_step_divide() < 1.0) + moles_max = kinetics_ptr->Get_step_divide(); + + rate_sim_time = rate_sim_time_start + h_sum; + + status(0, NULL); + while (h_sum < kin_time) + { + + if (step_bad > kinetics_ptr->Get_bad_step_max()) + { + error_string = sformatf( + "Bad RK steps > %d. Please decrease (time)step or increase -bad_step_max.", + kinetics_ptr->Get_bad_step_max()); + error_msg(error_string, STOP); + } + + MOLES_TOO_LARGE: + if (moles_reduction > 1.0) + { + h_old = h; + h = safety * h / (1.0 + moles_reduction); + moles_reduction = 1.0; + equal_rate = FALSE; + l_bad = TRUE; + } +/* + * find k1 + */ + if (l_bad == TRUE) + { + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + rk_moles[j] *= (h / h_old); + kinetics_comp_ptr->Set_moles(rk_moles[j] * 0.2); + kinetics_comp_ptr->Set_m(m_temp[j]); + } + l_bad = FALSE; + } + else + { +/* + * define pointers for calc_kinetic_, they are lost after saver()... + */ + if (state == TRANSPORT || state == PHAST) + { + set_transport(i, NOMIX, TRUE, i); + } + else if (state == ADVECTION) + { + set_advection(i, NOMIX, TRUE, i); + } + else if (state == REACTION) + { + set_reaction(i, NOMIX, TRUE); + } + /* + * Moles of minerals and solid solutions may change to make positive + * concentrations. Reactions may take out more than is present in + * solution. + */ + if (use.Get_pp_assemblage_ptr() != NULL) + { + cxxPPassemblage * pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, use.Get_pp_assemblage_ptr()->Get_n_user()); + assert(pp_assemblage_ptr); + pp_assemblage_save = new cxxPPassemblage(*pp_assemblage_ptr); + } + if (use.Get_ss_assemblage_ptr() != NULL) + { + cxxSSassemblage * ss_assemblage_ptr = Utilities::Rxn_find(Rxn_ss_assemblage_map, use.Get_ss_assemblage_ptr()->Get_n_user()); + assert(ss_assemblage_ptr); + ss_assemblage_save = new cxxSSassemblage(*ss_assemblage_ptr); + } + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(0.); + m_temp[j] = kinetics_comp_ptr->Get_m(); + } + + rate_sim_time = rate_sim_time_start + h_sum; + calc_kinetic_reaction(kinetics_ptr, h); + + /* store k1 in rk_moles ... */ + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + if (moles_reduction * moles_max < fabs(kinetics_comp_ptr->Get_moles())) + { + moles_reduction = fabs(kinetics_comp_ptr->Get_moles()) / moles_max; + } + /* define reaction for calculating k2 ... */ + rk_moles[j] = kinetics_comp_ptr->Get_moles(); + kinetics_comp_ptr->Set_moles(kinetics_comp_ptr->Get_moles() * 0.2); + } + if (moles_reduction > 1.0) + goto MOLES_TOO_LARGE; + } +/* + * Quit rk with rk = 1 and equal rates ... + */ + if (kinetics_ptr->Get_rk() == 1 && equal_rate) + { + zero_rate = TRUE; + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(rk_moles[j]); + if (fabs(kinetics_comp_ptr->Get_moles()) > MIN_TOTAL) + zero_rate = FALSE; + } + + if (zero_rate == FALSE) + { + calc_final_kinetic_reaction(kinetics_ptr); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles()); + if (kinetics_comp_ptr->Get_m() < 1.e-30) + kinetics_comp_ptr->Set_m(0); + kinetics_comp_ptr->Set_moles(0.); + } + if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == + MASS_BALANCE) + { + run_reactions_iterations += iterations; + moles_reduction = 9; + goto MOLES_TOO_LARGE; + } + run_reactions_iterations += iterations; + calc_kinetic_reaction(kinetics_ptr, h); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + if (fabs(rk_moles[j] - kinetics_comp_ptr->Get_moles()) > + kinetics_comp_ptr->Get_tol()) + { + equal_rate = FALSE; + break; + } + } + } + if (zero_rate || equal_rate) + { + /* removing the following line causes different results for + example 6 distributed with the program */ + saver(); + + /* Free space */ + + if (pp_assemblage_save != NULL) + { + delete pp_assemblage_save; + pp_assemblage_save = NULL; + } + if (ss_assemblage_save != NULL) + { + delete ss_assemblage_save; + ss_assemblage_save = NULL; + } + goto EQUAL_RATE_OUT; + } + else + { + kinetics_ptr->Set_rk(3); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(0.2 * rk_moles[j]); + } + } + } +/* + * Continue with rk ... + */ + calc_final_kinetic_reaction(kinetics_ptr); + if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE) + { + run_reactions_iterations += iterations; + moles_reduction = 9; + goto MOLES_TOO_LARGE; + } + run_reactions_iterations += iterations; + +/* + * find k2 + */ + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles()); + kinetics_comp_ptr->Set_moles(0.); + } + rate_sim_time = rate_sim_time_start + h_sum + 0.2 * h; + calc_kinetic_reaction(kinetics_ptr, h); + + /* Reset to values of last saver() */ + if (pp_assemblage_save != NULL) + { + Rxn_pp_assemblage_map[pp_assemblage_save->Get_n_user()] = *pp_assemblage_save; + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, pp_assemblage_save->Get_n_user())); + } + if (ss_assemblage_save != NULL) + { + Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save; + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user())); + } + + /* store k2 in rk_moles */ + k = n_reactions; + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + if (moles_reduction * moles_max < + fabs(kinetics_comp_ptr->Get_moles())) + { + moles_reduction = + fabs(kinetics_comp_ptr->Get_moles()) / moles_max; + } + /* define reaction for calculating k3 */ + rk_moles[k + j] = kinetics_comp_ptr->Get_moles(); + + kinetics_comp_ptr->Set_moles(b31 * rk_moles[j] + + b32 * rk_moles[k + j]); +/* + * check for equal_rate ... + */ + if (equal_rate + && fabs(rk_moles[j] - rk_moles[k + j]) > + kinetics_comp_ptr->Get_tol()) + { + equal_rate = FALSE; + } + } + if (moles_reduction > 1.0) + goto MOLES_TOO_LARGE; +/* + * Quit rk with rk = 2 and equal rates ... + */ + if (kinetics_ptr->Get_rk() == 2 && equal_rate) + { + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles( + 0.3 * rk_moles[j] + 0.7 * rk_moles[k + j]); + } + calc_final_kinetic_reaction(kinetics_ptr); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles()); + if (kinetics_comp_ptr->Get_m() < 1.e-30) + kinetics_comp_ptr->Set_m(0); + kinetics_comp_ptr->Set_moles(0.); + } + if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE) + { + run_reactions_iterations += iterations; + moles_reduction = 9; + goto MOLES_TOO_LARGE; + } + run_reactions_iterations += iterations; +/* + * Move next calc'n to rk = 1 when initial rate equals final rate ... + */ + calc_kinetic_reaction(kinetics_ptr, h); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + if (fabs(rk_moles[j] - kinetics_comp_ptr->Get_moles()) > + kinetics_comp_ptr->Get_tol()) + { + equal_rate = FALSE; + break; + } + } + if (equal_rate) + kinetics_ptr->Set_rk(1); + + saver(); + + /* Free space */ + + if (pp_assemblage_save != NULL) + { + delete pp_assemblage_save; + pp_assemblage_save = NULL; + } + if (ss_assemblage_save != NULL) + { + Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save; + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user())); + } + goto EQUAL_RATE_OUT; + } +/* + * Continue runge_kutta.. + */ + calc_final_kinetic_reaction(kinetics_ptr); + if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE) + { + run_reactions_iterations += iterations; + moles_reduction = 9; + goto MOLES_TOO_LARGE; + } + run_reactions_iterations += iterations; +/* + * find k3 + */ + + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_m (m_temp[j] - kinetics_comp_ptr->Get_moles()); + kinetics_comp_ptr->Set_moles(0.); + } + rate_sim_time = rate_sim_time_start + h_sum + 0.3 * h; + calc_kinetic_reaction(kinetics_ptr, h); + + /* Reset to values of last saver() */ + if (pp_assemblage_save != NULL) + { + Rxn_pp_assemblage_map[pp_assemblage_save->Get_n_user()] = *pp_assemblage_save; + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, pp_assemblage_save->Get_n_user())); + } + if (ss_assemblage_save != NULL) + { + Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save; + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user())); + } + + /* store k3 in rk_moles */ + k = 2 * n_reactions; + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + if (moles_reduction * moles_max < + fabs(kinetics_comp_ptr->Get_moles())) + { + moles_reduction = + fabs(kinetics_comp_ptr->Get_moles()) / moles_max; + } + /* define reaction for calculating k4 ... */ + rk_moles[k + j] = kinetics_comp_ptr->Get_moles(); + + kinetics_comp_ptr->Set_moles(0.3 * rk_moles[j] + - 0.9 * rk_moles[n_reactions + j] + 1.2 * rk_moles[k + j]); +/* + * check for equal_rate ... + */ + if (equal_rate + && fabs(rk_moles[j] - rk_moles[k + j]) > + kinetics_comp_ptr->Get_tol()) + equal_rate = FALSE; + } + if (moles_reduction > 1.0) + goto MOLES_TOO_LARGE; +/* + * Quit rk with rk = 3 and equal rates ... + */ + if (kinetics_ptr->Get_rk() == 3 && equal_rate) + { + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(0.5 * rk_moles[j] + - 1.5 * rk_moles[n_reactions + j] + 2 * rk_moles[k + j]); + } + calc_final_kinetic_reaction(kinetics_ptr); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles()); + if (kinetics_comp_ptr->Get_m() < 1.e-30) + kinetics_comp_ptr->Set_m(0.); + kinetics_comp_ptr->Set_moles(0.); + } + + if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE) + { + run_reactions_iterations += iterations; + moles_reduction = 9; + goto MOLES_TOO_LARGE; + } + run_reactions_iterations += iterations; +/* + * Move next calc'n to rk = 1 when initial rate equals final rate ... + */ + calc_kinetic_reaction(kinetics_ptr, h); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + if (fabs(rk_moles[j] - kinetics_comp_ptr->Get_moles()) > + kinetics_comp_ptr->Get_tol()) + { + equal_rate = FALSE; + break; + } + } + if (equal_rate) + kinetics_ptr->Set_rk(1); + + saver(); + + /* Free space */ + + if (pp_assemblage_save != NULL) + { + delete pp_assemblage_save; + pp_assemblage_save = NULL; + } + if (ss_assemblage_save != NULL) + { + delete ss_assemblage_save; + ss_assemblage_save = NULL; + } + goto EQUAL_RATE_OUT; + } +/* + * Continue runge_kutta.. + */ + + calc_final_kinetic_reaction(kinetics_ptr); + if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE) + { + run_reactions_iterations += iterations; + moles_reduction = 9; + goto MOLES_TOO_LARGE; + } + run_reactions_iterations += iterations; +/* + * find k4 + */ + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles()); + kinetics_comp_ptr->Set_moles(0.); + } + rate_sim_time = rate_sim_time_start + h_sum + 0.6 * h; + calc_kinetic_reaction(kinetics_ptr, h); + + /* Reset to values of last saver() */ + if (pp_assemblage_save != NULL) + { + Rxn_pp_assemblage_map[pp_assemblage_save->Get_n_user()] = *pp_assemblage_save; + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, pp_assemblage_save->Get_n_user())); + } + if (ss_assemblage_save != NULL) + { + Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save; + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user())); + } + + /* store k4 in rk_moles */ + k = 3 * n_reactions; + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + if (moles_reduction * moles_max < + fabs(kinetics_comp_ptr->Get_moles())) + { + moles_reduction = + fabs(kinetics_comp_ptr->Get_moles()) / moles_max; + } + + /* define reaction for calculating k5 */ + rk_moles[k + j] = kinetics_comp_ptr->Get_moles(); + kinetics_comp_ptr->Set_moles(b51 * rk_moles[j] + + 2.5 * rk_moles[n_reactions + j] + + b53 * rk_moles[2 * n_reactions + j] + b54 * rk_moles[k + j]); + } + if (moles_reduction > 1.0) + goto MOLES_TOO_LARGE; + calc_final_kinetic_reaction(kinetics_ptr); + if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE) + { + run_reactions_iterations += iterations; + moles_reduction = 9; + goto MOLES_TOO_LARGE; + } + run_reactions_iterations += iterations; +/* + * find k5 + */ + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles()); + kinetics_comp_ptr->Set_moles(0.); + } + rate_sim_time = rate_sim_time_start + h_sum + h; + calc_kinetic_reaction(kinetics_ptr, h); + + /* Reset to values of last saver() */ + if (pp_assemblage_save != NULL) + { + Rxn_pp_assemblage_map[pp_assemblage_save->Get_n_user()] = *pp_assemblage_save; + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, pp_assemblage_save->Get_n_user())); + } + if (ss_assemblage_save != NULL) + { + Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save; + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user())); + } + + /* store k5 in rk_moles */ + k = 4 * n_reactions; + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + if (moles_reduction * moles_max < + fabs(kinetics_comp_ptr->Get_moles())) + { + moles_reduction = + fabs(kinetics_comp_ptr->Get_moles()) / moles_max; + } + + /* define reaction for calculating k6 */ + rk_moles[k + j] = kinetics_comp_ptr->Get_moles(); + kinetics_comp_ptr->Set_moles(b61 * rk_moles[j] + + b62 * rk_moles[n_reactions + j] + + b63 * rk_moles[2 * n_reactions + j] + + b64 * rk_moles[3 * n_reactions + j] + b65 * rk_moles[k + j]); + } + if (moles_reduction > 1.0) + goto MOLES_TOO_LARGE; + calc_final_kinetic_reaction(kinetics_ptr); + if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE) + { + run_reactions_iterations += iterations; + moles_reduction = 9; + goto MOLES_TOO_LARGE; + } + run_reactions_iterations += iterations; +/* + * find k6 + */ + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles()); + kinetics_comp_ptr->Set_moles(0.); + } + rate_sim_time = rate_sim_time_start + h_sum + 0.875 * h; + calc_kinetic_reaction(kinetics_ptr, h); + + /* Reset to values of last saver() */ + if (pp_assemblage_save != NULL) + { + Rxn_pp_assemblage_map[pp_assemblage_save->Get_n_user()] = *pp_assemblage_save; + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, pp_assemblage_save->Get_n_user())); + } + if (ss_assemblage_save != NULL) + { + Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save; + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user())); + } + + /* store k6 in rk_moles */ + k = 5 * n_reactions; + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + rk_moles[k + j] = kinetics_comp_ptr->Get_moles(); + } + +/* + * Evaluate error + */ + error_max = 0.; + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + l_error = fabs(dc1 * rk_moles[j] + + dc3 * rk_moles[2 * n_reactions + j] + + dc4 * rk_moles[3 * n_reactions + j] + + dc5 * rk_moles[4 * n_reactions + j] + + dc6 * rk_moles[5 * n_reactions + j]); + + /* tol is in moles/l */ + l_error /= kinetics_comp_ptr->Get_tol(); + if (l_error > error_max) + error_max = l_error; + } + +/* + * repeat with smaller step + */ +/* printf("timest %g ; error_max %g\n", h, error_max); */ + if (error_max > 1) + { + h_old = h; + if (step_ok == 0) + h = h * safety / error_max; + else + h = h * safety * pow(error_max, (LDBLE) -0.25); + l_bad = TRUE; + step_bad++; + } + else + { +/* + * OK, calculate result + */ + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(c1 * rk_moles[j] + + c3 * rk_moles[2 * n_reactions + j] + + c4 * rk_moles[3 * n_reactions + j] + + c6 * rk_moles[5 * n_reactions + j]); + } + calc_final_kinetic_reaction(kinetics_ptr); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_m(m_temp[j] - kinetics_comp_ptr->Get_moles()); + if (kinetics_comp_ptr->Get_m() < 1.e-30) + kinetics_comp_ptr->Set_m(0); + kinetics_comp_ptr->Set_moles(0.); + } + + if (set_and_run_wrapper(i, NOMIX, TRUE, i, 0.) == MASS_BALANCE) + { + run_reactions_iterations += iterations; + moles_reduction = 9; + goto MOLES_TOO_LARGE; + } + run_reactions_iterations += iterations; +/* + * Move next calc'n to rk = 1 when initial rate equals final rate ... + */ + calc_kinetic_reaction(kinetics_ptr, h); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + if (fabs(rk_moles[j] - kinetics_comp_ptr->Get_moles()) > + kinetics_comp_ptr->Get_tol()) + { + equal_rate = FALSE; + break; + } + } + if (equal_rate && kinetics_ptr->Get_rk() < 6) + kinetics_ptr->Set_rk(1); + + saver(); + + step_ok++; + h_sum += h; + /* Free space */ + + if (pp_assemblage_save != NULL) + { + delete pp_assemblage_save; + pp_assemblage_save = NULL; + } + if (ss_assemblage_save != NULL) + { + delete ss_assemblage_save; + ss_assemblage_save = NULL; + } +/* + * and increase step size ... + */ + if (h_sum < kin_time) + { + if (error_max > 0.000577) + { + h = h * safety * pow(error_max, (LDBLE) -0.2e0); + } + else + { + h *= 4; + } + if (h > (kin_time - h_sum)) + h = (kin_time - h_sum); + } + } + { + char str[MAX_LENGTH]; + sprintf(str, "RK-steps: Bad%4d. OK%5d. Time %3d%%", step_bad, + step_ok, (int) (100 * h_sum / kin_time)); + status(0, str, true); + } +#ifdef SKIP +#if !defined(PHREEQCI_GUI) +#ifndef PHREEQ98 + if (pr.status == TRUE && status_on == TRUE + && (int) (1e3 / CLOCKS_PER_SEC * ((float) clock() - status_timer)) > status_interval) + { + char str[MAX_LENGTH]; + backspace_screen(37); + sprintf(str, "RK-steps: Bad%4d. OK%5d. Time %3d%%", step_bad, + step_ok, (int) (100 * h_sum / kin_time)); + screen_msg(sformatf("%-37s", str)); + } +#endif +#endif +#endif + } + + EQUAL_RATE_OUT: + +/* + * Run one more time to get distribution of species + */ + if (state >= REACTION || nsaver != i) + { + set_and_run_wrapper(i, NOMIX, FALSE, nsaver, 0.); + run_reactions_iterations += iterations; + } +/* saver(); */ /* reset for printing */ + if (use_mix == DISP) + { + use.Set_mix_ptr(Utilities::Rxn_find(Dispersion_mix_map, i)); + use.Set_mix_in(true); + use.Set_n_mix_user(i); + } + else if ((use_mix == STAG || use_mix == TRUE) && state == TRANSPORT) + { + use.Set_mix_ptr(Utilities::Rxn_find(Rxn_mix_map, i)); + if (use.Get_mix_ptr() != NULL) + { + use.Set_mix_in(true); + use.Set_n_mix_user(i); + } + } +/* + * Restore solution i, if necessary + */ + if (nsaver != i) + { + Utilities::Rxn_copy(Rxn_solution_map, save_old, i); + } + rk_moles = (LDBLE *) free_check_null(rk_moles); + + rate_sim_time = rate_sim_time_start + kin_time; + use.Set_kinetics_in(true); + + /* Free space */ + + if (pp_assemblage_save != NULL) + { + delete pp_assemblage_save; + pp_assemblage_save = NULL; + } + if (ss_assemblage_save != NULL) + { + delete ss_assemblage_save; + ss_assemblage_save = NULL; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_and_run_wrapper(int i, int use_mix, int use_kinetics, int nsaver, + LDBLE step_fraction) +/* ---------------------------------------------------------------------- */ +{ + int j, converge, max_try; + int old_diag, old_itmax; + LDBLE old_tol, old_min_value, old_step, old_pe, old_pp_column_scale; + LDBLE small_pe_step, small_step; +#if (__GNUC__ && (__cplusplus >= 201103L)) || (_MSC_VER >= 1600) + std::unique_ptr pp_assemblage_save=NULL; + std::unique_ptr ss_assemblage_save=NULL; + std::unique_ptr kinetics_save=NULL; +#else + std::auto_ptr pp_assemblage_save(NULL); + std::auto_ptr ss_assemblage_save(NULL); + std::auto_ptr kinetics_save(NULL); +#endif + + + small_pe_step = 5.; + small_step = 10.; + converge = FALSE; + + old_diag = diagonal_scale; + old_itmax = itmax; + old_tol = ineq_tol; + old_step = step_size; + old_pe = pe_step_size; + old_min_value = min_value; + old_pp_column_scale = pp_column_scale; + int old_equi_delay = equi_delay; + + if (state == TRANSPORT || state == PHAST) + { + set_transport(i, use_mix, use_kinetics, i); + } + else if (state == ADVECTION) + { + set_advection(i, use_mix, use_kinetics, i); + } + else if (state == REACTION) + { + set_reaction(i, use_mix, use_kinetics); + } + if (use.Get_pp_assemblage_ptr() != NULL) + { + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + pp_assemblage_save.reset(new cxxPPassemblage(*pp_assemblage_ptr)); + } + if (use.Get_ss_assemblage_ptr() != NULL) + { + cxxSSassemblage * ss_assemblage_ptr = use.Get_ss_assemblage_ptr(); + ss_assemblage_save.reset(new cxxSSassemblage(*ss_assemblage_ptr)); + } + if (use.Get_kinetics_ptr() != NULL) + { + kinetics_save.reset(new cxxKinetics(*use.Get_kinetics_ptr())); + } + + if (pitzer_model == TRUE || sit_model == TRUE) + { + diagonal_scale = TRUE; + always_full_pitzer = FALSE; + max_try = 13; + } + else + { + max_try = 14; + } + max_try = (max_tries < max_try) ? max_tries : max_try; + /*max_try = 1; */ + for (j = 0; j < max_try; j++) + { + if (j == 1) + { + /*always_full_pitzer = TRUE;*/ + if (pe_step_size <= small_pe_step && step_size <= small_step) + continue; + itmax *= 2; + step_size = small_step; + pe_step_size = small_pe_step; + error_string = sformatf( + "Trying smaller step size, pe step size %g, %g ... \n", + (double) step_size, (double) pe_step_size); + warning_msg(error_string); + } + else if (j == 2) + { + itmax *= 2; + ineq_tol /= 10.; + error_string = sformatf( "Trying reduced tolerance %g ...\n", + (double) ineq_tol); + warning_msg(error_string); + } + else if (j == 3) + { + itmax *= 2; + ineq_tol *= 10.; + error_string = sformatf( "Trying increased tolerance %g ...\n", + (double) ineq_tol); + warning_msg(error_string); + } + else if (j == 4) + { + always_full_pitzer = TRUE; + itmax *= 2; + if (diagonal_scale == TRUE) + { + diagonal_scale = FALSE; + } + else + { + diagonal_scale = TRUE; + } + error_string = sformatf( "Trying diagonal scaling ...\n"); + warning_msg(error_string); + } + else if (j == 5) + { + itmax *= 2; + if (diagonal_scale == TRUE) + { + diagonal_scale = FALSE; + } + else + { + diagonal_scale = TRUE; + } + ineq_tol /= 10.; + error_string = sformatf( + "Trying diagonal scaling and reduced tolerance %g ...\n", + (double) ineq_tol); + warning_msg(error_string); + } + else if (j == 6) + { + if (pitzer_model == TRUE || sit_model == TRUE) continue; + itmax *= 2; + pp_column_scale = 1e-10; + error_string = sformatf( + "Trying scaling pure_phase columns %g ...\n", + (double) pp_column_scale); + warning_msg(error_string); + } + else if (j == 7) + { + if (pitzer_model == TRUE || sit_model == TRUE) continue; + itmax *= 2; + pp_column_scale = 1e-10; + if (diagonal_scale == TRUE) + { + diagonal_scale = FALSE; + } + else + { + diagonal_scale = TRUE; + } + error_string = sformatf( + "Trying scaling pure_phase columns and diagonal scale %g ...\n", + (double) pp_column_scale); + warning_msg(error_string); + } + else if (j == 8) + { + if (use.Get_pp_assemblage_ptr() == NULL) continue; + if (equi_delay > 0) + { + equi_delay = 0; + } + else + { + equi_delay = 1; + } + error_string = sformatf( "Trying delay removal of equilibrium phases %g ...\n", + (double) equi_delay); + warning_msg(error_string); + } + + else if (j == 9) + { + if (pitzer_model == TRUE || sit_model == TRUE) continue; + itmax *= 2; + min_value *= 10; + error_string = sformatf( "Trying increased scaling %g ...\n", + (double) min_value); + warning_msg(error_string); + } + else if (j == 10) + { + if (pitzer_model == TRUE || sit_model == TRUE) continue; + aqueous_only = 5; + error_string = sformatf( + "Skipping optimize equations for first %d iterations ...\n", + aqueous_only); + warning_msg(error_string); + } + else if (j == 11) + { + if (pitzer_model == TRUE || sit_model == TRUE) continue; + negative_concentrations = TRUE; + error_string = sformatf( + "Adding inequality to make concentrations greater than zero.\n"); + warning_msg(error_string); + } + else if (j == 12) + { + itmax *= 2; + ineq_tol /= 100.; + error_string = sformatf( "Trying reduced tolerance %g ...\n", + (double) ineq_tol); + warning_msg(error_string); + } + else if (j == 13) + { + itmax *= 2; + ineq_tol /= 1000.; + error_string = sformatf( "Trying reduced tolerance %g ...\n", + (double) ineq_tol); + warning_msg(error_string); + } + if (j > 0) + { + if (pp_assemblage_save.get() != NULL) + { + Rxn_pp_assemblage_map[pp_assemblage_save->Get_n_user()] = *pp_assemblage_save; + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, pp_assemblage_save->Get_n_user())); + } + if (ss_assemblage_save.get() != NULL) + { + Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save; + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user())); + } + if (kinetics_save.get() != NULL) + { + Rxn_kinetics_map[kinetics_save->Get_n_user()] = *kinetics_save; + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, kinetics_save->Get_n_user())); + } + } + set_and_run_attempt = j; + + converge = + set_and_run(i, use_mix, use_kinetics, nsaver, step_fraction); + /* reset values */ + diagonal_scale = old_diag; + itmax = old_itmax; + ineq_tol = old_tol; + step_size = old_step; + pe_step_size = old_pe; + min_value = old_min_value; + pp_column_scale = old_pp_column_scale; + equi_delay = old_equi_delay; + aqueous_only = 0; + negative_concentrations = FALSE; + always_full_pitzer = FALSE; + if (converge == TRUE) + { + break; + } + else if (converge == MASS_BALANCE) + { + break; + } + warning_msg + ("Numerical method failed with this set of convergence parameters.\n"); + } + if (converge == FALSE && use.Get_kinetics_ptr() != NULL + && use.Get_kinetics_ptr()->Get_use_cvode()) + { + error_string = sformatf( + "Numerical method failed on all parameter combinations, retrying integration, cell/soln %d", this->solution_number()); + warning_msg(error_string); + converge = MASS_BALANCE; + } + if (converge == FALSE) + { +/* + * write to error.inp what failed to converge. + */ + std::ofstream error_input("error.inp"); + cxxStorageBin error_bin; + Use2cxxStorageBin(error_bin); + error_bin.dump_raw(error_input, 0); + error_input.close(); + + /* if (state == TRANSPORT && dump_modulus == 0) dump(); */ + check_residuals(); + pr.all = TRUE; + pr.gas_phase = use.Get_gas_phase_in(); + pr.pp_assemblage = use.Get_pp_assemblage_in(); + pr.ss_assemblage = use.Get_ss_assemblage_in(); + pr.surface = use.Get_surface_in(); + pr.exchange = use.Get_exchange_in(); + pr.totals = TRUE; + pr.species = TRUE; + pr.saturation_indices = TRUE; + pr.irrev = use.Get_reaction_in(); + pr.mix = use.Get_mix_in(); + pr.reaction = TRUE; + pr.use = TRUE; + sum_species(); + print_all(); + error_string = sformatf( + "Numerical method failed on all combinations of convergence parameters, cell/soln/mix %d", this->solution_number()); + error_msg(error_string, STOP); + } + numerical_fixed_volume = false; + if (converge == MASS_BALANCE) + { + return (MASS_BALANCE); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_and_run(int i, int use_mix, int use_kinetics, int nsaver, + LDBLE step_fraction) +/* ---------------------------------------------------------------------- */ +{ +/* + * i --user number for soln, reaction, etc. + * use_mix --integer flag + state == TRANSPORT: DISP, STAG, NOMIX + state == REACTION: TRUE, FALSE + * use_kinetics --true or false flag to calculate kinetic reactions + * nsaver --user number to store solution + * step_fraction--fraction of irreversible reaction to add + */ + int converge; + if (state == TRANSPORT || state == PHAST) + { + set_transport(i, use_mix, use_kinetics, nsaver); + } + else if (state == ADVECTION) + { + set_advection(i, use_mix, use_kinetics, nsaver); + } + else if (state == REACTION) + { + set_reaction(i, use_mix, use_kinetics); + } + cell = i; +/* + * Take step + */ + if (state >= REACTION) + { + if (step(step_fraction) == MASS_BALANCE) + { + return (MASS_BALANCE); + } +/* + * Always use solution, exchange, and surface -1 + */ + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, -1)); + /* new */ + if (use.Get_exchange_ptr() != NULL) + { + use.Set_exchange_ptr(Utilities::Rxn_find(Rxn_exchange_map, -1)); + } + if (use.Get_surface_ptr() != NULL) + { + use.Set_surface_ptr(Utilities::Rxn_find(Rxn_surface_map, -1)); + } + +/* + * Adjust the total pressure to the gas pressure + */ + if (use.Get_gas_phase_ptr() != NULL) + { + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + /* + * Fixed-pressure Gas phase and solution will react + * Change total pressure of current simulation to pressure + * of gas phase + */ + patm_x = gas_phase_ptr->Get_total_p(); + } + /* fixed volume gas phase is handled in calc_gas_pressures */ + + } + } + /* end new */ + if (use.Get_surface_ptr() != NULL) + { + dl_type_x = use.Get_surface_ptr()->Get_dl_type(); + } + if (use.Get_surface_ptr() != NULL && dl_type_x != cxxSurface::NO_DL) + { + converge = surface_model(); + } + else + { + prep(); + k_temp(use.Get_solution_ptr()->Get_tc(), use.Get_solution_ptr()->Get_patm()); + set(FALSE); + converge = model(); + } + sum_species(); + viscosity(); + return (converge); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_transport(int i, int use_mix, int use_kinetics, int nsaver) +/* ---------------------------------------------------------------------- */ +{ +/* + * i --user number for soln, reaction, etc. + * use_mix --integer flag + state == TRANSPORT: DISP, STAG, NOMIX + state == REACTION: TRUE, FALSE + * use_kinetics --true or false flag to calculate kinetic reactions + * nsaver --user number to store solution + */ + cell = i; + reaction_step = 1; +/* + * Find mixture or solution + */ + + use.Set_mix_ptr(NULL); + use.Set_mix_in(false); + if (use_mix == DISP) + { + use.Set_mix_ptr(Utilities::Rxn_find(Dispersion_mix_map, i)); + use.Set_mix_in(true); + use.Set_n_mix_user(i); + use.Set_n_mix_user_orig(i); + } + else if (use_mix == STAG && multi_Dflag != TRUE) + { + use.Set_mix_ptr(Utilities::Rxn_find(Rxn_mix_map, i)); + if (use.Get_mix_ptr() != NULL) + { + use.Set_mix_in(true); + use.Set_n_mix_user(i); + use.Set_n_mix_user_orig(i); + } + else + { + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, i)); + if (use.Get_solution_ptr() == NULL) + { + error_string = sformatf( "Solution %d not found, while searching mix structure for solution %d.", + i, use.Get_n_solution_user()); + error_msg(error_string, STOP); + } + use.Set_n_solution_user(i); + use.Set_solution_in(true); + } + } + else + { + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, i)); + if (use.Get_solution_ptr() == NULL) + { + error_string = sformatf( "Solution %d not found, while searching mix structure for solution %d.", + i, use.Get_n_solution_user()); + error_msg(error_string, STOP); + } + use.Set_n_solution_user(i); + use.Set_solution_in(true); + } + save.solution = TRUE; + save.n_solution_user = nsaver; + save.n_solution_user_end = nsaver; +/* + * Find pure phase assemblage + */ + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, i)); + if (use.Get_pp_assemblage_ptr() != NULL) + { + use.Set_pp_assemblage_in(true); + use.Set_n_pp_assemblage_user(i); + save.pp_assemblage = TRUE; + save.n_pp_assemblage_user = i; + save.n_pp_assemblage_user_end = i; + } + else + { + use.Set_pp_assemblage_in(false); + save.pp_assemblage = FALSE; + } +/* + * Find irreversible reaction + */ + use.Set_reaction_ptr(Utilities::Rxn_find(Rxn_reaction_map, i)); + if (use.Get_reaction_ptr() != NULL) + { + use.Set_reaction_in(true); + use.Set_n_reaction_user(i); + } + else + { + use.Set_reaction_in(false); + } +/* + * Find exchange + */ + use.Set_exchange_ptr(Utilities::Rxn_find(Rxn_exchange_map, i)); + if (use.Get_exchange_ptr() != NULL) + { + use.Set_exchange_in(true); + use.Set_n_exchange_user(i); + save.exchange = TRUE; + save.n_exchange_user = i; + save.n_exchange_user_end = i; + } + else + { + use.Set_exchange_in(false); + save.exchange = FALSE; + } + +/* + * Find surface + */ + use.Set_surface_ptr(Utilities::Rxn_find(Rxn_surface_map, i)); + if (use.Get_surface_ptr() != NULL) + { + use.Set_surface_in(true); + use.Set_n_surface_user(i); + save.surface = TRUE; + save.n_surface_user = i; + save.n_surface_user_end = i; + } + else + { + use.Set_surface_in(false); + save.surface = FALSE; + dl_type_x = cxxSurface::NO_DL; + } +/* + * Find temperature; temp retardation is done in step + */ + use.Set_temperature_ptr(Utilities::Rxn_find(Rxn_temperature_map, i)); + if (use.Get_temperature_ptr() != NULL) + { + use.Set_temperature_in(true); + use.Set_n_temperature_user(i); + } + else + { + use.Set_temperature_in(false); + } +/* + * Find pressure + */ + use.Set_pressure_ptr(Utilities::Rxn_find(Rxn_pressure_map, i)); + if (use.Get_pressure_ptr() != NULL) + { + use.Set_pressure_in(true); + use.Set_n_pressure_user(i); + } + else + { + use.Set_pressure_in(false); + } +/* + * Find gas + */ + use.Set_gas_phase_ptr(Utilities::Rxn_find(Rxn_gas_phase_map, i)); + if (use.Get_gas_phase_ptr() != NULL) + { + use.Set_gas_phase_in(true); + use.Set_n_gas_phase_user(i); + save.gas_phase = TRUE; + save.n_gas_phase_user = i; + save.n_gas_phase_user_end = i; + } + else + { + use.Set_gas_phase_in(false); + save.gas_phase = FALSE; + } +/* + * Find ss_assemblage + */ + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, i)); + if (use.Get_ss_assemblage_ptr() != NULL) + { + use.Set_ss_assemblage_in(true); + use.Set_n_ss_assemblage_user(i); + save.ss_assemblage = TRUE; + save.n_ss_assemblage_user = i; + save.n_ss_assemblage_user_end = i; + } + else + { + use.Set_ss_assemblage_in(false); + save.ss_assemblage = FALSE; + } +/* + * Find kinetics + */ + use.Set_kinetics_ptr(NULL); + use.Set_kinetics_in(false); + save.kinetics = FALSE; + if (use_kinetics == TRUE) + { + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, i)); + if (use.Get_kinetics_ptr() != NULL) + { + use.Set_n_kinetics_user(i); + use.Set_kinetics_in(true); + save.kinetics = TRUE; + save.n_kinetics_user = i; + save.n_kinetics_user_end = i; + } + } + /* + if (use.irrev_ptr != NULL && use.Get_kinetics_ptr() != NULL) + { + warning_msg("Should not use REACTION in same simulation with KINETICS."); + } + */ + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_reaction(int i, int use_mix, int use_kinetics) +/* ---------------------------------------------------------------------- */ +{ +/* + * i --user number for soln, reaction, etc. + * use_mix --integer flag + state == TRANSPORT: DISP, STAG, NOMIX + state == REACTION: TRUE, FALSE + * use_kinetics --true or false flag to calculate kinetic reactions + */ +/* + * Find mixture or solution + */ + use.Set_mix_ptr(NULL); + use.Set_solution_ptr(NULL); + if (use_mix == TRUE && use.Get_mix_in() == TRUE) + { + use.Set_mix_ptr(Utilities::Rxn_find(Rxn_mix_map, i)); + if (use.Get_mix_ptr() == NULL) + { + error_string = sformatf( "MIX %d not found.", i); + error_msg(error_string, STOP); + } + } + else + { + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, i)); + if (use.Get_solution_ptr() == NULL) + { + error_string = sformatf( "Solution %d not found.", i); + error_msg(error_string, STOP); + } + } +/* + * Find pure phase assemblage + */ + if (use.Get_pp_assemblage_in() == TRUE) + { + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, i)); + if (use.Get_pp_assemblage_ptr() == NULL) + { + error_string = sformatf( "PP_ASSEMBLAGE %d not found.", i); + error_msg(error_string, STOP); + } + } + +/* + * Find irreversible reaction + */ + if (use.Get_reaction_in() == TRUE) + { + use.Set_reaction_ptr(Utilities::Rxn_find(Rxn_reaction_map, i)); + if (use.Get_reaction_ptr() == NULL) + { + error_string = sformatf( "REACTION %d not found.", i); + error_msg(error_string, STOP); + } + } +/* + * Find exchange + */ + if (use.Get_exchange_in() == TRUE) + { + use.Set_exchange_ptr(Utilities::Rxn_find(Rxn_exchange_map, i)); + if (use.Get_exchange_ptr() == NULL) + { + error_string = sformatf( "EXCHANGE %d not found.", i); + error_msg(error_string, STOP); + } + } +/* + * Find surface + */ + dl_type_x = cxxSurface::NO_DL; + if (use.Get_surface_in() == TRUE) + { + use.Set_surface_ptr(Utilities::Rxn_find(Rxn_surface_map, i)); + if (use.Get_surface_ptr() == NULL) + { + error_string = sformatf( "SURFACE %d not found.", i); + error_msg(error_string, STOP); + } + } +/* + * Find temperature; temp retardation is done in step + */ + if (use.Get_temperature_in() == TRUE) + { + use.Set_temperature_ptr(Utilities::Rxn_find(Rxn_temperature_map, i)); + if (use.Get_temperature_ptr() == NULL) + { + error_string = sformatf( "TEMPERATURE %d not found.", i); + error_msg(error_string, STOP); + } + } +/* + * Find pressure + */ + if (use.Get_pressure_in() == TRUE) + { + use.Set_pressure_ptr(Utilities::Rxn_find(Rxn_pressure_map, i)); + if (use.Get_pressure_ptr() == NULL) + { + error_string = sformatf( "PRESSURE %d not found.", i); + error_msg(error_string, STOP); + } + } +/* + * Find gas + */ + if (use.Get_gas_phase_in() == TRUE) + { + use.Set_gas_phase_ptr(Utilities::Rxn_find(Rxn_gas_phase_map, i)); + if (use.Get_gas_phase_ptr() == NULL) + { + error_string = sformatf( "GAS_PHASE %d not found.", i); + error_msg(error_string, STOP); + } + } +/* + * Find ss_assemblage + */ + if (use.Get_ss_assemblage_in() == TRUE) + { + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, i)); + if (use.Get_ss_assemblage_ptr() == NULL) + { + error_string = sformatf( "Solid-solution Assemblage %d not found.", + i); + error_msg(error_string, STOP); + } + } +/* + * Find kinetics + */ + if (use_kinetics == TRUE && use.Get_kinetics_in() == TRUE) + { + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, i)); + if (use.Get_kinetics_ptr() == NULL) + { + error_string = sformatf( "KINETICS %d not found.", i); + error_msg(error_string, STOP); + } + } + else + { + use.Set_kinetics_ptr(NULL); + } + /* + if (use.irrev_ptr != NULL && use.Get_kinetics_ptr() != NULL) + { + warning_msg("Should not use REACTION in same simulation with KINETICS."); + } + */ + return (OK); +} +//#define REVISED_CVODE +#ifdef REVISED_CVODE +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +run_reactions(int i, LDBLE kin_time, int use_mix, LDBLE step_fraction) +/* ---------------------------------------------------------------------- */ +{ +/* + * Kinetics calculations + * Rates and moles of each reaction are calculated in calc_kinetic_reaction + * Total number of moles in reaction is stored in kinetics[i].totals + */ + + int converge, m_iter; + int pr_all_save; + int nsaver; + cxxKinetics *kinetics_ptr; + cxxPPassemblage *pp_assemblage_ptr; + cxxSSassemblage *ss_assemblage_ptr; + cxxUse use_save; + int save_old, n_reactions /*, nok, nbad */ ; + + /* CVODE definitions */ + realtype ropt[OPT_SIZE], reltol, t, tout, tout1, sum_t; + long int iopt[OPT_SIZE]; + int flag; +/* + * Set nsaver + */ + run_reactions_iterations = 0; + kin_time_x = kin_time; + rate_kin_time = kin_time; + nsaver = i; + if (state == TRANSPORT || state == PHAST) + { + if (use_mix == DISP) + { + nsaver = -2; + } + else if (use_mix == STAG) + { + nsaver = -2 - i; + } + } + if (state == ADVECTION) + { + nsaver = -2; + } +/* + * Check that reaction exists for this cell .. + */ + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i); + if (kin_time <= 0 || + (state == REACTION && use.Get_kinetics_in() == FALSE) || + (state == TRANSPORT && kinetics_ptr == NULL) || + (state == PHAST && kinetics_ptr == NULL) || + (state == ADVECTION && kinetics_ptr == NULL)) + { + converge = set_and_run_wrapper(i, use_mix, FALSE, nsaver, step_fraction); + if (converge == MASS_BALANCE) + { + error_msg("Negative concentration in system. Stopping calculation.", STOP); + } + run_reactions_iterations += iterations; + } + else + { +/* + * Save moles of kinetic reactants for printout... + */ + size_t count_comps = kinetics_ptr->Get_kinetics_comps().size(); + m_temp = (LDBLE *) PHRQ_malloc(count_comps * sizeof(LDBLE)); + if (m_temp == NULL) + malloc_error(); + + m_original = (LDBLE *) PHRQ_malloc(count_comps * sizeof(LDBLE)); + if (m_original == NULL) + malloc_error(); + + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + m_original[j] = kinetics_comp_ptr->Get_m(); + m_temp[j] = kinetics_comp_ptr->Get_m(); + } +/* +* Start the loop for timestepping ... + * Use either Runge-Kutta-Fehlberg, or final result extrapolation + */ + pr_all_save = pr.all; + pr.all = FALSE; +/* + * This condition makes output equal for incremental_reactions TRUE/FALSE... + * (if (incremental_reactions == FALSE || reaction_step == 1) + */ + store_get_equi_reactants(i, FALSE); + if (!kinetics_ptr->Get_use_cvode()) + { + rk_kinetics(i, kin_time, use_mix, nsaver, step_fraction); + + // finish up + rate_sim_time = rate_sim_time_start + kin_time; + store_get_equi_reactants(i, TRUE); + pr.all = pr_all_save; + + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(m_original[j] - kinetics_comp_ptr->Get_m()); + } + m_temp = (LDBLE *) free_check_null(m_temp); + m_original = (LDBLE *) free_check_null(m_original); + } + else + { + // save initial reactants + // cxxStorageBin save_bin(use); // if needed + + save_old = -2 - (count_cells * (1 + stag_data->count_stag) + 2); + if (nsaver != i) + { + Utilities::Rxn_copy(Rxn_solution_map, i, save_old); + } + for (int j = 0; j < OPT_SIZE; j++) + { + iopt[j] = 0; + ropt[j] = 0; + } + +/* + * Do mix first + */ + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i); + n_reactions = (int) kinetics_ptr->Get_kinetics_comps().size(); + cvode_n_user = i; + cvode_kinetics_ptr = (void *) kinetics_ptr; + cvode_n_reactions = n_reactions; + cvode_rate_sim_time_start = rate_sim_time_start; + cvode_rate_sim_time = rate_sim_time; + + if (multi_Dflag) + converge = set_and_run_wrapper(i, NOMIX, FALSE, i, 0.0); + else + converge = set_and_run_wrapper(i, use_mix, FALSE, i, 0.0); + if (converge == MASS_BALANCE) + error_msg + ("Negative concentration in system. Stopping calculation.", + STOP); + saver(); + pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, i); + ss_assemblage_ptr = Utilities::Rxn_find(Rxn_ss_assemblage_map, i); + if (pp_assemblage_ptr != NULL) + { + cvode_pp_assemblage_save = new cxxPPassemblage(*pp_assemblage_ptr); + } + if (ss_assemblage_ptr != NULL) + { + cvode_ss_assemblage_save = new cxxSSassemblage(*ss_assemblage_ptr); + } + + /* allocate space for CVODE */ + kinetics_machEnv = M_EnvInit_Serial(n_reactions); + kinetics_machEnv->phreeqc_ptr = this; + kinetics_y = N_VNew(n_reactions, kinetics_machEnv); /* Allocate y, abstol vectors */ + if (kinetics_y == NULL) + malloc_error(); + cvode_last_good_y = N_VNew(n_reactions, kinetics_machEnv); /* Allocate y, abstol vectors */ + if (cvode_last_good_y == NULL) + malloc_error(); + cvode_prev_good_y = N_VNew(n_reactions, kinetics_machEnv); /* Allocate y, abstol vectors */ + if (cvode_prev_good_y == NULL) + malloc_error(); + kinetics_abstol = N_VNew(n_reactions, kinetics_machEnv); + if (kinetics_abstol == NULL) + malloc_error(); + for (int j = 0; j < n_reactions; j++) + { + Ith(cvode_last_good_y, j + 1) = 0.0; + Ith(cvode_prev_good_y, j + 1) = 0.0; + Ith(kinetics_abstol, j + 1) = 0.0; + } +/* + * Set y to 0.0 + */ + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(0.0); + Ith(kinetics_y, j + 1) = 0.0; + Ith(kinetics_abstol, j + 1) = kinetics_comp_ptr->Get_tol(); + } + reltol = 0.0; + + /* Call CVodeMalloc to initialize CVODE: + + NEQ is the problem size = number of equations + f is the user's right hand side function in y'=f(t,y) + T0 is the initial time + y is the initial dependent variable vector + BDF specifies the Backward Differentiation Formula + NEWTON specifies a Newton iteration + SV specifies scalar relative and vector absolute tolerances + &reltol is a pointer to the scalar relative tolerance + abstol is the absolute tolerance vector + FALSE indicates there are no optional inputs in iopt and ropt + iopt is an array used to communicate optional integer input and output + ropt is an array used to communicate optional real input and output + + A pointer to CVODE problem memory is returned and stored in cvode_mem. */ + /* Don`t know what this does */ + /* + iopt[SLDET] = TRUE; + cvode_mem = CVodeMalloc(n_reactions, f, 0.0, y, BDF, NEWTON, SV, &reltol, abstol, NULL, NULL, TRUE, iopt, ropt, machEnv); + cvode_mem = CVodeMalloc(n_reactions, f, 0.0, y, ADAMS, FUNCTIONAL, SV, &reltol, abstol, NULL, NULL, FALSE, iopt, ropt, machEnv); + iopt[MXSTEP] is maximum number of steps that CVODE tries. + */ + iopt[MXSTEP] = kinetics_ptr->Get_cvode_steps(); + iopt[MAXORD] = kinetics_ptr->Get_cvode_order(); + kinetics_cvode_mem = + CVodeMalloc(n_reactions, f, 0.0, kinetics_y, BDF, NEWTON, SV, + &reltol, kinetics_abstol, this, NULL, TRUE, iopt, + ropt, kinetics_machEnv); + if (kinetics_cvode_mem == NULL) + malloc_error(); + + /* Call CVDense to specify the CVODE dense linear solver with the + user-supplied Jacobian routine Jac. */ + flag = CVDense(kinetics_cvode_mem, Jac, this); + if (flag != SUCCESS) + { + error_msg("CVDense failed.", STOP); + } + t = 0; + tout = kin_time; + /*ropt[HMAX] = tout/10.; */ + /*ropt[HMIN] = 1e-17; */ + use_save = use; + flag = CVode(kinetics_cvode_mem, tout, kinetics_y, &t, NORMAL); + rate_sim_time = rate_sim_time_start + t; + /* + printf("At t = %0.4e y =%14.6e %14.6e %14.6e\n", + t, Ith(y,1), Ith(y,2), Ith(y,3)); + */ + m_iter = 0; + sum_t = 0; +RESTART: + while (flag != SUCCESS) + { + sum_t += cvode_last_good_time; + error_string = sformatf( + "CVode incomplete at cvode_steps %d. Cell: %d\tTime: %e\tCvode calls: %d, continuing...\n", + (int) iopt[NST], cell_no, (double) sum_t, m_iter + 1); + warning_msg(error_string); + + // run with last good y, update reactants + cvode_update_reactants(i, nsaver, true); + + cvode_last_good_time = 0; + if (++m_iter >= kinetics_ptr->Get_bad_step_max()) + { + m_temp = (LDBLE *) free_check_null(m_temp); + m_original = (LDBLE *) free_check_null(m_original); + error_msg("Repeated restart of integration.", STOP); + } + tout1 = tout - sum_t; + t = 0; + N_VScale(1.0, cvode_last_good_y, kinetics_y); + for (int j = 0; j < OPT_SIZE; j++) + { + iopt[j] = 0; + ropt[j] = 0; + } + CVodeFree(kinetics_cvode_mem); /* Free the CVODE problem memory */ + iopt[MXSTEP] = kinetics_ptr->Get_cvode_steps(); + iopt[MAXORD] = kinetics_ptr->Get_cvode_order(); + kinetics_cvode_mem = + CVodeMalloc(n_reactions, f, 0.0, kinetics_y, BDF, NEWTON, + SV, &reltol, kinetics_abstol, this, NULL, + TRUE, iopt, ropt, kinetics_machEnv); + if (kinetics_cvode_mem == NULL) + malloc_error(); + + /* Call CVDense to specify the CVODE dense linear solver with the + user-supplied Jacobian routine Jac. */ + flag = CVDense(kinetics_cvode_mem, Jac, this); + if (flag != SUCCESS) + { + error_msg("CVDense failed.", STOP); + } + flag = CVode(kinetics_cvode_mem, tout1, kinetics_y, &t, NORMAL); + } + // end cvode integration + + // update +#ifdef SKIP + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(Ith(kinetics_y, j + 1)); + kinetics_comp_ptr->Set_m(m_original[j] - kinetics_comp_ptr->Get_moles()); + if (kinetics_comp_ptr->Get_m() < 0) + { + kinetics_comp_ptr->Set_moles(m_original[j]); + kinetics_comp_ptr->Set_m(0.0); + } + } + if (use.Get_pp_assemblage_ptr() != NULL) + { + Rxn_pp_assemblage_map[cvode_pp_assemblage_save->Get_n_user()] = *cvode_pp_assemblage_save; + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, cvode_pp_assemblage_save->Get_n_user())); + } + if (use.Get_ss_assemblage_ptr() != NULL) + { + Rxn_ss_assemblage_map[cvode_ss_assemblage_save->Get_n_user()] = *cvode_ss_assemblage_save; + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, cvode_ss_assemblage_save->Get_n_user())); + } +#endif + + // put remaining kinetic reaction in last_good_y for update + N_VScale(1.0, kinetics_y, cvode_last_good_y); + if (!cvode_update_reactants(i, nsaver, false)) + { + warning_msg("FAIL 2 after successful integration in CVode"); + flag = -1; + goto RESTART; + } +#ifdef SKIP + calc_final_kinetic_reaction(kinetics_ptr); + if (set_and_run_wrapper(i, NOMIX, TRUE, nsaver, 1.0) == MASS_BALANCE) + { + warning_msg("FAIL 2 after successful integration in CVode"); + flag = -1; + goto RESTART; + } + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_m(m_original[j] - kinetics_comp_ptr->Get_moles()); + } +#endif +/* + * Restore solution i, if necessary + */ + if (nsaver != i) + { + Utilities::Rxn_copy(Rxn_solution_map, save_old, i); + } + free_cvode(); + use.Set_mix_in(use_save.Get_mix_in()); + use.Set_mix_ptr(use_save.Get_mix_ptr()); + + // finish up + rate_sim_time = rate_sim_time_start + kin_time; + store_get_equi_reactants(i, TRUE); + pr.all = pr_all_save; + +#ifdef SKIP + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(m_original[j] - kinetics_comp_ptr->Get_m()); + } +#else + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i); + cxxKinetics *kinetics_original_ptr = Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_kinetics_user()); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + cxxKineticsComp * kinetics_original_comp_ptr = &(kinetics_original_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(kinetics_original_comp_ptr->Get_m() - kinetics_comp_ptr->Get_m()); + } +#endif + m_temp = (LDBLE *) free_check_null(m_temp); + m_original = (LDBLE *) free_check_null(m_original); + } // end cvode + } + iterations = run_reactions_iterations; + if (cvode_pp_assemblage_save != NULL) + { + delete cvode_pp_assemblage_save; + cvode_pp_assemblage_save = NULL; + } + if (cvode_ss_assemblage_save != NULL) + { + delete cvode_ss_assemblage_save; + cvode_ss_assemblage_save = NULL; + } + return (OK); +} +#else +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +run_reactions(int i, LDBLE kin_time, int use_mix, LDBLE step_fraction) +/* ---------------------------------------------------------------------- */ +{ +/* + * Kinetics calculations + * Rates and moles of each reaction are calculated in calc_kinetic_reaction + * Total number of moles in reaction is stored in kinetics[i].totals + */ + + int converge, m_iter; + int pr_all_save; + int nsaver; + cxxKinetics *kinetics_ptr; + cxxPPassemblage *pp_assemblage_ptr; + cxxSSassemblage *ss_assemblage_ptr; + cxxUse use_save; + int save_old, n_reactions /*, nok, nbad */ ; + + /* CVODE definitions */ + realtype ropt[OPT_SIZE], reltol, t, tout, tout1, sum_t; + long int iopt[OPT_SIZE]; + int flag; +/* + * Set nsaver + */ + run_reactions_iterations = 0; + kin_time_x = kin_time; + rate_kin_time = kin_time; + nsaver = i; + if (state == TRANSPORT || state == PHAST) + { + if (use_mix == DISP) + { + nsaver = -2; + } + else if (use_mix == STAG) + { + nsaver = -2 - i; + } + } + if (state == ADVECTION) + { + nsaver = -2; + } +/* + * Check that reaction exists for this cell .. + */ + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i); + if (kin_time <= 0 || + (state == REACTION && use.Get_kinetics_in() == FALSE) || + (state == TRANSPORT && kinetics_ptr == NULL) || + (state == PHAST && kinetics_ptr == NULL) || + (state == ADVECTION && kinetics_ptr == NULL)) + { + converge = + set_and_run_wrapper(i, use_mix, FALSE, nsaver, step_fraction); + if (converge == MASS_BALANCE) + error_msg + ("Negative concentration in system. Stopping calculation.", + STOP); + run_reactions_iterations += iterations; + } + else + { +/* + * Save moles of kinetic reactants for printout... + */ + size_t count_comps = kinetics_ptr->Get_kinetics_comps().size(); + m_temp = (LDBLE *) PHRQ_malloc(count_comps * sizeof(LDBLE)); + if (m_temp == NULL) + malloc_error(); + + m_original = + (LDBLE *) PHRQ_malloc(count_comps * sizeof(LDBLE)); + if (m_original == NULL) + malloc_error(); + + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + m_original[j] = kinetics_comp_ptr->Get_m(); + m_temp[j] = kinetics_comp_ptr->Get_m(); + } +/* +* Start the loop for timestepping ... + * Use either Runge-Kutta-Fehlberg, or final result extrapolation + */ + pr_all_save = pr.all; + pr.all = FALSE; +/* + * This condition makes output equal for incremental_reactions TRUE/FALSE... + * (if (incremental_reactions == FALSE || reaction_step == 1) + */ + store_get_equi_reactants(i, FALSE); + if (!kinetics_ptr->Get_use_cvode()) + { +/* in case dispersivity is not wanted.. + if (multi_Dflag) + rk_kinetics(i, kin_time, NOMIX, nsaver, step_fraction); + else + */ + rk_kinetics(i, kin_time, use_mix, nsaver, step_fraction); + } + else + { + save_old = -2 - (count_cells * (1 + stag_data->count_stag) + 2); + if (nsaver != i) + { + Utilities::Rxn_copy(Rxn_solution_map, i, save_old); + } + for (int j = 0; j < OPT_SIZE; j++) + { + iopt[j] = 0; + ropt[j] = 0; + } + +/* + * Do mix first + */ + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i); + n_reactions = (int) kinetics_ptr->Get_kinetics_comps().size(); + cvode_n_user = i; + cvode_kinetics_ptr = (void *) kinetics_ptr; + cvode_n_reactions = n_reactions; + cvode_rate_sim_time_start = rate_sim_time_start; + cvode_rate_sim_time = rate_sim_time; + + if (multi_Dflag) + converge = set_and_run_wrapper(i, NOMIX, FALSE, i, 0.0); + else + converge = set_and_run_wrapper(i, use_mix, FALSE, i, 0.0); + if (converge == MASS_BALANCE) + error_msg + ("Negative concentration in system. Stopping calculation.", + STOP); + saver(); + pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, i); + ss_assemblage_ptr = Utilities::Rxn_find(Rxn_ss_assemblage_map, i); + if (pp_assemblage_ptr != NULL) + { + cvode_pp_assemblage_save = new cxxPPassemblage(*pp_assemblage_ptr); + } + if (ss_assemblage_ptr != NULL) + { + cvode_ss_assemblage_save = new cxxSSassemblage(*ss_assemblage_ptr); + } + + /* allocate space for CVODE */ + kinetics_machEnv = M_EnvInit_Serial(n_reactions); + kinetics_machEnv->phreeqc_ptr = this; + kinetics_y = N_VNew(n_reactions, kinetics_machEnv); /* Allocate y, abstol vectors */ + if (kinetics_y == NULL) + malloc_error(); + cvode_last_good_y = N_VNew(n_reactions, kinetics_machEnv); /* Allocate y, abstol vectors */ + if (cvode_last_good_y == NULL) + malloc_error(); + cvode_prev_good_y = N_VNew(n_reactions, kinetics_machEnv); /* Allocate y, abstol vectors */ + if (cvode_prev_good_y == NULL) + malloc_error(); + kinetics_abstol = N_VNew(n_reactions, kinetics_machEnv); + if (kinetics_abstol == NULL) + malloc_error(); + for (int j = 0; j < n_reactions; j++) + { + Ith(cvode_last_good_y, j + 1) = 0.0; + Ith(cvode_prev_good_y, j + 1) = 0.0; + Ith(kinetics_abstol, j + 1) = 0.0; + } +/* + * Set y to 0.0 + */ + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(0.0); + Ith(kinetics_y, j + 1) = 0.0; + Ith(kinetics_abstol, j + 1) = kinetics_comp_ptr->Get_tol(); + /*Ith(abstol,j+1) = 1e-8; */ + /* m_temp[j] = kinetics_ptr->comps[j].m; */ + } + reltol = 0.0; + + /* Call CVodeMalloc to initialize CVODE: + + NEQ is the problem size = number of equations + f is the user's right hand side function in y'=f(t,y) + T0 is the initial time + y is the initial dependent variable vector + BDF specifies the Backward Differentiation Formula + NEWTON specifies a Newton iteration + SV specifies scalar relative and vector absolute tolerances + &reltol is a pointer to the scalar relative tolerance + abstol is the absolute tolerance vector + FALSE indicates there are no optional inputs in iopt and ropt + iopt is an array used to communicate optional integer input and output + ropt is an array used to communicate optional real input and output + + A pointer to CVODE problem memory is returned and stored in cvode_mem. */ + /* Don`t know what this does */ + /* + iopt[SLDET] = TRUE; + cvode_mem = CVodeMalloc(n_reactions, f, 0.0, y, BDF, NEWTON, SV, &reltol, abstol, NULL, NULL, TRUE, iopt, ropt, machEnv); + cvode_mem = CVodeMalloc(n_reactions, f, 0.0, y, ADAMS, FUNCTIONAL, SV, &reltol, abstol, NULL, NULL, FALSE, iopt, ropt, machEnv); + iopt[MXSTEP] is maximum number of steps that CVODE tries. + */ + iopt[MXSTEP] = kinetics_ptr->Get_cvode_steps(); + iopt[MAXORD] = kinetics_ptr->Get_cvode_order(); + kinetics_cvode_mem = + CVodeMalloc(n_reactions, f, 0.0, kinetics_y, BDF, NEWTON, SV, + &reltol, kinetics_abstol, this, NULL, TRUE, iopt, + ropt, kinetics_machEnv); + if (kinetics_cvode_mem == NULL) + malloc_error(); + + /* Call CVDense to specify the CVODE dense linear solver with the + user-supplied Jacobian routine Jac. */ + flag = CVDense(kinetics_cvode_mem, Jac, this); + if (flag != SUCCESS) + { + error_msg("CVDense failed.", STOP); + } + t = 0; + tout = kin_time; + /*ropt[HMAX] = tout/10.; */ + /*ropt[HMIN] = 1e-17; */ + use_save = use; + flag = CVode(kinetics_cvode_mem, tout, kinetics_y, &t, NORMAL); + rate_sim_time = rate_sim_time_start + t; + /* + printf("At t = %0.4e y =%14.6e %14.6e %14.6e\n", + t, Ith(y,1), Ith(y,2), Ith(y,3)); + */ + m_iter = 0; + sum_t = 0; + RESTART: + while (flag != SUCCESS) + { + sum_t += cvode_last_good_time; + error_string = sformatf( + "CVode incomplete at cvode_steps %d. Cell: %d\tTime: %e\tCvode calls: %d, continuing...\n", + (int) iopt[NST], cell_no, (double) sum_t, m_iter + 1); + warning_msg(error_string); +#ifdef DEBUG_KINETICS + if (m_iter > 5) + dump_kinetics_stderr(cell_no); +#endif + + cvode_last_good_time = 0; + if (++m_iter >= kinetics_ptr->Get_bad_step_max()) + { + m_temp = (LDBLE *) free_check_null(m_temp); + m_original = (LDBLE *) free_check_null(m_original); + error_msg("Repeated restart of integration.", STOP); + } + tout1 = tout - sum_t; + t = 0; + N_VScale(1.0, cvode_last_good_y, kinetics_y); + for (int j = 0; j < OPT_SIZE; j++) + { + iopt[j] = 0; + ropt[j] = 0; + } + CVodeFree(kinetics_cvode_mem); /* Free the CVODE problem memory */ + iopt[MXSTEP] = kinetics_ptr->Get_cvode_steps(); + iopt[MAXORD] = kinetics_ptr->Get_cvode_order(); + kinetics_cvode_mem = + CVodeMalloc(n_reactions, f, 0.0, kinetics_y, BDF, NEWTON, + SV, &reltol, kinetics_abstol, this, NULL, + TRUE, iopt, ropt, kinetics_machEnv); + if (kinetics_cvode_mem == NULL) + malloc_error(); + + /* Call CVDense to specify the CVODE dense linear solver with the + user-supplied Jacobian routine Jac. */ + flag = CVDense(kinetics_cvode_mem, Jac, this); + if (flag != SUCCESS) + { + error_msg("CVDense failed.", STOP); + } + flag = + CVode(kinetics_cvode_mem, tout1, kinetics_y, &t, NORMAL); + /* + error_string = sformatf( "CVode failed, flag=%d.\n", flag); + error_msg(error_string, STOP); + */ + } + /* + odeint(&ystart[-1], n_reactions, 0, kin_time, kinetics_ptr->comps[0].tol, kin_time/kinetics_ptr->step_divide, 0.0, &nok, &nbad, i, nsaver ); + */ + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(Ith(kinetics_y, j + 1)); + kinetics_comp_ptr->Set_m(m_original[j] - kinetics_comp_ptr->Get_moles()); + if (kinetics_comp_ptr->Get_m() < 0) + { + kinetics_comp_ptr->Set_moles(m_original[j]); + kinetics_comp_ptr->Set_m(0.0); + } + } + if (use.Get_pp_assemblage_ptr() != NULL) + { + Rxn_pp_assemblage_map[cvode_pp_assemblage_save->Get_n_user()] = *cvode_pp_assemblage_save; + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, cvode_pp_assemblage_save->Get_n_user())); + } + if (use.Get_ss_assemblage_ptr() != NULL) + { + Rxn_ss_assemblage_map[cvode_ss_assemblage_save->Get_n_user()] = *cvode_ss_assemblage_save; + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, cvode_ss_assemblage_save->Get_n_user())); + } + calc_final_kinetic_reaction(kinetics_ptr); + if (set_and_run_wrapper(i, NOMIX, TRUE, nsaver, 1.0) == + MASS_BALANCE) + { + /*error_msg("FAIL 2 after successful integration in CVode", CONTINUE); */ + warning_msg("FAIL 2 after successful integration in CVode"); + flag = -1; + goto RESTART; + } + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_m(m_original[j] - kinetics_comp_ptr->Get_moles()); + } +/* + * Restore solution i, if necessary + */ + if (nsaver != i) + { + Utilities::Rxn_copy(Rxn_solution_map, save_old, i); + } + free_cvode(); + use.Set_mix_in(use_save.Get_mix_in()); + use.Set_mix_ptr(use_save.Get_mix_ptr()); + } + + rate_sim_time = rate_sim_time_start + kin_time; + store_get_equi_reactants(i, TRUE); + pr.all = pr_all_save; + + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i); + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(m_original[j] - kinetics_comp_ptr->Get_m()); +/* if (kinetics_ptr->comps[j].moles < 1.e-15) kinetics_ptr->comps[j].moles = 0.0; + */ } + m_temp = (LDBLE *) free_check_null(m_temp); + m_original = (LDBLE *) free_check_null(m_original); + } + iterations = run_reactions_iterations; + if (cvode_pp_assemblage_save != NULL) + { + delete cvode_pp_assemblage_save; + cvode_pp_assemblage_save = NULL; + } + if (cvode_ss_assemblage_save != NULL) + { + delete cvode_ss_assemblage_save; + cvode_ss_assemblage_save = NULL; + } + return (OK); +} +#endif +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +free_cvode(void) +/* ---------------------------------------------------------------------- */ +{ + if (kinetics_y != NULL) + N_VFree(kinetics_y); /* Free vector */ + kinetics_y = NULL; + if (cvode_last_good_y != NULL) + N_VFree(cvode_last_good_y); /* Free vector */ + cvode_last_good_y = NULL; + if (cvode_prev_good_y != NULL) + N_VFree(cvode_prev_good_y); /* Free vector */ + cvode_prev_good_y = NULL; + if (kinetics_abstol != NULL) + N_VFree(kinetics_abstol); /* Free vector */ + kinetics_abstol = NULL; + if (kinetics_cvode_mem != NULL) + CVodeFree(kinetics_cvode_mem); /* Free the CVODE problem memory */ + kinetics_cvode_mem = NULL; + if (kinetics_machEnv != NULL) + M_EnvFree_Serial(kinetics_machEnv); /* Free the machine environment memory */ + kinetics_machEnv = NULL; + if (cvode_pp_assemblage_save != NULL) + { + delete cvode_pp_assemblage_save; + cvode_pp_assemblage_save = NULL; + } + if (cvode_ss_assemblage_save != NULL) + { + delete cvode_ss_assemblage_save; + cvode_ss_assemblage_save = NULL; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_advection(int i, int use_mix, int use_kinetics, int nsaver) +/* ---------------------------------------------------------------------- */ +{ +/* + * i --user number for soln, reaction, etc. + * use_mix --integer flag + state == TRANSPORT: DISP, STAG, NOMIX + state == REACTION: TRUE, FALSE + state == ADVECTION: TRUE, FALSE + * use_kinetics --true or false flag to calculate kinetic reactions + * nsaver --user number to store solution + */ + + cell = i; + reaction_step = 1; +/* + * Find mixture or solution + */ + + use.Set_mix_ptr(NULL); + use.Set_mix_in(false); + use.Set_mix_ptr(Utilities::Rxn_find(Rxn_mix_map, i)); + if (use_mix == TRUE && use.Get_mix_ptr() != NULL) + { + use.Set_mix_in(true); + use.Set_n_mix_user(i); + use.Set_n_mix_user_orig(i); + use.Set_n_solution_user(i); + } + else + { + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, i)); + if (use.Get_solution_ptr() == NULL) + { + error_string = sformatf( "Solution %d not found.", + use.Get_n_solution_user()); + error_msg(error_string, STOP); + } + use.Set_n_solution_user(i); + use.Set_solution_in(true); + } + save.solution = TRUE; + save.n_solution_user = nsaver; + save.n_solution_user_end = nsaver; +/* + * Find pure phase assemblage + */ + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, i)); + if (use.Get_pp_assemblage_ptr() != NULL) + { + use.Set_pp_assemblage_in(true); + use.Set_n_pp_assemblage_user(i); + save.pp_assemblage = TRUE; + save.n_pp_assemblage_user = i; + save.n_pp_assemblage_user_end = i; + } + else + { + use.Set_pp_assemblage_in(false); + save.pp_assemblage = FALSE; + } +/* + * Find irreversible reaction + */ + use.Set_reaction_ptr(Utilities::Rxn_find(Rxn_reaction_map, i)); + if (use.Get_reaction_ptr() != NULL) + { + use.Set_reaction_in(true); + use.Set_n_reaction_user(i); + } + else + { + use.Set_reaction_in(false); + } +/* + * Find exchange + */ + use.Set_exchange_ptr(Utilities::Rxn_find(Rxn_exchange_map, i)); + if (use.Get_exchange_ptr() != NULL) + { + use.Set_exchange_in(true); + use.Set_n_exchange_user(i); + save.exchange = TRUE; + save.n_exchange_user = i; + save.n_exchange_user_end = i; + } + else + { + use.Set_exchange_in(false); + save.exchange = FALSE; + } + +/* + * Find surface + */ + use.Set_surface_ptr(Utilities::Rxn_find(Rxn_surface_map, i)); + if (use.Get_surface_ptr() != NULL) + { + use.Set_surface_in(true); + use.Set_n_surface_user(i); + save.surface = TRUE; + save.n_surface_user = i; + save.n_surface_user_end = i; + } + else + { + use.Set_surface_in(false); + save.surface = FALSE; + dl_type_x = cxxSurface::NO_DL; + } +/* + * Find temperature; temp retardation is done in step + */ + use.Set_temperature_ptr(Utilities::Rxn_find(Rxn_temperature_map, i)); + if (use.Get_temperature_ptr() != NULL) + { + use.Set_temperature_in(true); + use.Set_n_temperature_user(i); + } + else + { + use.Set_temperature_in(false); + } +/* + * Find pressure + */ + use.Set_pressure_ptr(Utilities::Rxn_find(Rxn_pressure_map, i)); + if (use.Get_pressure_ptr() != NULL) + { + use.Set_pressure_in(true); + use.Set_n_pressure_user(i); + } + else + { + use.Set_pressure_in(false); + } +/* + * Find gas + */ + use.Set_gas_phase_ptr(Utilities::Rxn_find(Rxn_gas_phase_map, i)); + if (use.Get_gas_phase_ptr() != NULL) + { + use.Set_gas_phase_in(true); + use.Set_n_gas_phase_user(i); + save.gas_phase = TRUE; + save.n_gas_phase_user = i; + save.n_gas_phase_user_end = i; + } + else + { + use.Set_gas_phase_in(false); + save.gas_phase = FALSE; + } +/* + * Find ss_assemblage + */ + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, i)); + if (use.Get_ss_assemblage_ptr() != NULL) + { + use.Set_ss_assemblage_in(true); + use.Set_n_ss_assemblage_user(i); + save.ss_assemblage = TRUE; + save.n_ss_assemblage_user = i; + save.n_ss_assemblage_user_end = i; + } + else + { + use.Set_ss_assemblage_in(false); + save.ss_assemblage = FALSE; + } +/* + * Find kinetics + */ + use.Set_kinetics_ptr(NULL); + use.Set_kinetics_in(false); + save.kinetics = FALSE; + if (use_kinetics == TRUE) + { + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, i)); + if (use.Get_kinetics_ptr() != NULL) + { + use.Set_n_kinetics_user(i); + use.Set_kinetics_in(true); + save.kinetics = TRUE; + save.n_kinetics_user = i; + save.n_kinetics_user_end = i; + } + } + /* + if (use.irrev_ptr != NULL && use.Get_kinetics_ptr() != NULL) + { + warning_msg("Should not use REACTION in same simulation with KINETICS."); + } + */ + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +store_get_equi_reactants(int l, int kin_end) +/* ---------------------------------------------------------------------- */ +{ + int i, k; + + if (use.Get_pp_assemblage_in() == TRUE) + { + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, l)); + } + else + use.Set_pp_assemblage_ptr(NULL); + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + if (use.Get_gas_phase_in() == TRUE) + { + use.Set_gas_phase_ptr(Utilities::Rxn_find(Rxn_gas_phase_map, l)); + } + else + use.Set_gas_phase_ptr(NULL); + if (use.Get_ss_assemblage_in() == TRUE) + { + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, l)); + } + else + use.Set_ss_assemblage_ptr(NULL); + + if (kin_end == FALSE) + { + count_pp = count_ss = count_pg = 0; + if (use.Get_pp_assemblage_ptr() != NULL) + count_pp = (int) pp_assemblage_ptr->Get_pp_assemblage_comps().size(); + if (use.Get_gas_phase_ptr() != NULL) + { + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + count_pg = (int) gas_phase_ptr->Get_gas_comps().size(); + } + if (use.Get_ss_assemblage_ptr() != NULL) + { + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t i = 0; i < ss_ptrs.size(); i++) + { + cxxSS *ss_ptr = ss_ptrs[i]; + count_ss += (int) ss_ptr->Get_ss_comps().size(); + } + } + k = count_pp + count_ss + count_pg; + + if (k == 0) + return (OK); + x0_moles = (LDBLE *) free_check_null(x0_moles); + x0_moles = (LDBLE *) PHRQ_malloc((size_t) k * sizeof(LDBLE)); + if (x0_moles == NULL) malloc_error(); + for (i = 0; i < k; i++) + { + x0_moles[i] = 0.0; + } + k = -1; + if (pp_assemblage_ptr) + { + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + x0_moles[++k] = it->second.Get_moles(); + } + } + + { + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr) + { + for (size_t l = 0; l < gas_phase_ptr->Get_gas_comps().size(); l++) + { + x0_moles[++k] += gas_phase_ptr->Get_gas_comps()[l].Get_moles(); + } + } + } + if (count_ss != 0) + { + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t i = 0; i < ss_ptrs.size(); i++) + { + cxxSS *ss_ptr = ss_ptrs[i]; + for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[j]); + x0_moles[++k] = comp_ptr->Get_moles(); + } +/*!!!! also miscibility gap comps ?? + */ + } + } + } + else + { + k = -1; + if (pp_assemblage_ptr && count_pp > 0) + { + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + it->second.Set_moles(x0_moles[++k]); + it->second.Set_delta(0.0); + } + } + + { + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr && count_pg) + { + std::vector temp_comps(gas_phase_ptr->Get_gas_comps()); + for (size_t l = 0; l < temp_comps.size(); l++) + { + temp_comps[l].Set_moles(x0_moles[++k]); + } + gas_phase_ptr->Set_gas_comps(temp_comps); + } + } + if (count_ss != 0) + { + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t i = 0; i < ss_ptrs.size(); i++) + { + cxxSS *ss_ptr = ss_ptrs[i]; + for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[j]); + comp_ptr->Set_initial_moles(x0_moles[++k]); + } +/*!!!! also miscibility gap comps ?? + */ + } + } +/* + * This condition makes output equal for incremental_reactions TRUE/FALSE... + * if (incremental_reactions == FALSE || reaction_step == count_total_steps) + */ + x0_moles = (LDBLE *) free_check_null(x0_moles); + } + return (OK); +} +void Phreeqc:: +f(integertype N, realtype t, N_Vector y, N_Vector ydot, + void *f_data) +{ + int n_user; + //LDBLE step_fraction; + cxxKinetics *kinetics_ptr; + Phreeqc *pThis = (Phreeqc *) f_data; + + pThis->cvode_error = FALSE; + n_user = pThis->cvode_n_user; + kinetics_ptr = (cxxKinetics *) pThis->cvode_kinetics_ptr; + //step_fraction = pThis->cvode_step_fraction; + pThis->rate_sim_time = pThis->cvode_rate_sim_time; + + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + /* + kinetics_ptr->comps[i].moles = y[i + 1]; + kinetics_ptr->comps[i].m = m_original[i] - y[i + 1]; + */ + kinetics_comp_ptr->Set_moles(Ith(y, i + 1)); + kinetics_comp_ptr->Set_m(pThis->m_original[i] - Ith(y, i + 1)); + if (kinetics_comp_ptr->Get_m() < 0) + { + /* + NOTE: y is not correct if it is greater than m_original + However, it seems to work to let y wander off, but use + .moles as the correct integral. + It does not work to reset Y to m_original, presumably + because the rational extrapolation gets screwed up. + */ + + /* + Ith(y,i + 1) = m_original[i]; + */ + //if (kinetics_ptr->Get_use_cvode()) + //{ + // pThis->cvode_error = TRUE; + // return; + //} + kinetics_comp_ptr->Set_moles(pThis->m_original[i]); + kinetics_comp_ptr->Set_m(0.0); + } + } + pThis->calc_final_kinetic_reaction(kinetics_ptr); + /* if (set_and_run(n_user, FALSE, TRUE, n_user, step_fraction) == MASS_BALANCE) { */ + if (pThis->use.Get_pp_assemblage_ptr() != NULL) + { + pThis->Rxn_pp_assemblage_map[pThis->cvode_pp_assemblage_save->Get_n_user()] = *pThis->cvode_pp_assemblage_save; + pThis->use.Set_pp_assemblage_ptr(Utilities::Rxn_find(pThis->Rxn_pp_assemblage_map, pThis->cvode_pp_assemblage_save->Get_n_user())); + } + if (pThis->use.Get_ss_assemblage_ptr() != NULL) + { + pThis->Rxn_ss_assemblage_map[pThis->cvode_ss_assemblage_save->Get_n_user()] = *pThis->cvode_ss_assemblage_save; + pThis->use.Set_ss_assemblage_ptr(Utilities::Rxn_find(pThis->Rxn_ss_assemblage_map, pThis->cvode_ss_assemblage_save->Get_n_user())); + } + + if (pThis->set_and_run_wrapper(n_user, FALSE, TRUE, n_user, 0.0) == MASS_BALANCE) + { + pThis->run_reactions_iterations += pThis->iterations; + pThis->cvode_error = TRUE; + /* + error_msg("Mass balance error in f", CONTINUE); + */ + return; + } + if (pThis->cvode_test == TRUE) + { + return; + } + pThis->run_reactions_iterations += pThis->iterations; + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + kinetics_comp_ptr->Set_moles(0.0); + } + pThis->calc_kinetic_reaction(kinetics_ptr, 1.0); + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + /* + dydx[i + 1] = kinetics_ptr->comps[i].moles; + */ + Ith(ydot, i + 1) = kinetics_comp_ptr->Get_moles(); + } + return; +} + +/* +static void Jac(integertype N, DenseMat J, RhsFn f, void *f_data, realtype t, + N_Vector y, N_Vector fy, N_Vector ewt, realtype h, + realtype uround, void *jac_data, long int *nfePtr, + N_Vector vtemp1, N_Vector vtemp2, N_Vector vtemp3); +*/ +void Phreeqc:: +Jac(integertype N, DenseMat J, RhsFn f, void *f_data, + realtype t, N_Vector y, N_Vector fy, N_Vector ewt, + realtype h, realtype uround, void *jac_data, + long int *nfePtr, N_Vector vtemp1, N_Vector vtemp2, + N_Vector vtemp3) +{ + int count_cvode_errors; + int n_reactions, n_user; + LDBLE *initial_rates, del; + cxxKinetics *kinetics_ptr; + LDBLE step_fraction; + + Phreeqc *pThis = (Phreeqc *) f_data; + + pThis->cvode_error = FALSE; + n_reactions = pThis->cvode_n_reactions; + n_user = pThis->cvode_n_user; + kinetics_ptr = (cxxKinetics *) pThis->cvode_kinetics_ptr; + step_fraction = pThis->cvode_step_fraction; + pThis->rate_sim_time = pThis->cvode_rate_sim_time; + + initial_rates = + (LDBLE *) pThis->PHRQ_malloc ((size_t) n_reactions * sizeof(LDBLE)); + if (initial_rates == NULL) + pThis->malloc_error(); + + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + /* + kinetics_ptr->comps[i].moles = y[i + 1]; + kinetics_ptr->comps[i].m = m_original[i] - y[i + 1]; + */ + kinetics_comp_ptr->Set_moles(Ith(y, i + 1)); + kinetics_comp_ptr->Set_m(pThis->m_original[i] - Ith(y, i + 1)); + if (kinetics_comp_ptr->Get_m() < 0) + { + /* + NOTE: y is not correct if it is greater than m_original + However, it seems to work to let y wander off, but use + .moles as the correct integral. + It does not work to reset Y to m_original, presumably + because the rational extrapolation gets screwed up. + */ + + /* + Ith(y,i + 1) = m_original[i]; + */ + kinetics_comp_ptr->Set_moles(pThis->m_original[i]); + kinetics_comp_ptr->Set_m(0.0); + } + } + pThis->calc_final_kinetic_reaction(kinetics_ptr); + /* if (set_and_run(n_user, FALSE, TRUE, n_user, step_fraction) == MASS_BALANCE) { */ + if (pThis->use.Get_pp_assemblage_ptr() != NULL) + { + pThis->Rxn_pp_assemblage_map[pThis->cvode_pp_assemblage_save->Get_n_user()] = *pThis->cvode_pp_assemblage_save; + pThis->use.Set_pp_assemblage_ptr(Utilities::Rxn_find(pThis->Rxn_pp_assemblage_map, pThis->cvode_pp_assemblage_save->Get_n_user())); + } + if (pThis->set_and_run_wrapper(n_user, FALSE, TRUE, n_user, 0.0) == MASS_BALANCE) + { + pThis->run_reactions_iterations += pThis->iterations; + pThis->cvode_error = TRUE; + /* + error_msg("Mass balance error in jacobian", CONTINUE); + */ + initial_rates = (LDBLE *) pThis->free_check_null(initial_rates); + return; + } + pThis->run_reactions_iterations += pThis->iterations; + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + kinetics_comp_ptr->Set_moles(0.0); + } + pThis->calc_kinetic_reaction(kinetics_ptr, 1.0); + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + initial_rates[i] = kinetics_comp_ptr->Get_moles(); + } + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_i_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + /* calculate reaction up to current time */ + del = 1e-12; + pThis->cvode_error = TRUE; + count_cvode_errors = 0; + while (pThis->cvode_error == TRUE) + { + del /= 10.; + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_j_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + /* + kinetics_ptr->comps[j].moles = y[j + 1]; + kinetics_ptr->comps[j].m = m_original[j] - y[j + 1]; + */ + kinetics_comp_j_ptr->Set_moles(Ith(y, j + 1)); + kinetics_comp_j_ptr->Set_m(pThis->m_original[j] - Ith(y, j + 1)); + if (kinetics_comp_i_ptr->Get_m() < 0) + { + /* + NOTE: y is not correct if it is greater than m_original + However, it seems to work to let y wander off, but use + .moles as the correct integral. + It does not work to reset Y to m_original, presumably + because the rational extrapolation gets screwed up. + */ + + /* + Ith(y,i + 1) = m_original[i]; + */ + kinetics_comp_i_ptr->Set_moles(pThis->m_original[i]); + kinetics_comp_i_ptr->Set_m(0.0); + } + } + + /* Add small amount of ith reaction */ + kinetics_comp_i_ptr->Set_m(kinetics_comp_i_ptr->Get_m() - del); + if (kinetics_comp_i_ptr->Get_m() < 0) + { + kinetics_comp_i_ptr->Set_m(0); + } + kinetics_comp_i_ptr->Set_moles(kinetics_comp_i_ptr->Get_moles() + del); + pThis->calc_final_kinetic_reaction(kinetics_ptr); + if (pThis->use.Get_pp_assemblage_ptr() != NULL) + { + pThis->Rxn_pp_assemblage_map[pThis->cvode_pp_assemblage_save->Get_n_user()] = *pThis->cvode_pp_assemblage_save; + pThis->use.Set_pp_assemblage_ptr(Utilities::Rxn_find(pThis->Rxn_pp_assemblage_map, pThis->cvode_pp_assemblage_save->Get_n_user())); + } + if (pThis->set_and_run_wrapper + (n_user, FALSE, TRUE, n_user, step_fraction) == MASS_BALANCE) + { + count_cvode_errors++; + pThis->cvode_error = TRUE; + if (count_cvode_errors > 30) + { + initial_rates = (LDBLE *) pThis->free_check_null(initial_rates); + return; + } + pThis->run_reactions_iterations += pThis->iterations; + continue; + } + pThis->cvode_error = FALSE; + pThis->run_reactions_iterations += pThis->iterations; + /*kinetics_ptr->comps[i].moles -= del; */ + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_moles(0.0); + } + pThis->calc_kinetic_reaction(kinetics_ptr, 1.0); + + /* calculate new rates for df/dy[i] */ + /* dfdx[i + 1] = 0.0; */ + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + IJth(J, j + 1, i + 1) = + (kinetics_comp_ptr->Get_moles() - initial_rates[j]) / del; + } + } + } + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + kinetics_comp_ptr->Set_moles(0); + } + initial_rates = (LDBLE *) pThis->free_check_null(initial_rates); + return; +} + +void Phreeqc:: +cvode_init(void) +{ + cvode_kinetics_ptr = NULL; + cvode_test = 0; + cvode_error = 0; + cvode_n_user = -99; + cvode_n_reactions = -99; + cvode_step_fraction = 0.0; + cvode_rate_sim_time = 0.0; + cvode_rate_sim_time_start = 0.0; + cvode_last_good_time = 0.0; + cvode_prev_good_time = 0.0; + cvode_last_good_y = NULL; + cvode_prev_good_y = NULL; + kinetics_machEnv = NULL; + kinetics_y = kinetics_abstol = NULL; + kinetics_cvode_mem = NULL; + cvode_pp_assemblage_save = NULL; + cvode_ss_assemblage_save = NULL; + return; +} +bool Phreeqc:: +cvode_update_reactants(int i, int nsaver, bool save_it) +{ + cxxKinetics *kinetics_ptr = use.Get_kinetics_ptr(); + int n_reactions = (int) kinetics_ptr->Get_kinetics_comps().size(); + + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + + // Adds reaction defined by last_good_y + kinetics_comp_ptr->Set_moles(Ith(cvode_last_good_y, j + 1)); + // Reduces m + kinetics_comp_ptr->Set_m(m_original[j] - kinetics_comp_ptr->Get_moles()); + // Don't update until after calc_final_reaction + //m_original[j] = kinetics_comp_ptr->Get_m(); + //m_temp[j] = kinetics_comp_ptr->Get_m(); + if (kinetics_comp_ptr->Get_m() < 0) + { + kinetics_comp_ptr->Set_moles(m_original[j]); + kinetics_comp_ptr->Set_m(0.0); + } + } + // calculates net reaction + calc_final_kinetic_reaction(kinetics_ptr); + if (use.Get_pp_assemblage_ptr() != NULL) + { + Rxn_pp_assemblage_map[cvode_pp_assemblage_save->Get_n_user()] = *cvode_pp_assemblage_save; + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, cvode_pp_assemblage_save->Get_n_user())); + } + if (use.Get_ss_assemblage_ptr() != NULL) + { + Rxn_ss_assemblage_map[cvode_ss_assemblage_save->Get_n_user()] = *cvode_ss_assemblage_save; + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, cvode_ss_assemblage_save->Get_n_user())); + } + // runs previous solution plus net reaction + if (set_and_run_wrapper(i, NOMIX, TRUE, nsaver, 1.0) == MASS_BALANCE) + { + error_msg("CVODE step was bad", STOP); + return false; + } + + // saves result to reactants defined by saver + if (save_it) + { + saver(); + + cxxPPassemblage *pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, nsaver); + cxxSSassemblage *ss_assemblage_ptr = Utilities::Rxn_find(Rxn_ss_assemblage_map, nsaver); + if (cvode_pp_assemblage_save != NULL) + { + delete cvode_pp_assemblage_save; + cvode_pp_assemblage_save = new cxxPPassemblage(*pp_assemblage_ptr); + } + if (cvode_ss_assemblage_save != NULL) + { + delete cvode_ss_assemblage_save; + cvode_ss_assemblage_save = new cxxSSassemblage(*ss_assemblage_ptr); + } + + for (int j = 0; j < n_reactions; j++) + { + Ith(cvode_last_good_y, j + 1) = 0.0; + Ith(cvode_prev_good_y, j + 1) = 0.0; + + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + m_original[j] = kinetics_comp_ptr->Get_m(); + m_temp[j] = kinetics_comp_ptr->Get_m(); + } + } + return true; +} +/* ---------------------------------------------------------------------- */ +bool Phreeqc:: +limit_rates(cxxKinetics *kinetics_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through kinetic components to + * determine rates and + * a list of elements and amounts in + * the reaction. + */ + + // check if any small concentrations with negative rates + if (!use_kinetics_limiter) + { + return false; + } + std::vector negative_rate; + cxxNameDouble::iterator it = kinetics_ptr->Get_totals().begin(); + for ( ; it != kinetics_ptr->Get_totals().end(); it++) + { + if (total(it->first.c_str()) < 1e-10 && it->second < -1e-20) + { + //if (total(it->first.c_str()) > fabs(it->second)) + // continue; + negative_rate.push_back(it->first); + } + } + if (negative_rate.size() == 0) return false; + + for (size_t j = 0; j < negative_rate.size(); j++) + { + std::string elt = negative_rate[j]; + LDBLE positive_rates = 0; + LDBLE negative_rates = 0; + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + cxxNameDouble::iterator it = kinetics_comp_ptr->Get_moles_of_reaction().find(elt); + if (it != kinetics_comp_ptr->Get_moles_of_reaction().end()) + { + if (it->second >= 0.0) + { + positive_rates += it->second; + } + else + { + negative_rates += it->second; + } + } + } + + // factor to reduce precipitation to equal dissolution + LDBLE limiter_fraction = 1.0; + if (negative_rates < 0.0) + { + limiter_fraction = fabs(positive_rates / negative_rates); + //limiter_fraction = fabs((0.9*total(elt.c_str()) + positive_rates) / negative_rates); + } + else + { + assert(false); + } + + // Now limit precipitation + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + cxxNameDouble::iterator it = kinetics_comp_ptr->Get_moles_of_reaction().find(elt); + if (it != kinetics_comp_ptr->Get_moles_of_reaction().end()) + { + if (it->second < 0.0) + { + kinetics_comp_ptr->Set_moles(kinetics_comp_ptr->Get_moles() * limiter_fraction); + } + } + } + } + + return true; +} diff --git a/phreeqcpp/mainsubs.cpp b/phreeqcpp/mainsubs.cpp new file mode 100644 index 00000000..ee53a2cd --- /dev/null +++ b/phreeqcpp/mainsubs.cpp @@ -0,0 +1,2643 @@ +#include +#include +#include "Utils.h" +#include "Phreeqc.h" +#include "phqalloc.h" +#include "PBasic.h" +#include "Temperature.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "Reaction.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "cxxKinetics.h" +#include "Solution.h" + +#if defined(WINDOWS) || defined(_WINDOWS) +#include +#endif + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +initialize(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Initialize global variables + */ + struct logk *logk_ptr; + char token[MAX_LENGTH]; + + moles_per_kilogram_string = string_duplicate("Mol/kgw"); + pe_string = string_duplicate("pe"); +/* + * Initialize advection + */ + advection_punch = (int *) PHRQ_malloc(sizeof(int)); + if (advection_punch == NULL) + malloc_error(); + advection_punch[0] = TRUE; + advection_print = (int *) PHRQ_malloc(sizeof(int)); + if (advection_print == NULL) + malloc_error(); + advection_print[0] = TRUE; +/* + * Allocate space + */ + cell_data_max_cells = count_cells + 2; + space((void **) ((void *) &cell_data), INIT, &cell_data_max_cells, + sizeof(struct cell_data)); + for (int i = 0; i < cell_data_max_cells; i++) + { + cell_data[i].length = 1.0; + cell_data[i].mid_cell_x = 1.0; + cell_data[i].disp = 1.0; + cell_data[i].temp = 25.0; + cell_data[i].por = 0.1; + cell_data[i].por_il = 0.01; + cell_data[i].potV = 0; + cell_data[i].punch = FALSE; + cell_data[i].print = FALSE; + } + + space((void **) ((void *) &elements), INIT, &max_elements, + sizeof(struct element *)); + + space((void **) ((void *) &elt_list), INIT, &max_elts, + sizeof(struct elt_list)); + + inverse = (struct inverse *) PHRQ_malloc((size_t) sizeof(struct inverse)); + if (inverse == NULL) malloc_error(); + count_inverse = 0; + space((void **) ((void *) &line), INIT, &max_line, sizeof(char)); + + space((void **) ((void *) &line_save), INIT, &max_line, sizeof(char)); + + space((void **) ((void *) &master), INIT, &max_master, + sizeof(struct master *)); + + space((void **) ((void *) &mb_unknowns), INIT, &max_mb_unknowns, + sizeof(struct unknown_list)); + + // one stag_data + stag_data = (struct stag_data *) PHRQ_calloc(1, sizeof(struct stag_data)); + if (stag_data == NULL) + malloc_error(); + stag_data->count_stag = 0; + stag_data->exch_f = 0; + stag_data->th_m = 0; + stag_data->th_im = 0; + + space((void **) ((void *) &phases), INIT, &max_phases, + sizeof(struct phase *)); + + space((void **) ((void *) &trxn.token), INIT, &max_trxn, + sizeof(struct rxn_token_temp)); + + space((void **) ((void *) &s), INIT, &max_s, sizeof(struct species *)); + + space((void **) ((void *) &logk), INIT, &max_logk, sizeof(struct logk *)); + + space((void **) ((void *) &master_isotope), INIT, &max_master_isotope, + sizeof(struct master_isotope *)); + +/* + * Create hash tables + */ + hcreate_multi((unsigned) max_logk, &logk_hash_table); + hcreate_multi((unsigned) max_master_isotope, &master_isotope_hash_table); + hcreate_multi((unsigned) max_elements, &elements_hash_table); + hcreate_multi((unsigned) max_s, &species_hash_table); + hcreate_multi((unsigned) max_phases, &phases_hash_table); +#ifdef SKIP_OLD_SELECTED_OUTPUT +/* + * Initialize punch + */ + punch.in = FALSE; + punch.new_def = FALSE; + + // one punch.totals + punch.count_totals = 0; + punch.totals = + (struct name_master *) PHRQ_malloc(sizeof(struct name_master)); + if (punch.totals == NULL) + malloc_error(); + + // one punch.molalities + punch.count_molalities = 0; + punch.molalities = + (struct name_species *) PHRQ_malloc(sizeof(struct name_species)); + if (punch.molalities == NULL) + malloc_error(); + + // one punch.activities + punch.count_activities = 0; + punch.activities = + (struct name_species *) PHRQ_malloc(sizeof(struct name_species)); + if (punch.activities == NULL) + malloc_error(); + + // one punch.pure_phases + punch.count_pure_phases = 0; + punch.pure_phases = + (struct name_phase *) PHRQ_malloc(sizeof(struct name_phase)); + if (punch.pure_phases == NULL) + malloc_error(); + + // one punch.si + punch.count_si = 0; + punch.si = (struct name_phase *) PHRQ_malloc(sizeof(struct name_phase)); + if (punch.si == NULL) + malloc_error(); + + // one punch.gases + punch.count_gases = 0; + punch.gases = + (struct name_phase *) PHRQ_malloc(sizeof(struct name_phase)); + if (punch.gases == NULL) + malloc_error(); + + // one punch.s_s + punch.count_s_s = 0; + punch.s_s = (struct name_phase *) PHRQ_malloc(sizeof(struct name_phase)); + if (punch.s_s == NULL) + malloc_error(); + + // one punch.kinetics + punch.count_kinetics = 0; + punch.kinetics = + (struct name_phase *) PHRQ_malloc(sizeof(struct name_phase)); + if (punch.kinetics == NULL) + malloc_error(); + + // one punch.isotopes + punch.count_isotopes = 0; + punch.isotopes = + (struct name_master *) PHRQ_malloc(sizeof(struct name_master)); + if (punch.isotopes == NULL) + malloc_error(); + + // one punch.calculate_values + punch.count_calculate_values = 0; + punch.calculate_values = + (struct name_master *) PHRQ_malloc(sizeof(struct name_master)); + if (punch.calculate_values == NULL) + malloc_error(); +#endif + // one save_values + save_values = + (struct save_values *) PHRQ_malloc(sizeof(struct save_values)); + if (save_values == NULL) + malloc_error(); + + // one rate + rates = (struct rate *) PHRQ_malloc(sizeof(struct rate)); + if (rates == NULL) + malloc_error(); + + // user_print + user_print = (struct rate *) PHRQ_malloc((size_t) sizeof(struct rate)); + if (user_print == NULL) + malloc_error(); + user_print->commands = NULL; + user_print->linebase = NULL; + user_print->varbase = NULL; + user_print->loopbase = NULL; + +#ifdef SKIP + // user_punch + user_punch = (struct rate *) PHRQ_malloc((size_t) sizeof(struct rate)); + if (user_punch == NULL) + malloc_error(); + user_punch->commands = NULL; + user_punch->linebase = NULL; + user_punch->varbase = NULL; + user_punch->loopbase = NULL; + user_punch_headings = (const char **) PHRQ_malloc(sizeof(char *)); + if (user_punch_headings == NULL) + malloc_error(); + user_punch_count_headings = 0; +#endif +#if defined PHREEQ98 +/* + * user_graph + */ + user_graph = (struct rate *) PHRQ_malloc((size_t) sizeof(struct rate)); + if (user_graph == NULL) + malloc_error(); + user_graph->commands = NULL; + user_graph->linebase = NULL; + user_graph->varbase = NULL; + user_graph->loopbase = NULL; + user_graph_headings = (char **) PHRQ_malloc(sizeof(char *)); + if (user_graph_headings == NULL) + malloc_error(); + user_graph_count_headings = 0; +#endif + /* + Initialize llnl aqueous model parameters + */ + llnl_temp = (LDBLE *) PHRQ_malloc(sizeof(LDBLE)); + if (llnl_temp == NULL) + malloc_error(); + llnl_count_temp = 0; + llnl_adh = (LDBLE *) PHRQ_malloc(sizeof(LDBLE)); + if (llnl_adh == NULL) + malloc_error(); + llnl_count_adh = 0; + llnl_bdh = (LDBLE *) PHRQ_malloc(sizeof(LDBLE)); + if (llnl_bdh == NULL) + malloc_error(); + llnl_count_bdh = 0; + llnl_bdot = (LDBLE *) PHRQ_malloc(sizeof(LDBLE)); + if (llnl_bdot == NULL) + malloc_error(); + llnl_count_bdot = 0; + llnl_co2_coefs = (LDBLE *) PHRQ_malloc(sizeof(LDBLE)); + if (llnl_co2_coefs == NULL) + malloc_error(); + llnl_count_co2_coefs = 0; + // new PBasic + basic_interpreter = new PBasic(this, phrq_io); + // allocate one change_surf + change_surf = + (struct Change_Surf *) + PHRQ_malloc((size_t) (2 * sizeof(struct Change_Surf))); + if (change_surf == NULL) + malloc_error(); + change_surf[0].cell_no = -99; + change_surf[0].next = TRUE; + change_surf[1].cell_no = -99; + change_surf[1].next = FALSE; + +#ifdef PHREEQCI_GUI + g_spread_sheet.heading = NULL; + g_spread_sheet.units = NULL; + g_spread_sheet.count_rows = 0; + g_spread_sheet.rows = NULL; + g_spread_sheet.defaults.units = NULL; + g_spread_sheet.defaults.count_iso = 0; + g_spread_sheet.defaults.iso = NULL; + g_spread_sheet.defaults.redox = NULL; +#endif + + /* calculate_value */ + max_calculate_value = MAX_ELTS; + count_calculate_value = 0; + space((void **) ((void *) &calculate_value), INIT, &max_calculate_value, + sizeof(struct calculate_value *)); + hcreate_multi((unsigned) max_calculate_value, + &calculate_value_hash_table); + + /* isotope_ratio */ + max_isotope_ratio = MAX_ELTS; + count_isotope_ratio = 0; + space((void **) ((void *) &isotope_ratio), INIT, &max_isotope_ratio, + sizeof(struct isotope_ratio *)); + hcreate_multi((unsigned) max_isotope_ratio, &isotope_ratio_hash_table); + + /* isotope_value */ + max_isotope_alpha = MAX_ELTS; + count_isotope_alpha = 0; + space((void **) ((void *) &isotope_alpha), INIT, &max_isotope_alpha, + sizeof(struct isotope_alpha *)); + hcreate_multi((unsigned) max_isotope_alpha, &isotope_alpha_hash_table); + + /* + * define constant named log_k + */ + strcpy(token, "XconstantX"); + logk_ptr = logk_store(token, TRUE); + strcpy(token, "1.0"); + read_log_k_only(token, &logk_ptr->log_k[0]); + + // allocate space for copier + copier_init(©_solution); + copier_init(©_pp_assemblage); + copier_init(©_exchange); + copier_init(©_surface); + copier_init(©_ss_assemblage); + copier_init(©_gas_phase); + copier_init(©_kinetics); + copier_init(©_mix); + copier_init(©_reaction); + copier_init(©_temperature); + copier_init(©_pressure); + + // Initialize cvode + cvode_init(); + + // Allocate space for pitzer + pitzer_init(); + + // Allocate space for sit + sit_init(); + + // Allocate zeros + zeros = (LDBLE *) PHRQ_malloc(sizeof(LDBLE)); + if (zeros == NULL) + malloc_error(); + zeros[0] = 0.0; + zeros_max = 1; + use_kinetics_limiter = false; + + return; +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_use(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Structure "use" has list of solution, ex, surf, pp_assemblage, + * gas_phase and solid solution to use in current calculations, + * also mix, irrev, and temp. + * This routine searches for the user numbers in each list + * (solution, ex, ...) and sets a pointer in structure use + */ + +/* + * Initial solution case + */ + use.Set_pp_assemblage_ptr(NULL); + use.Set_mix_ptr(NULL); + use.Set_reaction_ptr(NULL); + use.Set_exchange_ptr(NULL); + use.Set_kinetics_ptr(NULL); + use.Set_surface_ptr(NULL); + use.Set_temperature_ptr(NULL); + use.Set_pressure_ptr(NULL); + use.Set_gas_phase_ptr(NULL); + use.Set_ss_assemblage_ptr(NULL); + + if (state < REACTION) + { + return (OK); + } +/* + * Reaction case + */ + if (use.Get_pp_assemblage_in() == FALSE && + use.Get_reaction_in() == FALSE && + use.Get_mix_in() == FALSE && + use.Get_exchange_in() == FALSE && + use.Get_kinetics_in() == FALSE && + use.Get_surface_in() == FALSE && + use.Get_temperature_in() == FALSE && + use.Get_pressure_in() == FALSE && + use.Get_gas_phase_in() == FALSE && use.Get_ss_assemblage_in() == FALSE) + { + return (FALSE); + } + if (use.Get_solution_in() == FALSE && use.Get_mix_in() == FALSE) + return (FALSE); +/* + * Find solution + */ + if (use.Get_solution_in()) + { + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, use.Get_n_solution_user())); + if (use.Get_solution_ptr() == NULL) + { + error_string = sformatf( "Solution %d not found.", + use.Get_n_solution_user()); + error_msg(error_string, STOP); + } + } +/* + * Find mixture + */ + if (use.Get_mix_in() == TRUE) + { + use.Set_mix_ptr(Utilities::Rxn_find(Rxn_mix_map, use.Get_n_mix_user())); + use.Set_n_mix_user_orig(use.Get_n_mix_user()); + if (use.Get_mix_ptr() == NULL) + { + error_string = sformatf( "Mix %d not found.", + use.Get_n_mix_user()); + error_msg(error_string, STOP); + } + } + else + { + use.Set_mix_ptr(NULL); + } +/* + * Find pure phase assemblage + */ + if (use.Get_pp_assemblage_in() == TRUE) + { + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, use.Get_n_pp_assemblage_user())); + if (use.Get_pp_assemblage_ptr() == NULL) + { + error_string = sformatf( "Pure phase assemblage %d not found.", + use.Get_n_pp_assemblage_user()); + error_msg(error_string, STOP); + } + } + else + { + use.Set_pp_assemblage_ptr(NULL); + } +/* + * Find irrev reaction + */ + if (use.Get_reaction_in() == TRUE) + { + use.Set_reaction_ptr(Utilities::Rxn_find(Rxn_reaction_map, use.Get_n_reaction_user())); + if (use.Get_reaction_ptr() == NULL) + { + error_string = sformatf( "Reaction %d not found.", + use.Get_n_reaction_user()); + error_msg(error_string, STOP); + } + } + else + { + use.Set_reaction_ptr(NULL); + } +/* + * Find exchange + */ + if (use.Get_exchange_in() == TRUE) + { + use.Set_exchange_ptr(Utilities::Rxn_find(Rxn_exchange_map, use.Get_n_exchange_user())); + if (use.Get_exchange_ptr() == NULL) + { + error_string = sformatf( "Exchange %d not found.", + use.Get_n_exchange_user()); + error_msg(error_string, STOP); + } + } + else + { + use.Set_exchange_ptr(NULL); + } +/* + * Find kinetics + */ + if (use.Get_kinetics_in() == TRUE) + { + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_kinetics_user())); + if (use.Get_kinetics_ptr() == NULL) + { + error_string = sformatf( "Kinetics %d not found.", + use.Get_n_kinetics_user()); + error_msg(error_string, STOP); + } + } + else + { + use.Set_kinetics_ptr(NULL); + } +/* + * Find surface + */ + dl_type_x = cxxSurface::NO_DL; + if (use.Get_surface_in() == TRUE) + { + use.Set_surface_ptr(Utilities::Rxn_find(Rxn_surface_map, use.Get_n_surface_user())); + if (use.Get_surface_ptr() == NULL) + { + error_string = sformatf( "Surface %d not found.", + use.Get_n_surface_user()); + error_msg(error_string, STOP); + } + } + else + { + use.Set_surface_ptr(NULL); + } +/* + * Find temperature + */ + if (use.Get_temperature_in() == TRUE) + { + use.Set_temperature_ptr(Utilities::Rxn_find(Rxn_temperature_map, use.Get_n_temperature_user())); + if (use.Get_temperature_ptr() == NULL) + { + error_string = sformatf( "Temperature %d not found.", + use.Get_n_temperature_user()); + error_msg(error_string, STOP); + } + } + else + { + use.Set_temperature_ptr(NULL); + } +/* + * Find pressure + */ + if (use.Get_pressure_in() == TRUE) + { + use.Set_pressure_ptr(Utilities::Rxn_find(Rxn_pressure_map, use.Get_n_pressure_user())); + if (use.Get_pressure_ptr() == NULL) + { + error_string = sformatf( "Pressure %d not found.", use.Get_n_pressure_user()); + error_msg(error_string, STOP); + } + } + else + { + use.Set_pressure_ptr(NULL); + } +/* + * Find gas + */ + if (use.Get_gas_phase_in() == TRUE) + { + use.Set_gas_phase_ptr(Utilities::Rxn_find(Rxn_gas_phase_map, use.Get_n_gas_phase_user())); + if (use.Get_gas_phase_ptr() == NULL) + { + error_string = sformatf( "Gas_phase %d not found.", + use.Get_n_gas_phase_user()); + error_msg(error_string, STOP); + } + } + else + { + use.Set_gas_phase_ptr(NULL); + } +/* + * Find ss_assemblage + */ + if (use.Get_ss_assemblage_in() == TRUE) + { + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, use.Get_n_ss_assemblage_user())); + if (use.Get_ss_assemblage_ptr() == NULL) + { + error_string = sformatf( "ss_assemblage %d not found.", + use.Get_n_ss_assemblage_user()); + error_msg(error_string, STOP); + } + } + else + { + use.Set_ss_assemblage_ptr(NULL); + } + /* + if (use.irrev_ptr != NULL && use.Get_kinetics_ptr() != NULL) + { + warning_msg("Should not use REACTION in same simulation with KINETICS."); + } + */ + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +initial_solutions(int print) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through list of solutions, make initial solution calculations + * for any marked "new". + */ + int converge, converge1; + int last, n_user, print1; + char token[2 * MAX_LENGTH]; + + state = INITIAL_SOLUTION; + set_use(); + print1 = TRUE; + dl_type_x = cxxSurface::NO_DL; + //std::map::iterator it = Rxn_solution_map.begin(); + //for ( ; it != Rxn_solution_map.end(); it++) + //{ + //for (size_t nn = 0; nn < Rxn_new_solution.size(); nn++) + for (std::set::const_iterator nit = Rxn_new_solution.begin(); nit != Rxn_new_solution.end(); nit++) + { + std::map::iterator it = Rxn_solution_map.find(*nit); + if (it == Rxn_solution_map.end()) + { + assert(false); + } + cxxSolution &solution_ref = it->second; + initial_solution_isotopes = FALSE; + if (solution_ref.Get_new_def()) + { + if (print1 == TRUE && print == TRUE) + { + dup_print("Beginning of initial solution calculations.", + TRUE); + print1 = FALSE; + } + if (print == TRUE) + { + sprintf(token, "Initial solution %d.\t%.350s", + solution_ref.Get_n_user(), solution_ref.Get_description().c_str()); + dup_print(token, FALSE); + } + use.Set_solution_ptr(&solution_ref); + LDBLE d0 = solution_ref.Get_density(); + //LDBLE d1 = 0; + bool diag = (diagonal_scale == TRUE) ? true : false; + int count_iterations = 0; + std::string input_units = solution_ref.Get_initial_data()->Get_units(); + cxxISolution *initial_data_ptr = solution_ref.Get_initial_data(); + for (;;) + { + prep(); + k_temp(solution_ref.Get_tc(), solution_ref.Get_patm()); + set(TRUE); + always_full_pitzer = FALSE; + + diagonal_scale = TRUE; + converge = model(); + if (converge == ERROR /*&& diagonal_scale == FALSE*/) + { + diagonal_scale = TRUE; + always_full_pitzer = TRUE; + set(TRUE); + converge = model(); + } + if (solution_ref.Get_initial_data()->Get_calc_density()) + { + solution_ref.Set_density(calc_dens()); + if (!equal(d0, solution_ref.Get_density(), 1e-8)) + { + initial_data_ptr->Set_units(input_units); + d0 = solution_ref.Get_density(); + if (count_iterations++ < 20) + { + diag = (diagonal_scale == TRUE) ? true : false; + continue; + } + else + { + error_msg(sformatf("%s %d.", "Density calculation failed for initial solution ", solution_ref.Get_n_user()), + STOP); + } + } + } + break; + } + diagonal_scale = (diag) ? TRUE : FALSE; + converge1 = check_residuals(); + sum_species(); + viscosity(); + add_isotopes(solution_ref); + punch_all(); + print_all(); + /* free_model_allocs(); */ +// remove pr_in + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type == SOLUTION_PHASE_BOUNDARY) + x[i]->phase->pr_in = false; + } + + if (converge == ERROR || converge1 == ERROR) + { + error_msg(sformatf("%s %d.", "Model failed to converge for initial solution ", solution_ref.Get_n_user()), + STOP); + } + n_user = solution_ref.Get_n_user(); + last = solution_ref.Get_n_user_end(); + /* copy isotope data */ + if (solution_ref.Get_isotopes().size() > 0) + { + isotopes_x = solution_ref.Get_isotopes(); + } + else + { + isotopes_x.clear(); + } + xsolution_save(n_user); + Utilities::Rxn_copies(Rxn_solution_map, n_user, last); + } + } + initial_solution_isotopes = FALSE; + return (OK); +} +#ifdef SKIP +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +solution_mix() +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through list of solution_mix, mix, save solutions + */ + std::map::iterator it; + for (it = Rxn_solution_mix_map.begin(); it != Rxn_solution_mix_map.end(); it++) + { + cxxSolution sol(Rxn_solution_map, it->second, it->second.Get_n_user(), this->phrq_io); + Rxn_solution_map[it->second.Get_n_user()] = sol; + Utilities::Rxn_copies(Rxn_solution_map, it->second.Get_n_user(), it->second.Get_n_user_end()); + } + Rxn_solution_mix_map.clear(); + return OK; +} +#endif +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +initial_exchangers(int print) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through list of exchangers, make initial calculations + * for any marked "new" that are defined to be in equilibrium with a + * solution. + */ + int i, converge, converge1; + int last, n_user, print1; + char token[2 * MAX_LENGTH]; + + state = INITIAL_EXCHANGE; + set_use(); + print1 = TRUE; + dl_type_x = cxxSurface::NO_DL; + //std::map::iterator it = Rxn_exchange_map.begin(); + //for ( ; it != Rxn_exchange_map.end(); it++) + //{ + //for (size_t nn = 0; nn < Rxn_new_exchange.size(); nn++) + //{ + //std::map::iterator it = Rxn_exchange_map.find(Rxn_new_exchange[nn]); + for (std::set::const_iterator nit = Rxn_new_exchange.begin(); nit != Rxn_new_exchange.end(); nit++) + { + std::map::iterator it = Rxn_exchange_map.find(*nit); + if (it == Rxn_exchange_map.end()) + { + assert(false); + } + if (!it->second.Get_new_def()) + continue; + cxxExchange *exchange_ptr = &(it->second); + n_user = exchange_ptr->Get_n_user(); + last = exchange_ptr->Get_n_user_end(); + exchange_ptr->Set_n_user_end(n_user); + exchange_ptr->Set_new_def(false); + if (exchange_ptr->Get_solution_equilibria()) + { + if (print1 == TRUE && print == TRUE) + { + dup_print("Beginning of initial exchange" + "-composition calculations.", TRUE); + print1 = FALSE; + } + if (print == TRUE) + { + sprintf(token, "Exchange %d.\t%.350s", + exchange_ptr->Get_n_user(), exchange_ptr->Get_description().c_str()); + dup_print(token, FALSE); + } + use.Set_exchange_ptr(exchange_ptr); + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, exchange_ptr->Get_n_solution())); + if (use.Get_solution_ptr() == NULL) + { + error_msg + ("Solution not found for initial exchange calculation", + STOP); + } + + prep(); + k_temp(use.Get_solution_ptr()->Get_tc(), use.Get_solution_ptr()->Get_patm()); + set(TRUE); + converge = model(); + converge1 = check_residuals(); + sum_species(); + viscosity(); + species_list_sort(); + print_exchange(); + xexchange_save(n_user); + punch_all(); + /* free_model_allocs(); */ + if (converge == ERROR || converge1 == ERROR) + { + error_msg + ("Model failed to converge for initial exchange calculation.", + STOP); + } + } + for (i = n_user + 1; i <= last; i++) + { + Utilities::Rxn_copy(Rxn_exchange_map, n_user, i); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +initial_gas_phases(int print) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through list of gas_phases, make initial calculations + * for any marked "new" that are defined to be in equilibrium with a + * solution. + */ + int converge, converge1; + int last, n_user, print1; + char token[2 * MAX_LENGTH]; + struct phase *phase_ptr; + struct rxn_token *rxn_ptr; + LDBLE lp; + bool PR = false; + + state = INITIAL_GAS_PHASE; + set_use(); + print1 = TRUE; + dl_type_x = cxxSurface::NO_DL; + //std::map::iterator it = Rxn_gas_phase_map.begin(); + //for ( ; it != Rxn_gas_phase_map.end(); it++) + //{ + //for (size_t nn = 0; nn < Rxn_new_gas_phase.size(); nn++) + //{ + //std::map::iterator it = Rxn_gas_phase_map.find(Rxn_new_gas_phase[nn]); + for (std::set::const_iterator nit = Rxn_new_gas_phase.begin(); nit != Rxn_new_gas_phase.end(); nit++) + { + std::map::iterator it = Rxn_gas_phase_map.find(*nit); + if (it == Rxn_gas_phase_map.end()) + { + assert(false); + } + cxxGasPhase *gas_phase_ptr = &it->second; + if (!gas_phase_ptr->Get_new_def()) + continue; + n_user = gas_phase_ptr->Get_n_user(); + last = gas_phase_ptr->Get_n_user_end(); + gas_phase_ptr->Set_n_user_end(n_user); + gas_phase_ptr->Set_new_def(false); + if (gas_phase_ptr->Get_solution_equilibria()) + { + if (print1 == TRUE && print == TRUE) + { + dup_print("Beginning of initial gas_phase" + "-composition calculations.", TRUE); + print1 = FALSE; + } + if (print == TRUE) + { + sprintf(token, "Gas_Phase %d.\t%.350s", + gas_phase_ptr->Get_n_user(), gas_phase_ptr->Get_description().c_str()); + dup_print(token, FALSE); + } + + /* Try to obtain a solution pointer */ + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, gas_phase_ptr->Get_n_solution())); + prep(); + k_temp(use.Get_solution_ptr()->Get_tc(), use.Get_solution_ptr()->Get_patm()); + set(TRUE); + converge = model(); + converge1 = check_residuals(); + if (converge == ERROR || converge1 == ERROR) + { + /* free_model_allocs(); */ + error_msg + ("Model failed to converge for initial gas phase calculation.", + STOP); + } + use.Set_gas_phase_ptr(gas_phase_ptr); + gas_phase_ptr->Set_total_p(0); + gas_phase_ptr->Set_total_moles(0); + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + cxxGasComp * gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + int k; + phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str(), &k, FALSE); + if (phase_ptr->in == TRUE) + { + lp = -phase_ptr->lk; + for (rxn_ptr = phase_ptr->rxn_x->token + 1; + rxn_ptr->s != NULL; rxn_ptr++) + { + lp += rxn_ptr->s->la * rxn_ptr->coef; + } + phase_ptr->p_soln_x = exp(lp * LOG_10); + gas_phase_ptr->Set_total_p(gas_phase_ptr->Get_total_p() + phase_ptr->p_soln_x); + phase_ptr->moles_x = phase_ptr->p_soln_x * + gas_phase_ptr->Get_volume() / (R_LITER_ATM * tk_x); + gc_ptr->Set_moles(phase_ptr->moles_x); + gas_phase_ptr->Set_total_moles(gas_phase_ptr->Get_total_moles() + phase_ptr->moles_x); + if (phase_ptr->p_c || phase_ptr->t_c) + PR = true; + } + else + { + phase_ptr->moles_x = 0; + } + } + if (fabs(gas_phase_ptr->Get_total_p() - use.Get_solution_ptr()->Get_patm()) > 5) + { + sprintf(token, + "WARNING: While initializing gas phase composition by equilibrating:\n%s (%.2f atm) %s (%.2f atm).\n%s.", + " Gas phase pressure", + (double) gas_phase_ptr->Get_total_p(), + "is not equal to solution-pressure", + (double) use.Get_solution_ptr()->Get_patm(), + " Pressure effects on solubility may be incorrect"); + dup_print(token, FALSE); + } + + print_gas_phase(); + if (PR /*&& use.Get_gas_phase_ptr()->total_p > 1.0*/) + warning_msg("While initializing gas phase composition by equilibrating:\n" + " Found definitions of gas` critical temperature and pressure.\n" + " Going to use Peng-Robinson in subsequent calculations.\n"); + xgas_save(n_user); + punch_all(); + /* free_model_allocs(); */ + } + Utilities::Rxn_copies(Rxn_gas_phase_map, n_user, last); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +initial_surfaces(int print) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through list of surfaces, make initial calculations + * for any marked "new" that are defined to be in equilibrium with a + * solution. + */ + int last, n_user, print1; + + state = INITIAL_SURFACE; + set_use(); + print1 = TRUE; + + //std::map::iterator it = Rxn_surface_map.begin(); + //for ( ; it != Rxn_surface_map.end(); it++) + //{ + //for (size_t nn = 0; nn < Rxn_new_surface.size(); nn++) + //{ + //std::map::iterator it = Rxn_surface_map.find(Rxn_new_surface[nn]); + for (std::set::const_iterator nit = Rxn_new_surface.begin(); nit != Rxn_new_surface.end(); nit++) + { + std::map::iterator it = Rxn_surface_map.find(*nit); + if (it == Rxn_surface_map.end()) + { + assert(false); + } + cxxSurface * surface_ptr = &it->second; + if (!surface_ptr->Get_new_def()) + continue; + n_user = surface_ptr->Get_n_user(); + last = surface_ptr->Get_n_user_end(); + surface_ptr->Set_n_user_end(n_user); + if (surface_ptr->Get_solution_equilibria()) + { + if (print1 == TRUE && print == TRUE) + { + dup_print + ("Beginning of initial surface-composition calculations.", + TRUE); + print1 = FALSE; + } + if (print == TRUE) + { + std::ostringstream msg; + msg << "Surface " << n_user << ".\t" << surface_ptr->Get_description().c_str(); + dup_print(msg.str().c_str(), FALSE); + } + use.Set_surface_ptr(surface_ptr); + dl_type_x = use.Get_surface_ptr()->Get_dl_type(); + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, surface_ptr->Get_n_solution())); + if (use.Get_solution_ptr() == NULL) + { + error_msg + ("Solution not found for initial surface calculation", + STOP); + } + set_and_run_wrapper(-1, FALSE, FALSE, -1, 0.0); + species_list_sort(); + print_surface(); + /*print_all(); */ + punch_all(); + xsurface_save(n_user); + /* free_model_allocs(); */ + } + Utilities::Rxn_copies(Rxn_surface_map, n_user, last); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +reactions(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Make all reaction calculation which could include: + * equilibrium with a pure-phase assemblage, + * equilibrium with an exchanger, + * equilibrium with an surface, + * equilibrium with a gas phase, + * equilibrium with a solid solution assemblage, + * kinetics, + * change of temperature, + * mixture, + * or irreversible reaction. + */ + int count_steps, use_mix; + char token[2 * MAX_LENGTH]; + struct save save_data; + LDBLE kin_time; + cxxKinetics *kinetics_ptr; + + state = REACTION; + /* last_model.force_prep = TRUE; */ + if (set_use() == FALSE) + return (OK); +/* + * Find maximum number of steps + */ + dup_print("Beginning of batch-reaction calculations.", TRUE); + count_steps = 1; + if (use.Get_reaction_in() == TRUE && use.Get_reaction_ptr() != NULL) + { + cxxReaction *reaction_ptr = use.Get_reaction_ptr(); + if (reaction_ptr->Get_reaction_steps() > count_steps) + count_steps = reaction_ptr->Get_reaction_steps(); + } + if (use.Get_kinetics_in() == TRUE && use.Get_kinetics_ptr() != NULL) + { + if (use.Get_kinetics_ptr()->Get_reaction_steps() > count_steps) + count_steps = use.Get_kinetics_ptr()->Get_reaction_steps(); + } + if (use.Get_temperature_in() == TRUE && use.Get_temperature_ptr() != NULL) + { + int count = use.Get_temperature_ptr()->Get_countTemps(); + if (count > count_steps) + { + count_steps = count; + } + } + if (use.Get_pressure_in() == TRUE && use.Get_pressure_ptr() != NULL) + { + int count = use.Get_pressure_ptr()->Get_count(); + if (count > count_steps) + { + count_steps = count; + } + } + count_total_steps = count_steps; +/* + * save data for saving solutions + */ + memcpy(&save_data, &save, sizeof(struct save)); + /* + *Copy everything to -2 + */ + copy_use(-2); + rate_sim_time_start = 0; + rate_sim_time = 0; + for (reaction_step = 1; reaction_step <= count_steps; reaction_step++) + { + sprintf(token, "Reaction step %d.", reaction_step); + if (reaction_step > 1 && incremental_reactions == FALSE) + { + copy_use(-2); + } + set_initial_moles(-2); + dup_print(token, FALSE); +/* + * Determine time step for kinetics + */ + kin_time = 0.0; + + if (use.Get_kinetics_in() == TRUE) + { + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, -2); + kin_time = kinetics_ptr->Current_step((incremental_reactions==TRUE), reaction_step); + } + if (incremental_reactions == FALSE || + (incremental_reactions == TRUE && reaction_step == 1)) + { + use_mix = TRUE; + } + else + { + use_mix = FALSE; + } +/* + * Run reaction step + */ + run_reactions(-2, kin_time, use_mix, 1.0); + if (incremental_reactions == TRUE) + { + rate_sim_time_start += kin_time; + rate_sim_time = rate_sim_time_start; + } + else + { + rate_sim_time = kin_time; + } + if (state != ADVECTION) + { + punch_all(); + print_all(); + } + /* saves back into -2 */ + if (reaction_step < count_steps) + { + saver(); + } + } +/* + * save end of reaction + */ + memcpy(&save, &save_data, sizeof(struct save)); + if (use.Get_kinetics_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_kinetics_map, -2, use.Get_n_kinetics_user()); + } + saver(); + + /* free_model_allocs(); */ +//// set pr_in to false for following steps... +// if (use.Get_pp_assemblage_in()) +// { +// for (int i = 0; i < count_unknowns; i++) +// { +// if (x[i]->type == PP) +// x[i]->phase->pr_in = false; +// } +// } +// if (use.Get_gas_phase_in()) +// { +// cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); +// for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) +// { +// cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); +// int k; +// struct phase *phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str(), &k, FALSE); +// assert(phase_ptr); +// phase_ptr->pr_in = false; +// } +// } + /* last_model.force_prep = TRUE; */ + rate_sim_time = 0; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +saver(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Save results of calcuations (data in variables with _x, + * in unknown structure x, in master, or s) into structure + * arrays. Structure "save" has info on whether to save + * data for each entity (solution, ex, surf, pp, gas, or s_s). + * Initial calculation may be saved into multiple "n_user" + * slots. + */ + int i, n; + char token[MAX_LENGTH]; + + if (save.solution == TRUE) + { + sprintf(token, "Solution after simulation %d.", simulation); + description_x = (char *) free_check_null(description_x); + description_x = string_duplicate(token); + n = save.n_solution_user; + xsolution_save(n); + for (i = save.n_solution_user + 1; i <= save.n_solution_user_end; i++) + { + Utilities::Rxn_copy(Rxn_solution_map, n, i); + } + } + if (save.pp_assemblage == TRUE) + { + n = save.n_pp_assemblage_user; + xpp_assemblage_save(n); + Utilities::Rxn_copies(Rxn_pp_assemblage_map, save.n_pp_assemblage_user, save.n_pp_assemblage_user_end); + } + if (save.exchange == TRUE) + { + n = save.n_exchange_user; + xexchange_save(n); + for (i = save.n_exchange_user + 1; i <= save.n_exchange_user_end; i++) + { + Utilities::Rxn_copy(Rxn_exchange_map, n, i); + } + } + if (save.surface == TRUE) + { + n = save.n_surface_user; + xsurface_save(n); + Utilities::Rxn_copies(Rxn_surface_map, n, save.n_surface_user_end); + } + if (save.gas_phase == TRUE) + { + n = save.n_gas_phase_user; + xgas_save(n); + for (i = save.n_gas_phase_user + 1; i <= save.n_gas_phase_user_end; + i++) + { + Utilities::Rxn_copy(Rxn_gas_phase_map, n, i); + } + } + if (save.ss_assemblage == TRUE) + { + n = save.n_ss_assemblage_user; + xss_assemblage_save(n); + Utilities::Rxn_copies(Rxn_ss_assemblage_map, save.n_ss_assemblage_user, save.n_ss_assemblage_user_end); + } + if (save.kinetics == TRUE && use.Get_kinetics_in() == TRUE + /*&& use.Get_kinetics_ptr() != NULL */) + { + if (state == TRANSPORT || state == PHAST || state == ADVECTION) + { + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_kinetics_user())); + } + else if (use.Get_kinetics_in() != FALSE) + { + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, -2)); + } + if (use.Get_kinetics_ptr() != NULL) + { + n = use.Get_kinetics_ptr()->Get_n_user(); + for (i = save.n_kinetics_user; i <= save.n_kinetics_user_end; i++) + { + Utilities::Rxn_copy(Rxn_kinetics_map, n, i); + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +xexchange_save(int n_user) +/* ---------------------------------------------------------------------- */ +{ +/* + * Save exchanger assemblage into structure exchange with user + * number n_user. + */ + int i, j; + char token[MAX_LENGTH]; + + LDBLE charge; + if (use.Get_exchange_ptr() == NULL) + return (OK); + + cxxExchange temp_exchange = *use.Get_exchange_ptr(); +/* + * Store data for structure exchange + */ + temp_exchange.Set_n_user(n_user); + temp_exchange.Set_n_user_end(n_user); + temp_exchange.Set_new_def(false); + sprintf(token, "Exchange assemblage after simulation %d.", simulation); + temp_exchange.Set_description(token); + temp_exchange.Set_solution_equilibria(false); + temp_exchange.Set_n_solution(-999); + temp_exchange.Get_exchange_comps().clear(); + +/* + * Write exch_comp structure for each exchange component + */ + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == EXCH) + { + const cxxExchComp *comp_ptr = use.Get_exchange_ptr()->Find_comp(x[i]->exch_comp); + if (!comp_ptr) + { + assert(false); + } + cxxExchComp xcomp = *comp_ptr; + xcomp.Set_la(x[i]->master[0]->s->la); +/* + * Save element concentrations on exchanger + */ + count_elts = 0; + paren_count = 0; + charge = 0.0; + for (j = 0; j < count_species_list; j++) + { + if (species_list[j].master_s == x[i]->master[0]->s) + { + add_elt_list(species_list[j].s->next_elt, + species_list[j].s->moles); + charge += species_list[j].s->moles * species_list[j].s->z; + } + } +/* + * Keep exchanger related to phase even if none currently in solution + */ + if (xcomp.Get_phase_name().size() != 0 && count_elts == 0) + { + add_elt_list(x[i]->master[0]->s->next_elt, 1e-20); + } +/* + * Store list + */ + xcomp.Set_charge_balance(charge); + + xcomp.Set_totals(elt_list_NameDouble()); +/* debug + output_msg(sformatf( "Exchange charge_balance: %e\n", charge)); + */ + /* update unknown pointer */ + temp_exchange.Get_exchange_comps().push_back(xcomp); + } + } +/* + * Finish up + */ + Rxn_exchange_map[n_user] = temp_exchange; + + use.Set_exchange_ptr(NULL); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +xgas_save(int n_user) +/* ---------------------------------------------------------------------- */ +{ +/* + * Save gas composition into structure gas_phase with user + * number n_user. + */ + char token[MAX_LENGTH]; + + if (use.Get_gas_phase_ptr() == NULL) + return (OK); + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + cxxGasPhase temp_gas_phase(*gas_phase_ptr); +/* + * Store in gas_phase + */ + temp_gas_phase.Set_n_user(n_user); + temp_gas_phase.Set_n_user_end(n_user); + sprintf(token, "Gas phase after simulation %d.", simulation); + temp_gas_phase.Set_description(token); + temp_gas_phase.Set_new_def(false); + temp_gas_phase.Set_solution_equilibria(false); + temp_gas_phase.Set_n_solution(-99); +/* + * Update amounts + */ + for (size_t i = 0 ; i < temp_gas_phase.Get_gas_comps().size(); i++) + { + cxxGasComp * gc_ptr = &(temp_gas_phase.Get_gas_comps()[i]); + int k; + struct phase *phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str(), &k, FALSE); + assert(phase_ptr); + gc_ptr->Set_moles(phase_ptr->moles_x); + } + Rxn_gas_phase_map[n_user] = temp_gas_phase; + + use.Set_gas_phase_ptr(NULL); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +xss_assemblage_save(int n_user) +/* ---------------------------------------------------------------------- */ +{ +/* + * Save ss_assemblage composition into structure ss_assemblage with user + * number n_user. + */ + cxxSSassemblage temp_ss_assemblage; + + if (use.Get_ss_assemblage_ptr() == NULL) + return (OK); +/* + * Set ss_assemblage + */ + temp_ss_assemblage.Set_n_user(n_user); + temp_ss_assemblage.Set_n_user_end(n_user); + std::ostringstream msg; + msg << "Solid solution assemblage after simulation " << simulation; + temp_ss_assemblage.Set_description(msg.str().c_str()); + temp_ss_assemblage.Set_new_def(false); + temp_ss_assemblage.Set_SSs(use.Get_ss_assemblage_ptr()->Get_SSs()); + + std::vector ss_ptrs = temp_ss_assemblage.Vectorize(); + for (size_t i = 0; i < ss_ptrs.size(); i++) + { + cxxSS *ss_ptr = ss_ptrs[i]; + /* set initial moles for quick setup */ + for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[j]); + comp_ptr->Set_initial_moles(comp_ptr->Get_moles()); + } + } +/* + * Finish up + */ + Rxn_ss_assemblage_map[n_user] = temp_ss_assemblage; + + use.Set_ss_assemblage_ptr(NULL); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +xpp_assemblage_save(int n_user) +/* ---------------------------------------------------------------------- */ +{ +/* + * Save pure_phase assemblage into instance of cxxPPassemblage with user + * number n_user. + */ + std::string token; + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + if (use.Get_pp_assemblage_ptr() == NULL) + return (OK); + + cxxPPassemblage temp_pp_assemblage(*pp_assemblage_ptr); + + temp_pp_assemblage.Set_n_user(n_user); + temp_pp_assemblage.Set_n_user_end(n_user); + std::ostringstream desc; + desc << "Pure-phase assemblage after simulation " << simulation << "."; + temp_pp_assemblage.Set_description(desc.str().c_str()); + temp_pp_assemblage.Set_new_def(false); +/* + * Update amounts + */ + for (int j = 0; j < count_unknowns; j++) + { + if (x[j]->type != PP) + continue; + cxxPPassemblageComp *comp = temp_pp_assemblage.Find(x[j]->pp_assemblage_comp_name); + comp->Set_moles(x[j]->moles); + comp->Set_delta(0.0); + } +/* + * Finish up + */ + + Rxn_pp_assemblage_map[n_user] = temp_pp_assemblage; + use.Set_pp_assemblage_ptr(NULL); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +xsolution_save(int n_user) +/* ---------------------------------------------------------------------- */ +{ +/* + * Save solution composition into structure solution with user number + * n_user. + * + * input: n_user is user solution number of target + */ + struct master *master_i_ptr, *master_ptr; +/* + * Malloc space for solution data + */ + cxxSolution temp_solution; + temp_solution.Set_n_user_both(n_user); + temp_solution.Set_new_def(false); + temp_solution.Set_description(description_x); + temp_solution.Set_tc(tc_x); + temp_solution.Set_patm(patm_x); + temp_solution.Set_potV(potV_x); + temp_solution.Set_ph(ph_x); + temp_solution.Set_pe(solution_pe_x); + temp_solution.Set_mu(mu_x); + temp_solution.Set_ah2o(ah2o_x); + //temp_solution.Set_density(density_x); + temp_solution.Set_density(calc_dens()); + temp_solution.Set_total_h(total_h_x); + temp_solution.Set_total_o(total_o_x); + temp_solution.Set_cb(cb_x); /* cb_x does not include surface charge sfter sum_species */ + /* does include surface charge after step */ + temp_solution.Set_mass_water(mass_water_aq_x); + temp_solution.Set_total_alkalinity(total_alkalinity); + temp_solution.Set_soln_vol(this->calc_solution_volume()); +/* + * Copy pe data + */ + /* + * Add in minor isotopes if initial solution calculation + */ + if (initial_solution_isotopes == TRUE) + { + for (int i = 0; i < count_master_isotope; i++) + { + if (master_isotope[i]->moles > 0) + { + master_i_ptr = master_bsearch(master_isotope[i]->name); + master_ptr = master_isotope[i]->elt->master; + if (master_isotope[i]->minor_isotope == TRUE) + { + master_i_ptr->total = master_isotope[i]->moles; + if (master_ptr->total > 0) + { + master_i_ptr->s->la = + master_ptr->s->la + + log10(master_i_ptr->total / master_ptr->total); + } + else + { + master_i_ptr->s->la = master_ptr->s->la; + } + } + else if (master_isotope[i]->minor_isotope == FALSE + && master_ptr->s != s_hplus + && master_ptr->s != s_h2o) + { + if (master_ptr->s->secondary != NULL) + { + master_ptr->s->secondary->total = + master_isotope[i]->moles; + } + else + { + master_ptr->s->primary->total = + master_isotope[i]->moles; + } + } + } + } + } +/* + * Copy totals data + */ + for (int i = 0; i < count_master; i++) + { + if (master[i]->s->type == EX || + master[i]->s->type == SURF || master[i]->s->type == SURF_PSI) + continue; + if (master[i]->s == s_hplus) + continue; + if (master[i]->s == s_h2o) + continue; +/* + * Save list of log activities + */ + if (master[i]->in != FALSE) + { + temp_solution.Get_master_activity()[master[i]->elt->name] = master[i]->s->la; + } + if (master[i]->total <= MIN_TOTAL) + { + master[i]->total = 0.0; + master[i]->total_primary = 0.0; + continue; + } +/* + * Save list of concentrations + */ + temp_solution.Get_totals()[master[i]->elt->name] = master[i]->total; + } + if (pitzer_model == TRUE || sit_model == TRUE) + { + for (int j = 0; j < count_s_x; j++) + { + if (s_x[j]->lg != 0.0) + { + temp_solution.Get_species_gamma()[s_x[j]->name] = s_x[j]->lg; + } + } + } +/* + * Save isotope data + */ + temp_solution.Set_isotopes(isotopes_x); + std::map< std::string, cxxSolutionIsotope >::iterator it; + for (it = temp_solution.Get_isotopes().begin(); it != temp_solution.Get_isotopes().end(); it++) + { + struct master *iso_master_ptr = master_bsearch(it->second.Get_elt_name().c_str()); + it->second.Set_total(iso_master_ptr->total); + if (iso_master_ptr == s_hplus->secondary) + { + it->second.Set_total(2 * mass_water_aq_x / gfw_water); + } + if (iso_master_ptr == s_h2o->secondary) + { + it->second.Set_total(mass_water_aq_x / gfw_water); + } + } +#ifdef SKIP +/* + * Bug-fix + * Create and initialize intial data (this object should always be present even if it is left empty) + */ + + temp_solution.Create_initial_data(); + cxxISolution* initialData = temp_solution.Get_initial_data(); + + initialData->Set_units( "Mol/kgw" ); + + // Copy totals to initialdata when present + if ( !temp_solution.Get_totals().empty() ) + { + cxxNameDouble& totals = temp_solution.Get_totals(); + + for (cxxNameDouble::iterator jit = totals.begin(); jit != totals.end(); jit++) + { + std::string compName( jit->first ); + double compConc = jit->second; + + SolutionCompMap& comps = initialData->Get_comps(); + + cxxISolutionComp& tempComp = comps[ compName ]; + + tempComp.Set_description( compName.c_str() ); + tempComp.Set_input_conc( compConc / temp_solution.Get_mass_water()); + tempComp.Set_units( initialData->Get_units().c_str() ); + } + } +#endif + if (this->save_species) + { + // saves mol/L + temp_solution.Get_species_map().clear(); + for (int i = 0; i < this->count_s_x; i++) + { + if (s_x[i]->type <= H2O) + { + temp_solution.Get_species_map()[s_x[i]->number] = s_x[i]->moles / temp_solution.Get_soln_vol(); + } + } + // saves gamma + temp_solution.Get_log_gamma_map().clear(); + for (int i = 0; i < this->count_s_x; i++) + { + if (s_x[i]->type <= H2O) + { + temp_solution.Get_log_gamma_map()[s_x[i]->number] = s_x[i]->lg; + } + } + } +/* + * Save solution + */ + Rxn_solution_map[n_user] = temp_solution; + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +xsurface_save(int n_user) +/* ---------------------------------------------------------------------- */ +{ +/* + * Save surface data into structure surface with user + * number n_user. + */ + LDBLE charge; + if (use.Get_surface_ptr() == NULL) + return (OK); +/* + * Store data for structure surface + */ + cxxSurface temp_surface = *use.Get_surface_ptr(); + temp_surface.Set_n_user(n_user); + temp_surface.Set_n_user_end(n_user); + temp_surface.Set_new_def(false); + temp_surface.Set_dl_type(dl_type_x); + temp_surface.Set_solution_equilibria(false); + temp_surface.Set_n_solution(-999); + + if (temp_surface.Get_type() == cxxSurface::NO_EDL) + { + temp_surface.Get_surface_charges().clear(); + } +/* + * Write surface_comp structure for each surf component into comps_ptr + */ + /* + * Initial entry of surface sites is random + * Charge balance numbering follows the initial entry + * Surface sites are then sorted alphabetically + * Now when we save, the site order differs from the charge order + * last_charge sets up logic to renumber charge balance equations. + */ + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type == SURFACE) + { + cxxSurfaceComp *comp_ptr = temp_surface.Find_comp(x[i]->surface_comp); + assert(comp_ptr); + comp_ptr->Set_la(x[i]->master[0]->s->la); + comp_ptr->Set_moles(0.); +/* + * Save element concentrations on surface + */ + count_elts = 0; + paren_count = 0; + charge = 0.0; + for (int j = 0; j < count_species_list; j++) + { + if (species_list[j].master_s == x[i]->master[0]->s) + { + add_elt_list(species_list[j].s->next_elt, + species_list[j].s->moles); + //add_elt_list_multi_surf(species_list[j].s->next_elt, + // species_list[j].s->moles, x[i]->master[0]->elt); + charge += species_list[j].s->moles * species_list[j].s->z; + } + } + { + cxxNameDouble nd = elt_list_NameDouble(); + comp_ptr->Set_totals(nd); + } + comp_ptr->Set_charge_balance(charge); + } + else if (x[i]->type == SURFACE_CB && (use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM)) + { + cxxSurfaceCharge *charge_ptr = temp_surface.Find_charge(x[i]->surface_charge); + assert(charge_ptr); + charge_ptr->Set_charge_balance(x[i]->f); + charge_ptr->Set_la_psi(x[i]->master[0]->s->la); +/* + * Store moles from diffuse_layer + */ + if (dl_type_x != cxxSurface::NO_DL) + { + sum_diffuse_layer(charge_ptr); + cxxNameDouble nd = elt_list_NameDouble(); + charge_ptr->Set_diffuse_layer_totals(nd); + } + } + else if (x[i]->type == SURFACE_CB + && use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + cxxSurfaceCharge *charge_ptr = temp_surface.Find_charge(x[i]->surface_charge); + assert(charge_ptr); + if (dl_type_x != cxxSurface::NO_DL) + { + charge_ptr->Set_charge_balance( + (charge_ptr->Get_sigma0() + + charge_ptr->Get_sigma1() + + charge_ptr->Get_sigma2() + + charge_ptr->Get_sigmaddl()) + * (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()) / F_C_MOL); + } + else + { + charge_ptr->Set_charge_balance( + (charge_ptr->Get_sigma0() + + charge_ptr->Get_sigma1() + + charge_ptr->Get_sigma2()) + * (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()) / F_C_MOL); + } + charge_ptr->Set_la_psi(x[i]->master[0]->s->la); +/* + * Store moles from diffuse_layer + */ + if (dl_type_x != cxxSurface::NO_DL) + { + sum_diffuse_layer(charge_ptr); + cxxNameDouble nd = elt_list_NameDouble(); + charge_ptr->Set_diffuse_layer_totals(nd); + } + } + } + if (!(dl_type_x == cxxSurface::NO_DL)) + { + cxxSurface *surface_ptr = &temp_surface; + for (size_t i = 0; i < surface_ptr->Get_surface_charges().size(); i++) + { + cxxSurfaceCharge & charge_ref = surface_ptr->Get_surface_charges()[i]; + double mass_water_surface = charge_ref.Get_mass_water(); + for (int j = 0; j < count_s_x; j++) + { + if (s_x[j]->type > H2O) + continue; + double molality = under(s_x[j]->lm); + double moles_excess = mass_water_aq_x * molality * charge_ref.Get_g_map()[s_x[j]->z].Get_g(); + double moles_surface = mass_water_surface * molality + moles_excess; + charge_ref.Get_dl_species_map()[s_x[j]->number] = moles_surface/mass_water_surface; +//#ifdef SKIP + double g = charge_ref.Get_g_map()[s_x[j]->z].Get_g(); + //double moles_excess = mass_water_aq_x * molality * (g * s_x[j]->erm_ddl + + // mass_water_surface / + // mass_water_aq_x * (s_x[j]->erm_ddl - 1)); + + //LDBLE g = charge_ptr->Get_g_map()[s_x[j]->z].Get_g(); + if (s_x[j]->erm_ddl != 1) + { + LDBLE ratio_aq = mass_water_surface / mass_water_aq_x; + LDBLE g2 = g / ratio_aq + 1; + g = ratio_aq * (g2 * s_x[j]->erm_ddl - 1); + } + moles_excess = mass_water_aq_x * molality * g; + double c = (mass_water_surface * molality + moles_excess) / mass_water_surface; + charge_ref.Get_dl_species_map()[s_x[j]->number] = c; + + +//#endif + } + //charge_ref.Get_dl_species_map()[s_h2o->number] = 0.0; + charge_ref.Get_dl_species_map()[s_h2o->number] = 1.0/gfw_water; + } + } + +/* + * Finish up + */ + Rxn_surface_map[n_user] = temp_surface; + use.Set_surface_ptr(NULL); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +copy_use(int i) +/* ---------------------------------------------------------------------- */ +{ +/* + * Find mixture + */ + if (use.Get_mix_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_mix_map, use.Get_n_mix_user(), i); + } +/* + * Find solution + */ + if (use.Get_solution_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_solution_map, use.Get_n_solution_user(), i); + } +/* + * Always save solution to i, mixing or not + */ + save.solution = TRUE; + save.n_solution_user = i; + save.n_solution_user_end = i; +/* + * Find pure phase assemblage + */ + if (use.Get_pp_assemblage_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_pp_assemblage_map, use.Get_n_pp_assemblage_user(), i); + save.pp_assemblage = TRUE; + save.n_pp_assemblage_user = i; + save.n_pp_assemblage_user_end = i; + } + else + { + save.pp_assemblage = FALSE; + } +/* + * Find irrev reaction + */ + if (use.Get_reaction_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_reaction_map, use.Get_n_reaction_user(), i); + save.reaction = TRUE; + save.n_reaction_user = i; + save.n_reaction_user_end = i; + } + else + { + save.reaction = FALSE; + } +/* + * Find exchange + */ + if (use.Get_exchange_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_exchange_map, use.Get_n_exchange_user(), i); + save.exchange = TRUE; + save.n_exchange_user = i; + save.n_exchange_user_end = i; + } + else + { + save.exchange = FALSE; + } +/* + * Find kinetics + */ + if (use.Get_kinetics_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_kinetics_map, use.Get_n_kinetics_user(), i); + save.kinetics = TRUE; + save.n_kinetics_user = i; + save.n_kinetics_user_end = i; + } + else + { + save.kinetics = FALSE; + } +/* + * Find surface + */ + dl_type_x = cxxSurface::NO_DL; + if (use.Get_surface_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_surface_map, use.Get_n_surface_user(), i); + save.surface = TRUE; + save.n_surface_user = i; + save.n_surface_user_end = i; + } + else + { + save.surface = FALSE; + } +/* + * Find temperature + */ + if (use.Get_temperature_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_temperature_map, use.Get_n_temperature_user(), i); + } +/* + * Find pressure + */ + if (use.Get_pressure_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_pressure_map, use.Get_n_pressure_user(), i); + } +/* + * Find gas + */ + if (use.Get_gas_phase_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_gas_phase_map, use.Get_n_gas_phase_user(), i); + save.gas_phase = TRUE; + save.n_gas_phase_user = i; + save.n_gas_phase_user_end = i; + } + else + { + save.gas_phase = FALSE; + } +/* + * Find solid solution + */ + if (use.Get_ss_assemblage_in() == TRUE) + { + Utilities::Rxn_copy(Rxn_ss_assemblage_map, use.Get_n_ss_assemblage_user(), i); + save.ss_assemblage = TRUE; + save.n_ss_assemblage_user = i; + save.n_ss_assemblage_user_end = i; + } + else + { + save.ss_assemblage = FALSE; + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +step_save_exch(int n_user) +/* ---------------------------------------------------------------------- */ +{ +/* + * Save exchange composition + * + * input: n_user is user exchange number of target + */ + + if (use.Get_exchange_ptr() == NULL) + return (OK); + + cxxExchange *temp_ptr = Utilities::Rxn_find(Rxn_exchange_map, use.Get_n_exchange_user()); + assert(temp_ptr); + + // Set all totals to 0.0 + cxxExchange temp_exchange = *temp_ptr; + { + for (size_t i = 0; i < temp_exchange.Get_exchange_comps().size(); i++) + { + temp_exchange.Get_exchange_comps()[i].Get_totals().multiply(0.0); + } + } + + // Set exchange total in one component + for (int i = 0; i < count_master; i++) + { + if (master[i]->s->type != EX) + continue; + std::string e(master[i]->elt->name); + for (size_t j = 0; j < temp_exchange.Get_exchange_comps().size(); j++) + { + cxxNameDouble *nd = &(temp_exchange.Get_exchange_comps()[j].Get_totals()); + cxxNameDouble::iterator nd_it = nd->find(e); + if (nd_it != nd->end()) + { + LDBLE coef; + if (master[i]->total <= MIN_TOTAL) + { + coef = MIN_TOTAL; + } + else + { + coef = master[i]->total; + } + nd->insert(nd_it->first.c_str(), coef); + break; + } + } + } + + Rxn_exchange_map[n_user] = temp_exchange; + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +step_save_surf(int n_user) +/* ---------------------------------------------------------------------- */ +{ + /* + * Save surface for intermediate calculation + * Amt of surface may have changed due to reaction or surface related + * to kinetic reactant. + * + * input: n_user is user solution number of target + */ + if (use.Get_surface_ptr() == NULL) + return (OK); + Utilities::Rxn_copy(Rxn_surface_map, use.Get_surface_ptr()->Get_n_user(), n_user); + cxxSurface *surface_ptr = Utilities::Rxn_find(Rxn_surface_map, n_user); + for (int i = 0; i < count_master; i++) + { + if (master[i]->s->type != SURF) + continue; + for (size_t j = 0; j < surface_ptr->Get_surface_comps().size(); j++) + { + cxxSurfaceComp * comp_ptr = &(surface_ptr->Get_surface_comps()[j]); + cxxNameDouble & totals = comp_ptr->Get_totals(); + if (totals.find(master[i]->elt->name) == totals.end()) + { + continue; + } + else + { + LDBLE coef = master[i]->total; + if (master[i]->total <= MIN_TOTAL) + { + coef = MIN_TOTAL; + } + totals[master[i]->elt->name] = coef; + break; + } + } + } + /* + * Update grams + */ + if ((surface_ptr->Get_type() == cxxSurface::DDL || surface_ptr->Get_type() == cxxSurface::CCM || surface_ptr->Get_type() == cxxSurface::CD_MUSIC) + && surface_ptr->Get_related_rate() && use.Get_kinetics_ptr() != NULL) + { + for (size_t j = 0; j < surface_ptr->Get_surface_comps().size(); j++) + { + cxxSurfaceComp *surface_comp_ptr = &(surface_ptr->Get_surface_comps()[j]); + if (surface_comp_ptr->Get_rate_name().size() > 0) + { + cxxKinetics *kinetics_ptr = use.Get_kinetics_ptr(); + for (size_t m = 0; m < kinetics_ptr->Get_kinetics_comps().size(); m++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[m]); + if (strcmp_nocase + (kinetics_comp_ptr->Get_rate_name().c_str(), + surface_comp_ptr->Get_rate_name().c_str()) != 0) + continue; + cxxSurfaceCharge *charge_ptr = surface_ptr->Find_charge(surface_comp_ptr->Get_charge_name()); + charge_ptr->Set_grams(kinetics_comp_ptr->Get_m()); + break; + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +copy_entities(void) +/* ---------------------------------------------------------------------- */ +{ + int i, j, return_value; + int verbose; + + verbose = FALSE; + return_value = OK; + if (copy_solution.count > 0) + { + for (j = 0; j < copy_solution.count; j++) + { + if (Utilities::Rxn_find(Rxn_solution_map, copy_solution.n_user[j]) != NULL) + { + for (i = copy_solution.start[j]; i <= copy_solution.end[j]; + i++) + { + if (i == copy_solution.n_user[j]) + continue; + Utilities::Rxn_copy(Rxn_solution_map, copy_solution.n_user[j], i); + } + } + else + { + if (verbose == TRUE) + { + warning_msg("SOLUTION to copy not found."); + return_value = ERROR; + } + } + } + } + if (copy_pp_assemblage.count > 0) + { + for (j = 0; j < copy_pp_assemblage.count; j++) + { + if (Utilities::Rxn_find(Rxn_pp_assemblage_map, copy_pp_assemblage.n_user[j]) != NULL) + { + for (i = copy_pp_assemblage.start[j]; + i <= copy_pp_assemblage.end[j]; i++) + { + if (i == copy_pp_assemblage.n_user[j]) + continue; + Utilities::Rxn_copy(Rxn_pp_assemblage_map, copy_pp_assemblage.n_user[j], i); + } + } + else + { + if (verbose == TRUE) + { + warning_msg("EQUILIBRIUM_PHASES to copy not found."); + return_value = ERROR; + } + } + } + } + if (copy_reaction.count > 0) + { + for (j = 0; j < copy_reaction.count; j++) + { + if (Utilities::Rxn_find(Rxn_reaction_map, copy_reaction.n_user[j]) != NULL) + { + for (i = copy_reaction.start[j]; i <= copy_reaction.end[j]; i++) + { + if (i == copy_reaction.n_user[j]) + continue; + Utilities::Rxn_copy(Rxn_reaction_map, copy_reaction.n_user[j], i); + } + } + else + { + if (verbose == TRUE) + { + warning_msg("REACTION to copy not found."); + return_value = ERROR; + } + } + } + } + if (copy_mix.count > 0) + { + for (j = 0; j < copy_mix.count; j++) + { + if (Utilities::Rxn_find(Rxn_mix_map, copy_mix.n_user[j]) != NULL) + { + for (i = copy_mix.start[j]; i <= copy_mix.end[j]; i++) + { + if (i != copy_mix.n_user[j]) + { + Utilities::Rxn_copy(Rxn_mix_map, copy_mix.n_user[j], i); + } + } + } + else + { + if (verbose == TRUE) + { + warning_msg("Mix to copy not found."); + return_value = ERROR; + } + } + } + } + + if (copy_exchange.count > 0) + { + for (j = 0; j < copy_exchange.count; j++) + { + if (Utilities::Rxn_find(Rxn_exchange_map, copy_exchange.n_user[j]) != NULL) + { + for (i = copy_exchange.start[j]; i <= copy_exchange.end[j]; + i++) + { + if (i == copy_exchange.n_user[j]) + continue; + Utilities::Rxn_copy(Rxn_exchange_map, copy_exchange.n_user[j], i); + } + } + else + { + if (verbose == TRUE) + { + warning_msg("EXCHANGE to copy not found."); + return_value = ERROR; + } + } + } + } + if (copy_surface.count > 0) + { + for (j = 0; j < copy_surface.count; j++) + { + if (Utilities::Rxn_find(Rxn_surface_map, copy_surface.n_user[j]) != NULL) + { + for (i = copy_surface.start[j]; i <= copy_surface.end[j]; i++) + { + if (i == copy_surface.n_user[j]) + continue; + Utilities::Rxn_copy(Rxn_surface_map, copy_surface.n_user[j], i); + } + } + else + { + if (verbose == TRUE) + { + warning_msg("SURFACE to copy not found."); + return_value = ERROR; + } + } + } + } + + if (copy_temperature.count > 0) + { + for (j = 0; j < copy_temperature.count; j++) + { + if (Utilities::Rxn_find(Rxn_temperature_map, copy_temperature.n_user[j]) != NULL) + { + for (i = copy_temperature.start[j]; i <= copy_temperature.end[j]; i++) + { + if (i != copy_temperature.n_user[j]) + { + Utilities::Rxn_copy(Rxn_temperature_map, copy_temperature.n_user[j], i); + } + } + } + else + { + if (verbose == TRUE) + { + warning_msg("temperature to copy not found."); + return_value = ERROR; + } + } + } + } + if (copy_pressure.count > 0) + { + for (j = 0; j < copy_pressure.count; j++) + { + if (Utilities::Rxn_find(Rxn_pressure_map, copy_pressure.n_user[j]) != NULL) + { + for (i = copy_pressure.start[j]; i <= copy_pressure.end[j]; i++) + { + if (i != copy_pressure.n_user[j]) + { + Utilities::Rxn_copy(Rxn_pressure_map, copy_pressure.n_user[j], i); + } + } + } + else + { + if (verbose == TRUE) + { + warning_msg("pressure to copy not found."); + return_value = ERROR; + } + } + } + } + if (copy_gas_phase.count > 0) + { + for (j = 0; j < copy_gas_phase.count; j++) + { + if (Utilities::Rxn_find(Rxn_gas_phase_map, copy_gas_phase.n_user[j]) != NULL) + { + for (i = copy_gas_phase.start[j]; i <= copy_gas_phase.end[j]; + i++) + { + if (i == copy_gas_phase.n_user[j]) + continue; + Utilities::Rxn_copy(Rxn_gas_phase_map, copy_gas_phase.n_user[j], i); + } + } + else + { + if (verbose == TRUE) + { + warning_msg("EXCHANGE to copy not found."); + return_value = ERROR; + } + } + } + } + if (copy_kinetics.count > 0) + { + for (j = 0; j < copy_kinetics.count; j++) + { + if (Utilities::Rxn_find(Rxn_kinetics_map, copy_kinetics.n_user[j]) != NULL) + { + for (i = copy_kinetics.start[j]; i <= copy_kinetics.end[j]; + i++) + { + if (i == copy_kinetics.n_user[j]) + continue; + Utilities::Rxn_copy(Rxn_kinetics_map, copy_kinetics.n_user[j], i); + } + } + else + { + if (verbose == TRUE) + { + warning_msg("KINETICS to copy not found."); + return_value = ERROR; + } + } + } + } + if (copy_ss_assemblage.count > 0) + { + for (j = 0; j < copy_ss_assemblage.count; j++) + { + if (Utilities::Rxn_find(Rxn_ss_assemblage_map, copy_ss_assemblage.n_user[j]) != NULL) + { + for (i = copy_ss_assemblage.start[j]; + i <= copy_ss_assemblage.end[j]; i++) + { + if (i == copy_ss_assemblage.n_user[j]) + continue; + Utilities::Rxn_copy(Rxn_ss_assemblage_map, copy_ss_assemblage.n_user[j], i); + } + } + else + { + if (verbose == TRUE) + { + warning_msg("SOLID_SOLUTIONS to copy not found."); + return_value = ERROR; + } + } + } + } + copy_solution.count = 0; + copy_pp_assemblage.count = 0; + copy_exchange.count = 0; + copy_surface.count = 0; + copy_ss_assemblage.count = 0; + copy_gas_phase.count = 0; + copy_kinetics.count = 0; + copy_mix.count = 0; + copy_reaction.count = 0; + copy_temperature.count = 0; + copy_pressure.count = 0; + new_copy = FALSE; + return return_value; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_database(void) +/* ---------------------------------------------------------------------- */ +{ + simulation = 0; + +/* + * Prepare error handling + */ + try + { + set_reading_database(TRUE); + dup_print("Reading data base.", TRUE); + read_input(); + tidy_model(); + status(0, NULL); + } + catch (const PhreeqcStop&) + { + return get_input_errors(); + } + set_reading_database(FALSE); + return 0; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +run_simulations(void) +/* ---------------------------------------------------------------------- */ +{ + char token[MAX_LENGTH]; +//#ifdef SKIP_KEEP +#if defined(_MSC_VER) && (_MSC_VER < 1900) // removed in vs2015 + unsigned int old_exponent_format; + old_exponent_format = _set_output_format(_TWO_DIGIT_EXPONENT); +#endif +//#endif +/* + * Prepare error handling + */ + try + { +/* + * Read input data for simulation + */ + for (simulation = 1;; simulation++) + { + +#if defined PHREEQ98 + AddSeries = !connect_simulations; +#endif + +#if defined PHREEQCI_GUI + sprintf(token, "\nSimulation %d\n", simulation); + screen_msg(token); +#endif + sprintf(token, "Reading input data for simulation %d.", simulation); + + dup_print(token, TRUE); + if (read_input() == EOF) + break; + + if (title_x != NULL) + { + sprintf(token, "TITLE"); + dup_print(token, TRUE); + if (pr.headings == TRUE) + output_msg(sformatf( "%s\n\n", title_x)); + } + tidy_model(); +#ifdef PHREEQ98 + if (!phreeq98_debug) + { +#endif + +/* + * Calculate distribution of species for initial solutions + */ + if (new_solution) + { + initial_solutions(TRUE); + } + +/* + * Calculate distribution for exchangers + */ + if (new_exchange) + initial_exchangers(TRUE); +/* + * Calculate distribution for surfaces + */ + if (new_surface) + initial_surfaces(TRUE); +/* + * Calculate initial gas composition + */ + if (new_gas_phase) + initial_gas_phases(TRUE); +/* + * Calculate reactions + */ + reactions(); +/* + * Calculate inverse models + */ + inverse_models(); +/* + * Calculate advection + */ + if (use.Get_advect_in()) + { + dup_print("Beginning of advection calculations.", TRUE); + advection(); + } +/* + * Calculate transport + */ + if (use.Get_trans_in()) + { + dup_print("Beginning of transport calculations.", TRUE); + transport(); + } +/* + * run + */ + run_as_cells(); +/* + * Calculate mixes + */ + do_mixes(); + +/* + * Copy + */ + if (new_copy) copy_entities(); +/* + * dump + */ + dump_entities(); +/* + * delete + */ + delete_entities(); +/* + * End of simulation + */ + dup_print("End of simulation.", TRUE); + output_flush(); + error_flush(); +#ifdef PHREEQ98 + } /* if (!phreeq98_debug) */ +#endif + } + } + catch (const PhreeqcStop&) + { + return get_input_errors(); + } + return 0; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +do_initialize(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prepare error handling + */ + try { + + state = INITIALIZE; + + initialize(); + } + catch (const PhreeqcStop&) + { + return get_input_errors(); + } + return 0; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +do_status(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prepare error handling + */ + try { + + if (pr.status == TRUE) + { + status(0, "\nDone."); + screen_msg("\n"); + } + //pr.headings = TRUE; // set in class_main; not set for IPhreeqc + LDBLE ext = (double) clock() / CLOCKS_PER_SEC; + dup_print(sformatf("End of Run after %g Seconds.", ext), TRUE); + screen_msg(sformatf("\nEnd of Run after %g Seconds.\n", ext)); +// appt this gives output when the charts are active... + phrq_io->output_flush(); + phrq_io->error_flush(); + } + catch (const PhreeqcStop&) + { + return get_input_errors(); + } + return 0; +} +void Phreeqc:: +save_init(int i) +{ + save.solution = i; + save.n_solution_user = i; + save.n_solution_user_end = i; + save.mix = i; + save.n_mix_user = i; + save.n_mix_user_end = i; + save.reaction = i; + save.n_reaction_user = i; + save.n_reaction_user_end = i; + save.pp_assemblage = i; + save.n_pp_assemblage_user = i; + save.n_pp_assemblage_user_end = i; + save.exchange = i; + save.n_exchange_user = i; + save.n_exchange_user_end = i; + save.kinetics = i; + save.n_kinetics_user = i; + save.n_kinetics_user_end = i; + save.surface = i; + save.n_surface_user = i; + save.n_surface_user_end = i; + save.gas_phase = i; + save.n_gas_phase_user = i; + save.n_gas_phase_user_end = i; + save.ss_assemblage = i; + save.n_ss_assemblage_user = i; + save.n_ss_assemblage_user_end = i; +} +void +Phreeqc::do_mixes(void) +{ + Utilities::Rxn_mix(Rxn_solution_mix_map, Rxn_solution_map, this); + Utilities::Rxn_mix(Rxn_exchange_mix_map, Rxn_exchange_map, this); + Utilities::Rxn_mix(Rxn_gas_phase_mix_map, Rxn_gas_phase_map, this); + Utilities::Rxn_mix(Rxn_kinetics_mix_map, Rxn_kinetics_map, this); + Utilities::Rxn_mix(Rxn_pp_assemblage_mix_map, Rxn_pp_assemblage_map, this); + Utilities::Rxn_mix(Rxn_ss_assemblage_mix_map, Rxn_ss_assemblage_map, this); + Utilities::Rxn_mix(Rxn_surface_mix_map, Rxn_surface_map, this); +} diff --git a/phreeqcpp/model.cpp b/phreeqcpp/model.cpp new file mode 100644 index 00000000..818bfdff --- /dev/null +++ b/phreeqcpp/model.cpp @@ -0,0 +1,5798 @@ +#include "Utils.h" +#include "Phreeqc.h" +#include "phqalloc.h" +#include "cxxMix.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "Solution.h" + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +model(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * model is called after the equations have been set up by prep + * and initial guesses have been made in set. + * + * Here is the outline of the calculation sequence: + * residuals--residuals are calculated, if small we are done + * sum_jacobian--jacobian is calculated + * ineq--inequality solver is called + * reset--estimates of unknowns revised, if changes are small solution + * has been found, usually convergence is found in residuals. + * gammas--new activity coefficients + * molalities--calculate molalities + * mb_sums--calculate mass-balance sums + * mb_gases--decide if gas_phase exists + * mb_ss--decide if solid_solutions exists + * switch_bases--check to see if new basis species is needed + * reprep--rewrite equations with new basis species if needed + * revise_guesses--revise unknowns to get initial mole balance + * check_residuals--check convergence one last time + * sum_species--calculate sums of elements from species concentrations + * + * An additional pass through may be needed if unstable phases still exist + * in the phase assemblage. + */ + int l_kode, return_kode; + int r; + int count_infeasible, count_basis_change; + int debug_model_save; + int mass_water_switch_save; + + set_inert_moles(); +/* debug_model = TRUE; */ +/* debug_prep = TRUE; */ +/* debug_set = TRUE; */ + /* mass_water_switch == TRUE, mass of water is constant */ + if (pitzer_model == TRUE && sit_model == TRUE) + { + input_error++; + error_msg("Cannot use PITZER and SIT data blocks in same run (database + input file).", STOP); + } + if (pitzer_model == TRUE) + { + + l_kode = model_pz(); + unset_inert_moles(); + return l_kode; + } + if (sit_model == TRUE) + { + + l_kode = model_sit(); + unset_inert_moles(); + return l_kode; + } + mass_water_switch_save = mass_water_switch; + if (mass_water_switch_save == FALSE && delay_mass_water == TRUE) + { + mass_water_switch = TRUE; + } + debug_model_save = debug_model; + pe_step_size_now = pe_step_size; + step_size_now = step_size; +#ifdef NPP + if (!use.Get_kinetics_in()) status(0, NULL); +#else + status(0, NULL); +#endif + iterations = 0; + count_basis_change = count_infeasible = 0; + stop_program = FALSE; + remove_unstable_phases = FALSE; + for (;;) + { + mb_gases(); + mb_ss(); + l_kode = 1; + while ((r = residuals()) != CONVERGED + || remove_unstable_phases == TRUE) + { +#if defined(PHREEQCI_GUI) + PhreeqcIWait(this); +#endif + iterations++; + if (iterations > itmax - 1 && debug_model == FALSE + && pr.logfile == TRUE) + { + set_forward_output_to_log(TRUE); + debug_model = TRUE; + } + if (debug_model == TRUE) + { + output_msg(sformatf( + "\nIteration %d\tStep_size = %f\n", iterations, + (double) step_size_now)); + output_msg(sformatf( "\t\tPe_step_size = %f\n\n", + (double) pe_step_size_now)); + } +/* + * Iterations exceeded + */ + if (iterations > itmax) + { + error_string = sformatf( "Maximum iterations exceeded, %d\n", + itmax); + warning_msg(error_string); + stop_program = TRUE; + break; + } +/* + * Calculate jacobian + */ + if (state >= REACTION && numerical_deriv) + { + //jacobian_sums(); + numerical_jacobian(); + } + else /* hmm */ + { + jacobian_sums(); + numerical_jacobian(); + } +/* + * Full matrix with pure phases + */ + if (r == OK || remove_unstable_phases == TRUE) + { + return_kode = ineq(l_kode); + if (return_kode != OK) + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "Ineq had infeasible solution, " + "kode %d, iteration %d\n", return_kode, + iterations)); + } + log_msg(sformatf("Ineq had infeasible solution, " + "kode %d, iteration %d\n", return_kode, + iterations)); + count_infeasible++; + } + if (return_kode == 2) + { + ineq(0); + } + reset(); + } + gammas(mu_x); + if (molalities(FALSE) == ERROR) + { + revise_guesses(); +/* adjust_step_size(); */ + } + if (use.Get_surface_ptr() != NULL && + use.Get_surface_ptr()->Get_dl_type() != cxxSurface::NO_DL && + use.Get_surface_ptr()->Get_related_phases()) + initial_surface_water(); + mb_sums(); + mb_gases(); + mb_ss(); +/* + * Switch bases if necessary + */ + + if (switch_bases() == TRUE) + { + count_basis_change++; + reprep(); + gammas(mu_x); + molalities(TRUE); + if (use.Get_surface_ptr() != NULL && + use.Get_surface_ptr()->Get_dl_type() != cxxSurface::NO_DL && + use.Get_surface_ptr()->Get_related_phases()) + initial_surface_water(); + revise_guesses(); + mb_sums(); + mb_gases(); + mb_ss(); + } +/* debug + species_list_sort(); + sum_species(); + print_species(); + print_exchange(); + print_surface(); + */ + if (stop_program == TRUE) + { + break; + } + } +/* + * Check for stop_program + */ + + if (stop_program == TRUE) + { + break; + } + if (check_residuals() == ERROR) + { + stop_program = TRUE; + break; + } + if (remove_unstable_phases == FALSE && mass_water_switch_save == FALSE + && mass_water_switch == TRUE) + { + log_msg(sformatf( + "\nChanging water switch to FALSE. Iteration %d.\n", + iterations)); + mass_water_switch = FALSE; + continue; + } + if (remove_unstable_phases == FALSE) + break; + if (debug_model == TRUE) + { + output_msg(sformatf( + "\nRemoving unstable phases. Iteration %d.\n", + iterations)); + } + log_msg(sformatf( "\nRemoving unstable phases. Iteration %d.\n", + iterations)); + } + log_msg(sformatf( "\nNumber of infeasible solutions: %d\n", + count_infeasible)); + log_msg(sformatf( "Number of basis changes: %d\n\n", + count_basis_change)); + log_msg(sformatf( "Number of iterations: %d\n\n", iterations)); + debug_model = debug_model_save; + set_forward_output_to_log(FALSE); + unset_inert_moles(); + if (stop_program == TRUE) + { + return (ERROR); + } + return (OK); +} + +#ifdef SKIP +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +adjust_step_size(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Step sizes are cut down if overflow occurs in molalities + */ + pe_step_size_now -= (pe_step_size_now - 1.0) / 2.0; + step_size_now -= (step_size_now - 1.) / 2.0; + if (pe_step_size_now < 1.5) + pe_step_size_now = 1.5; + if (step_size_now < 1.5) + step_size_now = 1.5; + log_msg(sformatf( "\tNew step sizes: %f\t%f\t%d\n", + step_size_now, pe_step_size_now, iterations)); + return (OK); +} +#endif +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_residuals(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Checks for convergence of all equations, prints any nonconvergence + * Sets variable remove_unstable_phases if a phase is present, + * but undersaturated (i.e. aragonite in calcite-saturated solution). + */ + int i, return_value; + LDBLE epsilon; + epsilon = convergence_tolerance; + + return_value = OK; + if (stop_program == TRUE) + { + warning_msg + ("The program has failed to converge to a numerical solution.\n\nThe following equations were not satisfied:"); + /*error_msg("The program has failed to converge to a numerical solution.\n\nThe following equations were not satisfied:", CONTINUE); */ + } + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == MB || x[i]->type == ALK) + { + if (fabs(residual[i]) >= epsilon * x[i]->moles && fabs(residual[i]) > sqrt(fabs(x[i]->moles) * MIN_TOTAL) + && x[i]->moles > MIN_TOTAL /* || stop_program == TRUE */ ) + { + error_string = sformatf( + "%20s has not converged. Total: %e\tCalculated: " + "%e\tResidual: %e\n", x[i]->description, + (double) x[i]->moles, (double) x[i]->f, + (double) residual[i]); + error_msg(error_string, CONTINUE); + if (x[i]->type == ALK) + { + error_msg("Is non-carbonate alkalinity " + "greater than total alkalinity?\n", CONTINUE); + } + return_value = ERROR; + } + } + else if (x[i]->type == SOLUTION_PHASE_BOUNDARY) + { + if (fabs(residual[i]) >= epsilon /* || stop_program == TRUE */ ) + { + error_string = sformatf( + "%20s solution phase boundary has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + else if (x[i]->type == CB) + { + if (fabs(residual[i]) >= + epsilon * mu_x * + mass_water_aq_x /* || stop_program == TRUE */ ) + { + error_string = sformatf( + "%20s Charge balance has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + else if (x[i]->type == MU /*&& pitzer_model == FALSE && sit_model == FALSE*/) + { + if (fabs(residual[i]) >= + epsilon * mu_x * + mass_water_aq_x /* || stop_program == TRUE */ ) + { + error_string = sformatf( + "%20s Ionic strength has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + else if (x[i]->type == AH2O && pitzer_model == FALSE && sit_model == FALSE) + { + if (fabs(residual[i]) >= epsilon /* || stop_program == TRUE */ ) + { + error_string = sformatf( + "%20s Activity of water has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + else if ((x[i]->type == MH + && (pitzer_model == FALSE || pitzer_pe == TRUE))) + { +#define COMBINE + /*#define COMBINE_CHARGE */ +#ifdef COMBINE +#ifndef COMBINE_CHARGE + if (fabs(residual[i]) > + epsilon * (x[i]->moles + 2 * mass_oxygen_unknown->moles)) +#else + if (fabs(residual[i]) > + epsilon * (x[i]->moles + 2 * mass_oxygen_unknown->moles + + charge_balance_unknown->moles)) +#endif +#else + if (fabs(residual[i]) >= + epsilon * x[i]->moles /* || stop_program == TRUE */ ) +#endif + { + error_string = sformatf( + "%20s Mass of hydrogen has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + else if (x[i]->type == MH2O) + { + if (mass_water_switch == TRUE) + continue; + if (fabs(residual[i]) >= + 0.01 * epsilon * x[i]->moles /* || stop_program == TRUE */ ) + { + error_string = sformatf( + "%20s Mass of oxygen has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + else if (x[i]->type == PP) + { + //cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr; + if (comp_ptr->Get_add_formula().size() == 0) + { + if (x[i]->dissolve_only == TRUE) + { + if ((residual[i] > epsilon && x[i]->moles > 0.0) + || + ((residual[i] < -epsilon + && (comp_ptr->Get_initial_moles() - x[i]->moles) > + 0))) + { + log_msg(sformatf( + "%20s Dissolve_only pure phase has not converged. \tResidual: %e\n", + x[i]->description, (double) residual[i])); + } + } + else + { + if ((residual[i] >= epsilon * 100 + && x[i]->moles > 0.0) /* || stop_program == TRUE */ ) + { + remove_unstable_phases = TRUE; + log_msg(sformatf( + "%20s Pure phase has not converged. \tResidual: %e\n", + x[i]->description, (double) residual[i])); + } + else if (residual[i] <= -epsilon) + { + error_string = sformatf( + "%20s Pure phase has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + } + else + { + if ((fabs(residual[i]) >= epsilon + && x[i]->moles > 0.0) /* || stop_program == TRUE */ ) + { + log_msg(sformatf( + "%s, Pure phase has not converged. \tResidual: %e\n", + x[i]->description, (double) residual[i])); + error_string = sformatf( + "%s, Pure phase with add formula has not converged.\n\t SI may be a local minimum." + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + warning_msg(error_string); + } + } + } + else if (x[i]->type == EXCH) + { + if ( /* stop_program == TRUE || */ + (x[i]->moles <= MIN_RELATED_SURFACE + && fabs(residual[i]) > epsilon) + || (x[i]->moles > MIN_RELATED_SURFACE + && (fabs(residual[i]) > epsilon * x[i]->moles))) + { + error_string = sformatf( + "%20s Exchanger mass balance has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + else if (x[i]->type == SURFACE) + { + if (fabs(residual[i]) < ineq_tol && fabs(residual[i]) < 1e-2*x[i]->moles) continue; + if ( /* stop_program == TRUE || */ + (x[i]->moles <= MIN_RELATED_SURFACE + && fabs(residual[i]) > epsilon) + || (x[i]->moles > MIN_RELATED_SURFACE + && (fabs(residual[i]) > epsilon * x[i]->moles))) + { + error_string = sformatf( + "%20s Surface mass balance has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + else if (x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1 + || x[i]->type == SURFACE_CB2) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + if ((charge_ptr->Get_grams() > MIN_RELATED_SURFACE + && fabs(residual[i]) > + epsilon) /* || stop_program == TRUE */ ) + { + error_string = sformatf( + "%20s Surface charge/potential has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + else if (x[i]->type == GAS_MOLES) + { + if (gas_in == FALSE) + continue; + if (residual[i] >= epsilon + || residual[i] <= -epsilon /* || stop_program == TRUE */ ) + { + error_string = sformatf( + "%20s Total moles in gas phase has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + else if (x[i]->type == PITZER_GAMMA) + { + if (fabs(residual[i]) > epsilon) + { + error_string = sformatf( + "%20s log gamma not converged.\tResidual: %e\n", + x[i]->description, (double) residual[i]); + } + } + else if (x[i]->type == SS_MOLES) + { + if (x[i]->ss_in == FALSE) + continue; + if (x[i]->moles <= MIN_TOTAL_SS) + continue; + if (residual[i] >= epsilon + || residual[i] <= -epsilon /* || stop_program == TRUE */ ) + { + error_string = sformatf( + "%20s Total moles in solid solution has not converged. " + "\tResidual: %e\n", x[i]->description, + (double) residual[i]); + error_msg(error_string, CONTINUE); + } + } + } + if (remove_unstable_phases == TRUE) + { + log_msg(sformatf( "%20sRemoving unstable phases, iteration %d.", + " ", iterations)); + } + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +gammas(LDBLE mu) +/* ---------------------------------------------------------------------- */ +{ +/* + * Calculates gammas and [moles * d(ln gamma)/d mu] for all aqueous + * species. + */ + int i, j; + int ifirst, ilast; + LDBLE d1, d2, d3, f, a_llnl, b_llnl, bdot_llnl, log_g_co2, dln_g_co2, c2_llnl; + + LDBLE c1, c2, a, b; + LDBLE muhalf, equiv; + /* Initialize */ + if (mu <= 0) mu = 1e-10; + if (pitzer_model == TRUE) + return gammas_pz(); + if (sit_model == TRUE) + return gammas_sit(); + a_llnl = b_llnl = bdot_llnl = log_g_co2 = dln_g_co2 = c2_llnl = 0; +/* + * compute temperature dependence of a and b for debye-huckel + */ + // a and b are calc'd in calc_dielectrics(tc_x, patm_x); + k_temp(tc_x, patm_x); + a = DH_A; + b = DH_B; + + /* + * LLNL temperature dependence + */ + if (llnl_count_temp > 0) + { + ifirst = 0; + ilast = llnl_count_temp; + if (tc_x < llnl_temp[0] || tc_x > llnl_temp[llnl_count_temp - 1]) + { + error_msg + ("Temperature out of range of LLNL_AQUEOUS_MODEL parameters", + STOP); + } + for (i = 0; i < llnl_count_temp; i++) + { + if (tc_x >= llnl_temp[i]) + ifirst = i; + if (tc_x <= llnl_temp[i]) + { + ilast = i; + break; + } + } + if (ilast == ifirst) + { + f = 1; + } + else + { + f = (tc_x - llnl_temp[ifirst]) / (llnl_temp[ilast] - + llnl_temp[ifirst]); + } + a_llnl = (1 - f) * llnl_adh[ifirst] + f * llnl_adh[ilast]; + b_llnl = (1 - f) * llnl_bdh[ifirst] + f * llnl_bdh[ilast]; + bdot_llnl = (1 - f) * llnl_bdot[ifirst] + f * llnl_bdot[ilast]; + /* + * CO2 activity coefficient + */ + log_g_co2 = + (llnl_co2_coefs[0] + llnl_co2_coefs[1] * tk_x + + llnl_co2_coefs[2] / tk_x) * mu - (llnl_co2_coefs[3] + + llnl_co2_coefs[4] * tk_x) * + (mu / (mu + 1)); + log_g_co2 /= LOG_10; + dln_g_co2 = + (llnl_co2_coefs[0] + llnl_co2_coefs[1] * tk_x + + llnl_co2_coefs[2] / tk_x) - (llnl_co2_coefs[3] + + llnl_co2_coefs[4] * tk_x) * (1 / + ((mu + + 1) * + (mu + + 1))); + } + +/* + * constants for equations + */ + muhalf = sqrt(mu); + c1 = (-a) * LOG_10 * (1.0 / + (2 * muhalf * (muhalf + 1.0) * (muhalf + 1.0)) - + 0.3); + c2 = -a / (2 * muhalf); + if (llnl_count_temp > 0) + { + c2_llnl = -a_llnl / (2 * muhalf); + } + +/* + * Calculate activity coefficients + */ + for (i = 0; i < count_s_x; i++) + { + switch (s_x[i]->gflag) + { + case 0: /* uncharged */ + s_x[i]->lg = s_x[i]->dhb * mu; + s_x[i]->dg = s_x[i]->dhb * LOG_10 * s_x[i]->moles; + break; + case 1: /* Davies */ + s_x[i]->lg = -s_x[i]->z * s_x[i]->z * a * + (muhalf / (1.0 + muhalf) - 0.3 * mu); + s_x[i]->dg = c1 * s_x[i]->z * s_x[i]->z * s_x[i]->moles; + break; + case 2: /* Extended D-H, WATEQ D-H */ + s_x[i]->lg = -a * muhalf * s_x[i]->z * s_x[i]->z / + (1.0 + s_x[i]->dha * b * muhalf) + s_x[i]->dhb * mu; + s_x[i]->dg = (c2 * s_x[i]->z * s_x[i]->z / + ((1.0 + s_x[i]->dha * b * muhalf) * (1.0 + + s_x[i]->dha * + b * muhalf)) + + s_x[i]->dhb) * LOG_10 * s_x[i]->moles; +/* if (mu_x < 1e-6) s_x[i]->dg = 0.0; */ + break; + case 3: /* Always 1.0 */ + s_x[i]->lg = 0.0; + s_x[i]->dg = 0.0; + break; + case 4: /* Exchange */ +/* + * Find CEC + * z contains charge of cation for exchange species, alk contains cec + */ +/* !!!!! */ + for (j = 1; s_x[i]->rxn_x->token[j].s != NULL; j++) + { + if (s_x[i]->rxn_x->token[j].s->type == EX) + { + s_x[i]->alk = + s_x[i]->rxn_x->token[j].s->primary->unknown->moles; + break; + } + } + if (s_x[i]->exch_gflag == 1 && s_x[i]->alk > 0) + { + /* Davies */ + d1 = s_x[i]->lg; + s_x[i]->lg = -s_x[i]->equiv * s_x[i]->equiv * a * + (muhalf / (1.0 + muhalf) - 0.3 * mu) + + log10(fabs(s_x[i]->equiv) / s_x[i]->alk); + if (s_x[i]->a_f && s_x[i]->primary == NULL) + { + d2 = s_x[i]->moles * s_x[i]->equiv / s_x[i]->alk; + if (d2 > 1) d2 = 1; + d2 = s_x[i]->lg - s_x[i]->a_f * (1 - d2); + d3 = 0.89; + if (iterations < 10) d3 = 0.7; else d3 = 0.89; + s_x[i]->lg = d3 * d1 + (1 - d3) * d2; + } + s_x[i]->dg = + c1 * s_x[i]->equiv * s_x[i]->equiv * s_x[i]->moles; + } + else if (s_x[i]->exch_gflag == 2 && s_x[i]->alk > 0) + { + /* Extended D-H, WATEQ D-H */ + d1 = s_x[i]->lg; + s_x[i]->lg = -a * muhalf * s_x[i]->equiv * s_x[i]->equiv / + (1.0 + s_x[i]->dha * b * muhalf) + s_x[i]->dhb * mu + + log10(fabs(s_x[i]->equiv) / s_x[i]->alk); + if (s_x[i]->a_f && s_x[i]->primary == NULL) + { + d2 = s_x[i]->moles * s_x[i]->equiv / s_x[i]->alk; + if (d2 > 1) d2 = 1; + d2 = s_x[i]->lg - s_x[i]->a_f * (1 - d2); + d3 = 0.89; + if (iterations < 10) d3 = 0.7; else d3 = 0.89; + s_x[i]->lg = d3 * d1 + (1 - d3) * d2; + } + s_x[i]->dg = (c2 * s_x[i]->equiv * s_x[i]->equiv / + ((1.0 + s_x[i]->dha * b * muhalf) * (1.0 + + s_x[i]-> + dha * b * + muhalf)) + + s_x[i]->dhb) * LOG_10 * s_x[i]->moles; + } + else if (s_x[i]->exch_gflag == 7 && s_x[i]->alk > 0) + { + if (llnl_count_temp > 0) + { + s_x[i]->lg = + -a_llnl * muhalf * s_x[i]->equiv * s_x[i]->equiv / + (1.0 + s_x[i]->dha * b_llnl * muhalf) + + bdot_llnl * mu + + log10(fabs(s_x[i]->equiv) / s_x[i]->alk); + s_x[i]->dg = + (c2_llnl * s_x[i]->equiv * s_x[i]->equiv / + ((1.0 + s_x[i]->dha * b_llnl * muhalf) * (1.0 + + s_x[i]-> + dha * + b_llnl * + muhalf)) + + bdot_llnl) * LOG_10 * s_x[i]->moles; + } + else + { + error_msg("LLNL_AQUEOUS_MODEL_PARAMETERS not defined.", + STOP); + } + } + else + { +/* + * Master species is a dummy variable with meaningless activity and mass + */ + if (s_x[i]->primary != NULL) + { + s_x[i]->lg = 0.0; + s_x[i]->dg = 0.0; + } + else + { + if (s_x[i]->alk <= 0) + { + s_x[i]->lg = 0.0; + } + else + { + s_x[i]->lg = log10(fabs(s_x[i]->equiv) / s_x[i]->alk); + } + s_x[i]->dg = 0.0; + } + } + break; + case 5: /* Always 1.0 */ + s_x[i]->lg = 0.0; + s_x[i]->dg = 0.0; + break; + case 6: /* Surface */ +/* + * Find moles of sites. + * s_x[i]->equiv is stoichiometric coefficient of sites in species + */ + for (j = 1; s_x[i]->rxn_x->token[j].s != NULL; j++) + { + if (s_x[i]->rxn_x->token[j].s->type == SURF) + { + s_x[i]->alk = + s_x[i]->rxn_x->token[j].s->primary->unknown->moles; + break; + } + } + if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + /* mole fraction */ + equiv = 1.0; + } + else + { + equiv = s_x[i]->equiv; + } + if (s_x[i]->alk > 0) + { + s_x[i]->lg = log10(equiv / s_x[i]->alk); + s_x[i]->dg = 0.0; + } + else + { + s_x[i]->lg = 0.0; + s_x[i]->dg = 0.0; + } + break; + case 7: /* LLNL */ + if (llnl_count_temp > 0) + { + if (s_x[i]->z == 0) + { + s_x[i]->lg = 0.0; + s_x[i]->dg = 0.0; + } + else + { + s_x[i]->lg = -a_llnl * muhalf * s_x[i]->z * s_x[i]->z / + (1.0 + s_x[i]->dha * b_llnl * muhalf) + + bdot_llnl * mu; + s_x[i]->dg = + (c2_llnl * s_x[i]->z * s_x[i]->z / + ((1.0 + s_x[i]->dha * b_llnl * muhalf) * (1.0 + + s_x[i]-> + dha * + b_llnl * + muhalf)) + + bdot_llnl) * LOG_10 * s_x[i]->moles; + break; + } + } + else + { + error_msg("LLNL_AQUEOUS_MODEL_PARAMETERS not defined.", STOP); + } + break; + case 8: /* LLNL CO2 */ + if (llnl_count_temp > 0) + { + s_x[i]->lg = log_g_co2; + s_x[i]->dg = dln_g_co2 * s_x[i]->moles; + } + else + { + error_msg("LLNL_AQUEOUS_MODEL_PARAMETERS not defined.", STOP); + } + break; + case 9: /* activity water */ + s_x[i]->lg = log10(exp(s_h2o->la * LOG_10) * gfw_water); + s_x[i]->dg = 0.0; + break; + } +/* + if (mu_unknown != NULL) { + if (fabs(residual[mu_unknown->number]) > 0.1 && + fabs(residual[mu_unknown->number])/mu_x > 0.5) { + s_x[i]->dg = 0.0; + } + } + */ + } + return (OK); +} +/* ------------------------------------------------------------------------------- */ +int Phreeqc:: +ineq(int in_kode) +/* ------------------------------------------------------------------------------- */ +{ +/* + * Sets up equations and inequalities for Cl1. + * Scales columns if necessary. + * Eliminates equations that are not necessary because + * gas_phase, s_s, or phase equation is not needed + * Mallocs space + * Calls Cl1 + * Rescales results if necessary + */ + int i, j; + int return_code; + int l_count_rows; + int l_count_optimize, count_equal; + int k, l, m, n; + int l_klmd, l_nklmd, l_n2d; + int l_iter; + LDBLE l_error; + LDBLE max; + int l_kode; + LDBLE min; +#ifdef SLNQ + LDBLE *slnq_array; + LDBLE *slnq_delta1; +#endif +/* Debug + if (debug_model == TRUE) { + output_msg(sformatf( "\narray:\n\n"); + array_print(array, count_unknowns, count_unknowns + 1, count_unknowns + 1)); + } + */ +/* + * Special case for removing unstable phases + */ + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + cxxPPassemblageComp * comp_ptr; + + if (remove_unstable_phases == TRUE) + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "\nSolution vector for removing unstable phases:\n")); + } + for (i = 0; i < count_unknowns; i++) + { + + if (x[i]->type == PP) + { + //std::map::iterator it; + //it = pp_assemblage_ptr->Get_pp_assemblage_comps().find(x[i]->pp_assemblage_comp_name); + //assert(it != pp_assemblage_ptr->Get_pp_assemblage_comps().end()); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr; + if (residual[i] > 0e-8 && x[i]->moles > 0 && + //it->second.Get_add_formula().size() == 0 + comp_ptr->Get_add_formula().size() == 0 + && x[i]->dissolve_only == FALSE) + { + /* + * Set mass transfer to all of phase + */ + delta[i] = x[i]->moles; + } + else + { + delta[i] = 0.0; + } + if (debug_model == TRUE) + { + output_msg(sformatf( "%6d %-12.12s %10.2e\n", i, + x[i]->description, (double) delta[i])); + } + } + } + remove_unstable_phases = FALSE; + return (OK); + } +/* + * Pitzer model does not have activity of water //or mu + */ + if (pitzer_model == TRUE || sit_model == TRUE) + { + for (i = 0; i < count_unknowns; i++) + { + if ((x[i]->type == AH2O && full_pitzer == FALSE) || + (x[i]->type == MH && pitzer_model == TRUE && pitzer_pe == FALSE) || + /*x[i]->type == MU ||*/ + (x[i]->type == PITZER_GAMMA && full_pitzer == FALSE)) + { + for (j = 0; j < count_unknowns; j++) + { + array[j * (count_unknowns + 1) + i] = 0.0; + } + for (j = 0; j < count_unknowns + 1; j++) + { + array[i * (count_unknowns + 1) + j] = 0.0; + } + } + } + } + +/* + * Initialize space if necessary + */ + ineq_init(3 * count_unknowns, 3 * count_unknowns); +/* + * Normalize column + */ + space((void **) ((void *) &normal), count_unknowns, &normal_max, + sizeof(LDBLE)); + + for (i = 0; i < count_unknowns; i++) + normal[i] = 1.0; + + + for (i = 0; i < count_unknowns; i++) + { + max = 0.0; + + if (x[i]->type == MB || x[i]->type == ALK || x[i]->type == EXCH + || x[i]->type == SURFACE || x[i]->type == SURFACE_CB + || x[i]->type == SURFACE_CB1 || x[i]->type == SURFACE_CB2) + { + if (x[i]->moles <= MIN_RELATED_SURFACE + && (x[i]->type == EXCH || x[i]->type == SURFACE)) + continue; + for (j = 0; j < count_unknowns; j++) + { + if (x[i]->type == SURFACE && x[j]->type == SURFACE_CB) + continue; + if (x[i]->type == SURFACE_CB1 && x[j]->type == SURFACE_CB2) + continue; + + if (fabs(array[j * (count_unknowns + 1) + i]) > max) + { + max = fabs(array[j * (count_unknowns + 1) + i]); + if (max > min_value) + break; + } + } + if (diagonal_scale == TRUE) + { + if (fabs(array[i * (count_unknowns + 1) + i]) < min_value) + { + max = fabs(array[i * (count_unknowns + 1) + i]); + } + } + + if (max == 0) + { + array[i * (count_unknowns + 1) + i] = 1e-5 * x[i]->moles; + max = fabs(1e-5 * x[i]->moles); + } + } + + if (x[i]->type == MH && (pitzer_model == FALSE || pitzer_pe == TRUE)) + { + /* make absolute value of diagonal at least 1e-12 */ + + min = 1e-12; + min = MIN_TOTAL; + array[x[i]->number * (count_unknowns + 1) + x[i]->number] += min; + if (fabs + (array[x[i]->number * (count_unknowns + 1) + x[i]->number]) < + min) + array[x[i]->number * (count_unknowns + 1) + x[i]->number] = + min; + max = 0.0; + + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != MB && + x[j]->type != SURFACE && + x[j]->type != SURFACE_CB && + x[j]->type != SURFACE_CB1 && + x[j]->type != SURFACE_CB2 && + x[j]->type != EXCH && x[j]->type != MH + && x[j]->type != MH2O) + continue; + if (fabs(array[j * (count_unknowns + 1) + i]) > max) + { + max = fabs(array[j * (count_unknowns + 1) + i]); + if (max > min_value) + break; + } + } + } + + if (max > 0.0 && max < min_value) + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "Scaling column for %s, max= %e\n", + x[i]->description, (double) max)); + } + for (j = 0; j < count_unknowns; j++) + { + array[j * (count_unknowns + 1) + i] *= min_value / max; + } + normal[i] = min_value / max; + } + } + +/* + * Allocate arrays for inequality solver + */ + max_row_count = 2 * count_unknowns + 2; + max_column_count = count_unknowns + 2; + space((void **) ((void *) &ineq_array), max_row_count * max_column_count, + &ineq_array_max, sizeof(LDBLE)); + + space((void **) ((void *) &back_eq), max_row_count, &back_eq_max, + sizeof(int)); + + space((void **) ((void *) &zero), max_row_count, &zero_max, + sizeof(LDBLE)); + zero_double(zero, max_row_count); + + space((void **) ((void *) &res), max_row_count, &res_max, sizeof(LDBLE)); + zero_double(res, max_row_count); + + space((void **) ((void *) &delta1), max_column_count, &delta1_max, + sizeof(LDBLE)); + zero_double(delta1, max_column_count); + +/* + * Copy equations to optimize into ineq_array + */ + l_count_rows = 0; + for (i = 0; i < count_unknowns; i++) + { + if (iterations < aqueous_only) + continue; +/* + * Pure phases + */ + if (x[i]->type == PP) + { + //std::map::iterator it; + //it = pp_assemblage_ptr->Get_pp_assemblage_comps().find(x[i]->pp_assemblage_comp_name); + + /* not in model, ignore */ + if (x[i]->phase->in == FALSE) + continue; + // delay removing phase + if (x[i]->moles > 0.0 || x[i]->f <= 0.0 || iterations == 0 || equi_delay == 0) + { + x[i]->iteration = iterations; + } + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr; + //if (it->second.Get_force_equality()) + if (comp_ptr->Get_force_equality()) + continue; + /* Undersaturated and no mass, ignore */ + if (x[i]->f > 0e-8 && x[i]->moles <= 0 + && iterations >= x[i]->iteration + equi_delay + && comp_ptr->Get_add_formula().size() == 0) + { + continue; + } + else if (x[i]->f < 0e-8 && x[i]->dissolve_only == TRUE + //&& (x[i]->moles - it->second.Get_initial_moles() >= 0)) + && (x[i]->moles - comp_ptr->Get_initial_moles() >= 0)) + { + continue; + } + else + { + /* Copy in saturation index equation (has mass or supersaturated) */ + memcpy((void *) &(ineq_array[l_count_rows * max_column_count]), + (void *) &(array[i * (count_unknowns + 1)]), + (size_t) (count_unknowns + 1) * sizeof(LDBLE)); + back_eq[l_count_rows] = i; + //if (it->second.Get_add_formula().size() == 0 + if (comp_ptr->Get_add_formula().size() == 0 + && x[i]->dissolve_only == FALSE) + { + res[l_count_rows] = 1.0; + } +/* + * If infeasible solution on first attempt, remove constraints on IAP + */ + if (pp_scale != 1) + { + for (j = 0; j < count_unknowns + 1; j++) + { + ineq_array[l_count_rows * max_column_count + j] *= + pp_scale; + } + } + + if (in_kode != 1) + { + res[l_count_rows] = 0.0; + } + l_count_rows++; + } + } + else if (x[i]->type == ALK || x[i]->type == SOLUTION_PHASE_BOUNDARY) + { +/* + * Alkalinity and solution phase boundary + */ + memcpy((void *) &(ineq_array[l_count_rows * max_column_count]), + (void *) &(array[i * (count_unknowns + 1)]), + (size_t) (count_unknowns + 1) * sizeof(LDBLE)); + back_eq[l_count_rows] = i; + l_count_rows++; +/* + * Gas phase + */ + } + else if (x[i]->type == GAS_MOLES && gas_in == TRUE) + { + memcpy((void *) &(ineq_array[l_count_rows * max_column_count]), + (void *) &(array[i * (count_unknowns + 1)]), + (size_t) (count_unknowns + 1) * sizeof(LDBLE)); + back_eq[l_count_rows] = i; + + res[l_count_rows] = 1.0; + if (in_kode != 1) + { + res[l_count_rows] = 0.0; + } + l_count_rows++; +/* + * Solid solution + */ + } + else if (x[i]->type == SS_MOLES && x[i]->ss_in == TRUE) + { + memcpy((void *) &(ineq_array[l_count_rows * max_column_count]), + (void *) &(array[i * (count_unknowns + 1)]), + (size_t) (count_unknowns + 1) * sizeof(LDBLE)); + back_eq[l_count_rows] = i; + res[l_count_rows] = 1.0; + if (in_kode != 1) + { + res[l_count_rows] = 0.0; + } + l_count_rows++; + } + } + l_count_optimize = l_count_rows; +/* + * Copy equality equations into ineq_array + */ + for (i = 0; i < count_unknowns; i++) + { + comp_ptr = NULL; + cxxPPassemblageComp *comp_ptr1 = NULL; + if (x[i]->type == PP) + { + //comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name); + comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr; + } + if (x[i]->type == SURFACE && x[i]->phase_unknown != NULL) + { + comp_ptr = pp_assemblage_ptr->Find(x[i]->phase_unknown->phase->name); + } + if ((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1 + || x[i]->type == SURFACE_CB2) + && x[i - 1]->phase_unknown != NULL) + { + comp_ptr1 = pp_assemblage_ptr->Find(x[i-1]->phase_unknown->phase->name); + } + if (x[i]->type != SOLUTION_PHASE_BOUNDARY && + x[i]->type != ALK && + x[i]->type != GAS_MOLES && x[i]->type != SS_MOLES && + x[i]->type != PITZER_GAMMA + /* && x[i]->type != PP */ + ) + { + if (x[i]->type == PP && !comp_ptr->Get_force_equality()) + continue; + if (x[i]->type == MH && pitzer_model == TRUE && pitzer_pe == FALSE) + continue; + if (mass_water_switch == TRUE && x[i] == mass_oxygen_unknown) + continue; +/* + * Mass balance, CB, MU, AH2O, MH, MH2O, others + */ + if (x[i]->type == EXCH && x[i]->moles <= MIN_RELATED_SURFACE) + continue; + if (x[i]->type == SURFACE && + x[i]->phase_unknown == NULL + && x[i]->moles <= MIN_RELATED_SURFACE) + continue; + if ((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1 + || x[i]->type == SURFACE_CB2) ) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + if (charge_ptr->Get_grams() <= MIN_RELATED_SURFACE) + { + continue; + } + } + if (x[i]->type == SURFACE && x[i]->phase_unknown != NULL && + x[i]->phase_unknown->moles <= MIN_RELATED_SURFACE && + comp_ptr->Get_add_formula().size() == 0) + continue; + if ((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1 + || x[i]->type == SURFACE_CB2) + && x[i - 1]->phase_unknown != NULL) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + if (charge_ptr->Get_grams() <= MIN_RELATED_SURFACE && + comp_ptr1->Get_add_formula().size() == 0) + { + continue; + } + } + memcpy((void *) &(ineq_array[l_count_rows * max_column_count]), + (void *) &(array[i * (count_unknowns + 1)]), + (size_t) (count_unknowns + 1) * sizeof(LDBLE)); + back_eq[l_count_rows] = i; + if (mass_water_switch == TRUE && x[i] == mass_hydrogen_unknown) + { + k = mass_oxygen_unknown->number; + for (j = 0; j < count_unknowns; j++) + { + ineq_array[l_count_rows * max_column_count + j] -= + 2 * array[k * (count_unknowns + 1) + j]; + } + } + l_count_rows++; + } + else if (x[i]->type == PITZER_GAMMA && full_pitzer == TRUE) + { + memcpy((void *) &(ineq_array[l_count_rows * max_column_count]), + (void *) &(array[i * (count_unknowns + 1)]), + (size_t) (count_unknowns + 1) * sizeof(LDBLE)); + back_eq[l_count_rows] = i; + l_count_rows++; + } + } + count_equal = l_count_rows - l_count_optimize; +/* + * Copy inequality constraints into ineq + */ + if (pure_phase_unknown != NULL) + { + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == PP) + { + //comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name); + comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr; + /* not in model, ignore */ + if (x[i]->phase->in == FALSE) + continue; + /* No moles and undersaturated, ignore */ + if (x[i]->moles <= 0.0 && x[i]->f > 0e-8 && + comp_ptr->Get_add_formula().size() == 0) + { + continue; + /* No moles of pure phase present, must precipitate */ + } + else if (x[i]->moles <= 0.0) + { + delta1[i] = -1.0; + } + else if (x[i]->f < 0e-8 && x[i]->dissolve_only == TRUE + && (x[i]->moles - comp_ptr->Get_initial_moles() >= + 0)) + { + continue; + } + else + { + + /* Pure phase is present, force Mass transfer to be <= amount of mineral remaining */ + memcpy((void *) + &(ineq_array[l_count_rows * max_column_count]), + (void *) &(zero[0]), + (size_t) (count_unknowns + 1) * sizeof(LDBLE)); + ineq_array[l_count_rows * max_column_count + i] = 1.0; + ineq_array[l_count_rows * max_column_count + + count_unknowns] = x[i]->moles; + back_eq[l_count_rows] = i; + l_count_rows++; + } + /* Pure phase is present and dissolve_only, force ppt to be <= amount of dissolved so far */ + if (x[i]->dissolve_only == TRUE) + { + memcpy((void *) + &(ineq_array[l_count_rows * max_column_count]), + (void *) &(zero[0]), + (size_t) (count_unknowns + 1) * sizeof(LDBLE)); + ineq_array[l_count_rows * max_column_count + i] = -1.0; + ineq_array[l_count_rows * max_column_count + + count_unknowns] = + comp_ptr->Get_initial_moles() - x[i]->moles; + back_eq[l_count_rows] = i; + l_count_rows++; + } + } + } + } +/* + * Add inequality for mass of oxygen greater than zero + */ + if (pitzer_model || sit_model) + { + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == MH2O) + { + memcpy((void *) &(ineq_array[l_count_rows * max_column_count]), + (void *) &(array[i * (count_unknowns + 1)]), + (size_t) (count_unknowns + 1) * sizeof(LDBLE)); + back_eq[l_count_rows] = i; + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type < PP) + { + ineq_array[l_count_rows * max_column_count + j] = 0.0; + } + else + { + /*ineq_array[l_count_rows*max_column_count + j] = -ineq_array[l_count_rows*max_column_count + j]; */ + } + } + ineq_array[l_count_rows * max_column_count + count_unknowns] = + 0.5 * x[i]->moles; + l_count_rows++; + } + } + } + + +/* + * Hydrogen mass balance is good + */ +/* + * No moles and undersaturated, mass transfer must be zero + */ + if (pure_phase_unknown != NULL) + { + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == PP) + { + //comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name); + comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr; + if ((x[i]->moles <= 0.0 && x[i]->f > 0e-8 && + comp_ptr->Get_add_formula().size() == 0) + || x[i]->phase->in == FALSE) + { + for (j = 0; j < l_count_rows; j++) + { + ineq_array[j * max_column_count + i] = 0.0; + } + } + if (x[i]->dissolve_only == TRUE) + { + if (x[i]->f < 0e-8 + && (x[i]->moles - comp_ptr->Get_initial_moles() >= + 0)) + { + for (j = 0; j < l_count_rows; j++) + { + ineq_array[j * max_column_count + i] = 0.0; + } + } + } + } + } + } +/* + * No moles of exchanger + */ + if (use.Get_exchange_ptr() != NULL + && (use.Get_exchange_ptr()->Get_related_phases() || + use.Get_exchange_ptr()->Get_related_rate())) + { + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == EXCH && x[i]->moles <= 0) + { + for (j = 0; j < l_count_rows; j++) + { + ineq_array[j * max_column_count + i] = 0.0; + } + } + } + } +/* + * No moles of surface + */ + if (use.Get_surface_ptr() != NULL + && (use.Get_surface_ptr()->Get_related_phases() + || use.Get_surface_ptr()->Get_related_rate())) + { + for (i = 0; i < count_unknowns; i++) + { + comp_ptr = NULL; + cxxPPassemblageComp * comp_ptr1 = NULL; + if (x[i]->type == SURFACE && x[i]->phase_unknown != NULL) + { + comp_ptr = pp_assemblage_ptr->Find(x[i]->phase_unknown->phase->name); + } + if ((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1 + || x[i]->type == SURFACE_CB2) + && (x[i - 1]->phase_unknown != NULL)) + { + comp_ptr1 = pp_assemblage_ptr->Find(x[i-1]->phase_unknown->phase->name); + } + LDBLE grams = 0; + if ((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1 + || x[i]->type == SURFACE_CB2) + && x[i - 1]->phase_unknown != NULL) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + grams = charge_ptr->Get_grams(); + } + if ((x[i]->type == SURFACE && x[i]->phase_unknown != NULL && + x[i]->phase_unknown->moles <= MIN_RELATED_SURFACE && + comp_ptr->Get_add_formula().size() == 0) || + ((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1 + || x[i]->type == SURFACE_CB2) + && x[i - 1]->phase_unknown != NULL && + grams <= MIN_RELATED_SURFACE && + comp_ptr1->Get_add_formula().size() == 0)) + { + for (j = 0; j < l_count_rows; j++) + { + ineq_array[j * max_column_count + i] = 0.0; + } + } + } + } +/* + * No moles of surface + */ + if (use.Get_surface_ptr() != NULL) + { + LDBLE grams = 0;; + for (i = 0; i < count_unknowns; i++) + { + if ((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1 + || x[i]->type == SURFACE_CB2) && x[i - 1]->phase_unknown == NULL) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + grams = charge_ptr->Get_grams(); + } + if ((x[i]->type == SURFACE && + x[i]->phase_unknown == NULL && + x[i]->moles <= MIN_RELATED_SURFACE) || + ((x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1 + || x[i]->type == SURFACE_CB2) + && x[i - 1]->phase_unknown == NULL + && grams <= MIN_RELATED_SURFACE)) + { + for (j = 0; j < l_count_rows; j++) + { + ineq_array[j * max_column_count + i] = 0.0; + } + } + } + } +/* + * Moles of gas must be >= zero + */ + if (gas_in == TRUE) + { + for (i = gas_unknown->number; i < count_unknowns; i++) + { + if (x[i]->type == GAS_MOLES) + { + memcpy((void *) &(ineq_array[l_count_rows * max_column_count]), + (void *) &(zero[0]), + (size_t) (count_unknowns + 1) * sizeof(LDBLE)); + ineq_array[l_count_rows * max_column_count + i] = -1.0; + ineq_array[l_count_rows * max_column_count + count_unknowns] = + x[i]->moles; + back_eq[l_count_rows] = i; + l_count_rows++; + } + else + { + break; + } + } + } + else if (use.Get_gas_phase_ptr() != NULL && gas_in == FALSE) + { +/* + * Moles of gas small and sum p < ptotal + */ + i = gas_unknown->number; + for (j = 0; j < l_count_rows; j++) + { + ineq_array[j * max_column_count + i] = 0.0; + } + } +/* + * Phase must be "in" and moles of solid solution must be >= zero + */ + + if (ss_unknown != NULL) + { + for (i = ss_unknown->number; i < count_unknowns; i++) + { + if (x[i]->type != SS_MOLES) + break; + if (x[i]->phase->in == TRUE && x[i]->ss_in == TRUE) + { + memcpy((void *) &(ineq_array[l_count_rows * max_column_count]), + (void *) &(zero[0]), + (size_t) (count_unknowns + 1) * sizeof(LDBLE)); + ineq_array[l_count_rows * max_column_count + i] = 1.0; + ineq_array[l_count_rows * max_column_count + count_unknowns] = + 0.99 * x[i]->moles - MIN_TOTAL_SS; + back_eq[l_count_rows] = i; + l_count_rows++; + } + else + { + for (j = 0; j < l_count_rows; j++) + { + ineq_array[j * max_column_count + i] = 0.0; + } + } + } + } +/* + * Add inequality if total moles of element is less than zero + */ + if (negative_concentrations) + { + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == MB && x[i]->moles < 0.0) + { + memcpy((void *) &(ineq_array[l_count_rows * max_column_count]), + (void *) &(array[i * (count_unknowns + 1)]), + (size_t) (count_unknowns + 1) * sizeof(LDBLE)); + back_eq[l_count_rows] = i; + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type < PP) + { + ineq_array[l_count_rows * max_column_count + j] = 0.0; + } + } + l_count_rows++; + } + } + } +/* + * Zero column for mass of water + */ + if (mass_oxygen_unknown != NULL && mass_water_switch == TRUE) + { + k = mass_oxygen_unknown->number; + for (j = 0; j < l_count_rows + 1; j++) + { + ineq_array[j * max_column_count + k] = 0; + } + } +/* + * Scale column for pure phases + */ + for (i = 0; i < count_unknowns; i++) + { + if ((x[i]->type == PP || x[i]->type == SS_MOLES) + && pp_column_scale != 1.0) + { + for (j = 0; j < l_count_rows; j++) + { + ineq_array[j * max_column_count + i] *= pp_column_scale; + } + normal[i] = pp_column_scale; + } + + } + if (debug_model == TRUE) + { + output_msg(sformatf( "\nA and B arrays:\n\n")); + array_print(ineq_array, l_count_rows, count_unknowns + 1, + max_column_count); + } +/* + * Calculate dimensions + */ + k = l_count_optimize; /* rows in A */ + l = count_equal; /* rows in C */ + m = l_count_rows - l - k; /* rows in E */ + if (m < 0) + m = 0; + + if (debug_model == TRUE) + { + output_msg(sformatf( "k, l, m\t%d\t%d\t%d\n", k, l, m)); + } +#define SHRINK_ARRAY +#ifdef SHRINK_ARRAY + if ((sit_model || pitzer_model) && full_pitzer == FALSE) + { + n = count_unknowns - (int) s_list.size(); + for (int i = 0; i < l_count_rows; i++) + { + for (int j = 0; j < n; j++) + { + ineq_array[i*(n+2) + j] = ineq_array[i*(count_unknowns+2) +j]; + } + //if (i > 0) + //{ + // memcpy((void *) &ineq_array[i*(n+2)], (void *) &ineq_array[i*(count_unknowns+2)], (size_t) (n) * sizeof(LDBLE)); + //} + ineq_array[i*(n+2) + n] = ineq_array[i*(count_unknowns+2) + count_unknowns]; + } + } + else + { + n = count_unknowns; /* columns in A, C, E */ + } +#else + n = count_unknowns; /* columns in A, C, E */ +#endif + l_klmd = max_row_count - 2; + l_nklmd = n + l_klmd; + l_n2d = n + 2; +/* + * Retain constraints on mineral mass transfers, even if infeasible on + * first attempt. + */ + l_kode = 1; + + if (in_kode == 2) + { + l_kode = 1; + } + l_iter = 2*(n + l_count_rows); +/* + * Allocate space for arrays + */ + space((void **) ((void *) &cu), 2 * l_nklmd, &cu_max, sizeof(LDBLE)); + + space((void **) ((void *) &iu), 2 * l_nklmd, &iu_max, sizeof(int)); + + space((void **) ((void *) &is), l_klmd, &is_max, sizeof(int)); + +#ifdef SLNQ + slnq_array = + (LDBLE *) PHRQ_malloc((size_t) count_unknowns * + (count_unknowns + 1) * sizeof(LDBLE)); + if (slnq_array == NULL) + malloc_error(); + for (i = 0; i < k + l; i++) + { + for (j = 0; j <= count_unknowns; j++) + { + slnq_array[i * (count_unknowns + 1) + j] = + ineq_array[i * max_column_count + j]; + } + } + slnq_delta1 = + (LDBLE *) PHRQ_malloc((size_t) max_column_count * sizeof(LDBLE)); + if (slnq_delta1 == NULL) + malloc_error(); + memcpy((void *) &(slnq_delta1[0]), (void *) &(zero[0]), + (size_t) max_column_count * sizeof(LDBLE)); +#endif +/* + * Call CL1 + */ + cl1(k, l, m, n, + l_nklmd, l_n2d, ineq_array, + &l_kode, ineq_tol, &l_iter, delta1, res, &l_error, cu, iu, is, FALSE); +/* Set return_kode */ + if (l_kode == 1) + { + return_code = ERROR; + } + else if (l_kode == 2) + { + return_code = 2; + } + else if (l_kode == 3) + { + return_code = ERROR; + return_code = 2; + warning_msg("Too many iterations in Cl1. Should not have done this."); + } + else + { + return_code = OK; + } +#ifdef SLNQ +/* if (l_kode > 0 && ((k + l) == count_unknowns)) { */ + if (l_kode > 0 && ((k + l) <= count_unknowns)) + { + if (add_trivial_eqns(k + l, count_unknowns, slnq_array) == TRUE) + { + if (debug_model == TRUE) + output_msg(sformatf( "Calling SLNQ, iteration %d\n", + iterations)); + log_msg(sformatf( "Calling SLNQ, iteration %d\n", + iterations)); + if (slnq + (count_unknowns, slnq_array, slnq_delta1, count_unknowns + 1, + debug_model) == OK) + { + memcpy((void *) &(delta1[0]), (void *) &(slnq_delta1[0]), + (size_t) count_unknowns * sizeof(LDBLE)); + if (debug_model == TRUE) + output_msg(sformatf( "Using SLNQ results.\n")); + log_msg(sformatf( "Using SLNQ results.\n")); + return_code = OK; + } + else + { + if (debug_model == TRUE) + output_msg(sformatf( + "Could not use SLNQ results.\n")); + log_msg(sformatf( "Could not use SLNQ results.\n")); + } + } + else + { + log_msg(sformatf( + "Could not call SLNQ, row %d, unknowns %d, iteration %d\n", + k + l, count_unknowns, iterations)); + } + } + else if (l_kode > 0) + { + log_msg(sformatf( "Could not call SLNQ, row %d, unknowns %d\n", + k + l, count_unknowns)); + } +#endif +/* Copy delta1 into delta and scale */ +#ifdef SHRINK_ARRAY + memcpy((void *) &(delta[0]), (void *) &(zero[0]), + (size_t) count_unknowns * sizeof(LDBLE)); +#endif + memcpy((void *) &(delta[0]), (void *) &(delta1[0]), + (size_t) n * sizeof(LDBLE)); + for (i = 0; i < n; i++) + delta[i] *= normal[i]; +/* + * Rescale columns of array + */ + for (i = 0; i < count_unknowns; i++) + { + if (normal[i] != 1.0) + { + for (j = 0; j < count_unknowns; j++) + { + array[j * (count_unknowns + 1) + i] /= normal[i]; + } + } + } + + /* adjust unknowns where cl1 failed */ + if (l_kode != 0) + { + for (i = 0; i < l_count_rows; i++) + { + /* + output_msg(sformatf( "%6d %-12.12s %10.2e\n", i, + x[back_eq[i]]->description, (double) res[i); + */ + j = back_eq[i]; + if (x[j]->type == MB && delta[j] == 0.0 && fabs(res[i]) > ineq_tol) + { + delta[j] = res[i]/fabs(res[i]) * 1; + } + } + } + +/* + * Debug, write results of ineq + */ + + if (debug_model == TRUE) + { + output_msg(sformatf( "kode: %d\titer: %d\terror: %e\n", l_kode, + l_iter, (double) l_error)); + output_msg(sformatf( "\nsolution vector:\n")); + for (i = 0; i < count_unknowns; i++) + { + output_msg(sformatf( "%6d %-12.12s %10.2e", i, + x[i]->description, (double) delta[i])); + if (x[i]->type == PP) + { + output_msg(sformatf( " -SI %10.2e Moles %10.2e", + (double) x[i]->f, (double) x[i]->moles)); + if (x[i]->f < 0e-8 || x[i]->moles > 0.0) + { + output_msg(sformatf( " **")); + } + } + output_msg(sformatf( "\n")); + } + + output_msg(sformatf( "\nresidual vector:\n")); + for (i = 0; i < l_count_rows; i++) + { + output_msg(sformatf( "%6d %-12.12s %10.2e\n", i, + x[back_eq[i]]->description, (double) res[i])); + } + } +#ifdef SLNQ + slnq_array = (LDBL *) free_check_null(slnq_array); + slnq_delta1 = (LDBL *) free_check_null(slnq_delta1); +#endif + return (return_code); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +jacobian_sums(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Fills in jacobian array, uses arrays sum_jacob0, sum_jacob1, and + * sum_jacob2. + */ + int i, j, k; + LDBLE sinh_constant; +/* + * Clear array, note residuals are in array[i, count_unknowns+1] + */ + for (i = 0; i < count_unknowns; i++) + { + array[i] = 0.0; + } + for (i = 1; i < count_unknowns; i++) + { + memcpy((void *) &(array[i * (count_unknowns + 1)]), + (void *) &(array[0]), (size_t) count_unknowns * sizeof(LDBLE)); + } +/* + * Add constant terms + */ + for (k = 0; k < count_sum_jacob0; k++) + { + *sum_jacob0[k].target += sum_jacob0[k].coef; + } +/* + * Add terms with coefficients of 1.0 + */ + for (k = 0; k < count_sum_jacob1; k++) + { + *sum_jacob1[k].target += *sum_jacob1[k].source; + } +/* + * Add terms with coefficients != 1.0 + */ + for (k = 0; k < count_sum_jacob2; k++) + { + *sum_jacob2[k].target += *sum_jacob2[k].source * sum_jacob2[k].coef; + } +/* + * Make final adustments to jacobian array + */ +/* + * Ionic strength + */ + if (mu_unknown != NULL) + { + for (i = 0; i < count_unknowns; i++) + { + // using straight mu equation + array[mu_unknown->number * (count_unknowns + 1) + i] *= 0.5; + } + array[mu_unknown->number * (count_unknowns + 1) + + mu_unknown->number] -= mass_water_aq_x; + } +/* + * Mass of oxygen + */ + if (mass_oxygen_unknown != NULL && mu_unknown != NULL) + { + array[mu_unknown->number * (count_unknowns + 1) + + mass_oxygen_unknown->number] -= mu_x * mass_water_aq_x; + } +/* + * Activity of water + */ + if (ah2o_unknown != NULL) + { + if (dampen_ah2o) + { + // factors for tanh attenuation to make ah2o positive + LDBLE y_sum = ah2o_unknown->f; + LDBLE x_h2o = mass_water_aq_x; + LDBLE a = AH2O_FACTOR; + LDBLE lim = 95.0 - 100.0*a*y_sum/x_h2o; + LDBLE factor = -a*(x_h2o*(-0.5*tanh(lim) - 0.5) + (50.0*a*y_sum - 47.5 * x_h2o) / (cosh(lim)*cosh(lim))) / + (x_h2o*x_h2o); + + for (i = 0; i < count_unknowns; i++) + { + array[ah2o_unknown->number * (count_unknowns + 1) + i] *= factor; + } + // activity of water term + array[ah2o_unknown->number * (count_unknowns + 1) + + ah2o_unknown->number] -= exp(s_h2o->la * LOG_10); + + // mass of water term + if (mass_oxygen_unknown != NULL) + { + array[ah2o_unknown->number * (count_unknowns + 1) + mass_oxygen_unknown->number] -= + a*y_sum*(x_h2o*(0.5*tanh(lim) + 0.5) + (47.5*x_h2o - 50.0*a*y_sum)/(cosh(lim)*cosh(lim))) / + (x_h2o*x_h2o*x_h2o); + } + } + else + { + for (i = 0; i < count_unknowns; i++) + { + array[ah2o_unknown->number * (count_unknowns + 1) + i] *= -AH2O_FACTOR; + } + array[ah2o_unknown->number * (count_unknowns + 1) + ah2o_unknown->number] -= + mass_water_aq_x * exp(s_h2o->la * LOG_10); + if (mass_oxygen_unknown != NULL) + { + array[ah2o_unknown->number * (count_unknowns + 1) + mass_oxygen_unknown->number] -= + (exp(s_h2o->la * LOG_10) - 1) * mass_water_aq_x; + } + } + } + +/* + * Surface charge balance + */ + if (surface_unknown != NULL && dl_type_x == cxxSurface::NO_DL) + { + if (use.Get_surface_ptr()->Get_type() != cxxSurface::CCM) + { + sinh_constant = + //sqrt(8 * EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000) * tk_x * + // 1000); + sqrt(8 * eps_r * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000) * tk_x * + 1000); + for (i = 0; i < count_unknowns; i++) + { + cxxSurfaceCharge *charge_ptr = NULL; + if (x[i]->type == SURFACE_CB) + { + charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + } + if (x[i]->type == SURFACE_CB && charge_ptr->Get_grams() > 0) + { + for (j = 0; j < count_unknowns; j++) + { + array[x[i]->number * (count_unknowns + 1) + j] *= + F_C_MOL / (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()); + } + array[x[i]->number * (count_unknowns + 1) + x[i]->number] -= + sinh_constant * sqrt(mu_x) * + cosh(x[i]->master[0]->s->la * LOG_10); + if (mu_unknown != NULL) + { + array[x[i]->number * (count_unknowns + 1) + + mu_unknown->number] -= + 0.5 * sinh_constant / sqrt(mu_x) * + sinh(x[i]->master[0]->s->la * LOG_10); + } + } + } + } + else + { + for (i = 0; i < count_unknowns; i++) + { + cxxSurfaceCharge *charge_ptr = NULL; + if (x[i]->type == SURFACE_CB) + { + charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + } + if (x[i]->type == SURFACE_CB && charge_ptr->Get_grams() > 0) + { + for (j = 0; j < count_unknowns; j++) + { + array[x[i]->number * (count_unknowns + 1) + j] *= + F_C_MOL / (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()); + } + array[x[i]->number * (count_unknowns + 1) + x[i]->number] -= + charge_ptr->Get_capacitance0() * 2 * R_KJ_DEG_MOL * + tk_x * LOG_10 / F_KJ_V_EQ; + } + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +mb_sums(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Calculates sums of species for calculation of mass balances, charge + * balance. Also calculates saturation indices for solution_phase_boundaries + * and pure_phases. After this routine total calcium calculated from all + * calcium species in solution is stored in x[i]->f. Also calculates + * x[i]->sum for some types of unknowns. Uses arrays sum_mb1 and + * sum_mb1, which are generated in prep and reprep. + */ + int k; +/* + * Clear functions in unknowns + */ + for (k = 0; k < count_unknowns; k++) + { + x[k]->f = 0.0; + x[k]->sum = 0.0; + } +/* + * Add terms with coefficients of 1.0 + */ + for (k = 0; k < count_sum_mb1; k++) + { + *sum_mb1[k].target += *sum_mb1[k].source; +/* { k += 1; k -= 1;} */ + } +/* + * Add terms with coefficients != 1.0 + */ + for (k = 0; k < count_sum_mb2; k++) + { + *sum_mb2[k].target += *sum_mb2[k].source * sum_mb2[k].coef; +/* { k += 1; k -= 1;} */ + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +mb_gases(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Determines whether gas_phase equation is needed + */ + gas_in = FALSE; + if (gas_unknown == NULL || use.Get_gas_phase_ptr() == NULL) + return (OK); + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + if (gas_unknown->f > gas_phase_ptr->Get_total_p() + 1e-7 || + gas_unknown->moles > MIN_TOTAL) + { + gas_in = TRUE; + //patm_x = gas_phase_ptr->Get_total_p(); + + } + } + else + { + if (numerical_fixed_volume && (gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume)) + { + gas_in = TRUE; + } + else + { + gas_in = FALSE; + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +mb_ss(void) +/* ---------------------------------------------------------------------- */ +{ + LDBLE lp, log10_iap, total_moles; + LDBLE iapc, iapb, l_kc, l_kb, lc, lb, xcaq, xbaq, xb, xc; + LDBLE sigmapi_aq, sigmapi_solid; + LDBLE total_p; + struct rxn_token *rxn_ptr; +/* + * Determines whether solid solution equation is needed + */ + if (ss_unknown == NULL || use.Get_ss_assemblage_ptr() == NULL) + return (OK); + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t i = 0; i < ss_ptrs.size(); i++) + { + cxxSS *ss_ptr = ss_ptrs[i]; + total_moles = 0; + for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[j]); + total_moles += comp_ptr->Get_moles(); + } + if (total_moles > 1e-13) + { + ss_ptr->Set_ss_in(true); + } + else if (ss_ptr->Get_a0() != 0.0 || ss_ptr->Get_a1() != 0.0) + { + int l; + struct phase *phase0_ptr = phase_bsearch(ss_ptr->Get_ss_comps()[0].Get_name().c_str(), &l, FALSE); + struct phase *phase1_ptr = phase_bsearch(ss_ptr->Get_ss_comps()[1].Get_name().c_str(), &l, FALSE); + /* + * Calculate IAPc and IAPb + */ + if (phase0_ptr->rxn_x != NULL) + { + log10_iap = 0; + for (rxn_ptr = phase0_ptr->rxn_x->token + 1; + rxn_ptr->s != NULL; rxn_ptr++) + { + log10_iap += rxn_ptr->s->la * rxn_ptr->coef; + } + iapc = exp(log10_iap * LOG_10); + } + else + { + iapc = 1e-99; + } + if (phase1_ptr->rxn_x != NULL) + { + log10_iap = 0; + for (rxn_ptr = phase1_ptr->rxn_x->token + 1; + rxn_ptr->s != NULL; rxn_ptr++) + { + log10_iap += rxn_ptr->s->la * rxn_ptr->coef; + } + iapb = exp(log10_iap * LOG_10); + } + else + { + iapb = 1e-99; + } + /* + * Calculate sigma pi, aq + */ + sigmapi_aq = iapc + iapb; + /* + * Calculate xc,aq and xb, aq + */ + xcaq = iapc / (iapb + iapc); + xbaq = iapb / (iapb + iapc); + /* + * Get Kc and Kb + */ + l_kc = exp(phase0_ptr->lk * LOG_10); + l_kb = exp(phase1_ptr->lk * LOG_10); + /* + * Solve for xb + */ + xb = ss_root(ss_ptr->Get_a0(), ss_ptr->Get_a1(), l_kc, l_kb, xcaq, xbaq); + /* + * Calculate lambdac and lambdab + */ + xc = 1 - xb; + lc = exp((ss_ptr->Get_a0() - ss_ptr->Get_a1() * (-4 * xb + 3)) * xb * xb); + lb = exp((ss_ptr->Get_a0() + ss_ptr->Get_a1() * (4 * xb - 1)) * xc * xc); + /* + * Calculate sigma pi, solid + */ + sigmapi_solid = xb * lb * l_kb + xc * lc * l_kc; + /* + * If Sigma pi, solid < sigma pi, aq, then use eqns + */ + if (sigmapi_solid < sigmapi_aq) + { + ss_ptr->Set_ss_in(true); + } + else + { + ss_ptr->Set_ss_in(false); + } + } + else + { + /* + * Calculate total mole fraction from solution activities + */ + total_p = 0; + for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[j]); + int l; + struct phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + if (phase_ptr->in == TRUE) + { + lp = -phase_ptr->lk; + for (rxn_ptr = phase_ptr->rxn_x->token + 1; + rxn_ptr->s != NULL; rxn_ptr++) + { + lp += rxn_ptr->s->la * rxn_ptr->coef; + } + total_p += exp(lp * LOG_10); + } + } + if (total_p > 1.0) + { + ss_ptr->Set_ss_in(true); + } + else + { + ss_ptr->Set_ss_in(false); + } + } + } + for (int i = ss_unknown->number; i < count_unknowns; i++) + { + if (x[i]->type != SS_MOLES) + break; + //cxxSS *ss_ptr = use.Get_ss_assemblage_ptr()->Find(x[i]->ss_name); + cxxSS *ss_ptr = (cxxSS *) x[i]->ss_ptr; + x[i]->ss_in = ss_ptr->Get_ss_in() ? TRUE : FALSE; + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +molalities(int allow_overflow) +/* ---------------------------------------------------------------------- */ +{ +/* + * Calculates la for master species + * Calculates lm and moles from lk, lg, and la's of master species + * Adjusts lm of h2 and o2. + */ + int i, j; + LDBLE total_g; + struct rxn_token *rxn_ptr; +/* + * la for master species + */ + for (i = 0; i < count_master; i++) + { + if (master[i]->in == REWRITE) + { + master[i]->s->la = master[i]->s->lm + master[i]->s->lg; + } + } + if (dl_type_x != cxxSurface::NO_DL) + { + s_h2o->tot_g_moles = s_h2o->moles; + s_h2o->tot_dh2o_moles = 0.0; + } + for (i = 0; i < count_s_x; i++) + { + if (s_x[i]->type > HPLUS && s_x[i]->type != EX + && s_x[i]->type != SURF) + continue; +/* + * lm and moles for all aqueous species + */ + s_x[i]->lm = s_x[i]->lk - s_x[i]->lg; + for (rxn_ptr = s_x[i]->rxn_x->token + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + s_x[i]->lm += rxn_ptr->s->la * rxn_ptr->coef; + /* + if (isnan(rxn_ptr->s->la)) + { + fprintf(stderr,"molalities la %s %e\n", rxn_ptr->s->name, rxn_ptr->s->la); + } + */ + } + if (s_x[i]->type == EX) + { + s_x[i]->moles = Utilities::safe_exp(s_x[i]->lm * LOG_10); + + } + else if (s_x[i]->type == SURF) + { + s_x[i]->moles = Utilities::safe_exp(s_x[i]->lm * LOG_10); + + } + else + { + s_x[i]->moles = under(s_x[i]->lm) * mass_water_aq_x; + if (s_x[i]->moles / mass_water_aq_x > 100) + { + log_msg(sformatf( "Overflow: %s\t%e\t%e\t%d\n", + s_x[i]->name, + (double) (s_x[i]->moles / mass_water_aq_x), + (double) s_x[i]->lm, iterations)); + + if (iterations >= 0 && allow_overflow == FALSE) + { + return (ERROR); + } + } + + } + } +/* + * other terms for diffuse layer model + */ + if (use.Get_surface_ptr() != NULL + && use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC + && dl_type_x != cxxSurface::NO_DL) + { + calc_all_donnan(); + } + + struct species *s_ptr = NULL; + for (i = 0; i < count_s_x; i++) + { + s_ptr = s_x[i]; + if (s_ptr->type > HPLUS && s_ptr->type != EX && s_ptr->type != SURF) + continue; + if (use.Get_surface_ptr() != NULL && dl_type_x != cxxSurface::NO_DL && s_ptr->type <= HPLUS) + { + total_g = 0.0; + s_ptr->tot_dh2o_moles = 0.0; + for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++) + { + cxxSurfaceCharge & charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j]; + cxxSpeciesDL & dl_ref = s_diff_layer[s_ptr->number][charge_ref.Get_name()]; + cxxSurfDL & surf_dl_ref = charge_ref.Get_g_map()[s_ptr->z]; + + //s_diff_layer[is][charge_ref.Get_name()] = dl_ref + //charge_ref.Get_g_map()[s_ptr->z] = surf_dl +/* + * partially corrected formulation assumes mass of water in diffuse layer + * is insignificant. Excess is calculated on the basis of moles_water_aq_x + * instead of moles_water_bulk. + */ + /* revised eq. 61 */ + dl_ref.Set_g_moles(s_ptr->moles * s_ptr->erm_ddl * + (surf_dl_ref.Get_g() + charge_ref.Get_mass_water() / mass_water_aq_x)); + if (s_ptr->moles > 1e-30) + { + dl_ref.Set_dg_g_moles(s_ptr->dg * dl_ref.Get_g_moles() / s_ptr->moles); + } + + /* + * first term of 63 is summed for all surfaces in + * s_ptr->tot_g_moles. This sum is then used in + * the jacobian for species i + */ + total_g += surf_dl_ref.Get_g() + charge_ref.Get_mass_water() / mass_water_aq_x; + /* revised eq. 63, second term */ + /* g.dg is dg/dx(-2y**2) or dg/d(ln y) */ + dl_ref.Set_dx_moles(s_ptr->moles * s_ptr->erm_ddl * surf_dl_ref.Get_dg()); + /* revised eq. 63, third term */ + dl_ref.Set_dh2o_moles(-s_ptr->moles * s_ptr->erm_ddl * + charge_ref.Get_mass_water() / mass_water_aq_x); + s_ptr->tot_dh2o_moles += dl_ref.Get_dh2o_moles(); + + /* surface related to phase */ + dl_ref.Set_drelated_moles(s_ptr->moles * s_ptr->erm_ddl * charge_ref.Get_specific_area() * + use.Get_surface_ptr()->Get_thickness() / mass_water_aq_x); + } + s_ptr->tot_g_moles = s_ptr->moles * (1 + total_g * s_ptr->erm_ddl); + + /* note that dg is for cb, act water, mu eqns */ + /* dg_total_g for mole balance eqns */ + /* dg_g_moles for surface cb */ + + if (s_ptr->moles > 1e-30) + { + s_ptr->dg_total_g = s_ptr->dg * s_ptr->tot_g_moles / s_ptr->moles; + } + else + { + s_ptr->dg_total_g = 0.0; + } + if (debug_diffuse_layer == TRUE) + { + output_msg(sformatf( "%s\t%e\t%e\n", s_ptr->name, + (double) s_ptr->moles, + (double) s_ptr->tot_g_moles)); + output_msg(sformatf( "\tg\n")); + for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++) + { + cxxSurfaceCharge &charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j]; + output_msg(sformatf( "\t%e", + (double) charge_ref.Get_g_map()[s_ptr->z].Get_g())); + } + output_msg(sformatf( "\n\tg_moles\n")); + for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++) + { + cxxSurfaceCharge &charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j]; + int is = s_ptr->number; + output_msg(sformatf( "\t%e", + (double) s_diff_layer[is][charge_ref.Get_name()].Get_g_moles())); + } + output_msg(sformatf( "\n\tdg\n")); + for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++) + { + cxxSurfaceCharge &charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j]; + output_msg(sformatf( "\t%e", + (double) charge_ref.Get_g_map()[s_ptr->z].Get_dg())); + } + output_msg(sformatf( "\n\tdx_moles\n")); + for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++) + { + int is = s_ptr->number; + cxxSurfaceCharge &charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j]; + output_msg(sformatf( "\t%e", + (double) s_diff_layer[is][charge_ref.Get_name()].Get_dx_moles())); + } + output_msg(sformatf( "\n\tdh2o_moles\t%e\n", + (double) s_ptr->tot_dh2o_moles)); + for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++) + { + cxxSurfaceCharge &charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j]; + int is = s_ptr->number; + output_msg(sformatf( "\t%e", + s_diff_layer[is][charge_ref.Get_name()].Get_dh2o_moles())); + } + output_msg(sformatf( "\n")); + } + } + } + calc_gas_pressures(); + calc_ss_fractions(); + + return (OK); +} +#ifdef SKIP +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +molalities(int allow_overflow) +/* ---------------------------------------------------------------------- */ +{ +/* + * Calculates la for master species + * Calculates lm and moles from lk, lg, and la's of master species + * Adjusts lm of h2 and o2. + */ + int i, j; + LDBLE total_g; + struct rxn_token *rxn_ptr; +/* + * la for master species + */ + for (i = 0; i < count_master; i++) + { + if (master[i]->in == REWRITE) + { + master[i]->s->la = master[i]->s->lm + master[i]->s->lg; + } + } + if (dl_type_x != cxxSurface::NO_DL) + { + s_h2o->tot_g_moles = s_h2o->moles; + s_h2o->tot_dh2o_moles = 0.0; + } + for (i = 0; i < count_s_x; i++) + { + if (s_x[i]->type > HPLUS && s_x[i]->type != EX + && s_x[i]->type != SURF) + continue; +/* + * lm and moles for all aqueous species + */ + s_x[i]->lm = s_x[i]->lk - s_x[i]->lg; + for (rxn_ptr = s_x[i]->rxn_x->token + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + s_x[i]->lm += rxn_ptr->s->la * rxn_ptr->coef; + /* + if (isnan(rxn_ptr->s->la)) + { + fprintf(stderr,"molalities la %s %e\n", rxn_ptr->s->name, rxn_ptr->s->la); + } + */ + } + if (s_x[i]->type == EX) + { + s_x[i]->moles = Utilities::safe_exp(s_x[i]->lm * LOG_10); + + } + else if (s_x[i]->type == SURF) + { + s_x[i]->moles = Utilities::safe_exp(s_x[i]->lm * LOG_10); + + } + else + { + s_x[i]->moles = under(s_x[i]->lm) * mass_water_aq_x; + if (s_x[i]->moles / mass_water_aq_x > 100) + { + log_msg(sformatf( "Overflow: %s\t%e\t%e\t%d\n", + s_x[i]->name, + (double) (s_x[i]->moles / mass_water_aq_x), + (double) s_x[i]->lm, iterations)); + + if (iterations >= 0 && allow_overflow == FALSE) + { + return (ERROR); + } + } + + } + } +/* + * other terms for diffuse layer model + */ + if (use.Get_surface_ptr() != NULL && use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC + && dl_type_x != cxxSurface::NO_DL) + calc_all_donnan(); + + for (i = 0; i < count_s_x; i++) + { + if (s_x[i]->type > HPLUS && s_x[i]->type != EX + && s_x[i]->type != SURF) + continue; + if (use.Get_surface_ptr() != NULL && dl_type_x != cxxSurface::NO_DL + && s_x[i]->type <= HPLUS) + { + total_g = 0.0; + s_x[i]->tot_dh2o_moles = 0.0; + for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++) + { + int is = s_x[i]->number; + cxxSurfaceCharge & charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j]; +/* + * partially corrected formulation assumes mass of water in diffuse layer + * is insignificant. Excess is calculated on the basis of moles_water_aq_x + * instead of moles_water_bulk. + */ + /* revised eq. 61 */ + s_diff_layer[is][charge_ref.Get_name()].Set_g_moles(s_x[i]->moles * s_x[i]->erm_ddl * + (charge_ref.Get_g_map()[s_x[i]->z].Get_g() + + charge_ref.Get_mass_water() / + mass_water_aq_x)); + if (s_x[i]->moles > 1e-30) + { + s_diff_layer[is][charge_ref.Get_name()].Set_dg_g_moles(s_x[i]->dg * s_diff_layer[is][charge_ref.Get_name()].Get_g_moles() / + s_x[i]->moles); + } + + /* + * first term of 63 is summed for all surfaces in + * s_x[i]->tot_g_moles. This sum is then used in + * the jacobian for species i + */ + total_g += + charge_ref.Get_g_map()[s_x[i]->z].Get_g() + + charge_ref.Get_mass_water() / + mass_water_aq_x; + /* revised eq. 63, second term */ + /* g.dg is dg/dx(-2y**2) or dg/d(ln y) */ + s_diff_layer[is][charge_ref.Get_name()].Set_dx_moles( + s_x[i]->moles * s_x[i]->erm_ddl * + charge_ref.Get_g_map()[s_x[i]->z].Get_dg()); + /* revised eq. 63, third term */ + s_diff_layer[is][charge_ref.Get_name()].Set_dh2o_moles( + -s_x[i]->moles * s_x[i]->erm_ddl * + charge_ref.Get_mass_water() / + mass_water_aq_x); + s_x[i]->tot_dh2o_moles += s_diff_layer[is][charge_ref.Get_name()].Get_dh2o_moles(); + + /* surface related to phase */ + s_diff_layer[is][charge_ref.Get_name()].Set_drelated_moles( + s_x[i]->moles * s_x[i]->erm_ddl * + charge_ref.Get_specific_area() * + use.Get_surface_ptr()->Get_thickness() / mass_water_aq_x); + } + s_x[i]->tot_g_moles = + s_x[i]->moles * (1 + total_g /* s_x[i]->erm_ddl */ ); + + /* note that dg is for cb, act water, mu eqns */ + /* dg_total_g for mole balance eqns */ + /* dg_g_moles for surface cb */ + + if (s_x[i]->moles > 1e-30) + { + s_x[i]->dg_total_g = + s_x[i]->dg * s_x[i]->tot_g_moles / s_x[i]->moles; + } + else + { + s_x[i]->dg_total_g = 0.0; + } + if (debug_diffuse_layer == TRUE) + { + output_msg(sformatf( "%s\t%e\t%e\n", s_x[i]->name, + (double) s_x[i]->moles, + (double) s_x[i]->tot_g_moles)); + output_msg(sformatf( "\tg\n")); + for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++) + { + cxxSurfaceCharge &charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j]; + output_msg(sformatf( "\t%e", + (double) charge_ref.Get_g_map()[s_x[i]->z].Get_g())); + } + output_msg(sformatf( "\n\tg_moles\n")); + for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++) + { + cxxSurfaceCharge &charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j]; + int is = s_x[i]->number; + output_msg(sformatf( "\t%e", + (double) s_diff_layer[is][charge_ref.Get_name()].Get_g_moles())); + } + output_msg(sformatf( "\n\tdg\n")); + for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++) + { + cxxSurfaceCharge &charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j]; + output_msg(sformatf( "\t%e", + (double) charge_ref.Get_g_map()[s_x[i]->z].Get_dg())); + } + output_msg(sformatf( "\n\tdx_moles\n")); + for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++) + { + int is = s_x[i]->number; + cxxSurfaceCharge &charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j]; + output_msg(sformatf( "\t%e", + (double) s_diff_layer[is][charge_ref.Get_name()].Get_dx_moles())); + } + output_msg(sformatf( "\n\tdh2o_moles\t%e\n", + (double) s_x[i]->tot_dh2o_moles)); + for (j = 0; j < (int) use.Get_surface_ptr()->Get_surface_charges().size(); j++) + { + cxxSurfaceCharge &charge_ref = use.Get_surface_ptr()->Get_surface_charges()[j]; + int is = s_x[i]->number; + output_msg(sformatf( "\t%e", + s_diff_layer[is][charge_ref.Get_name()].Get_dh2o_moles())); + } + output_msg(sformatf( "\n")); + } + } + } + calc_gas_pressures(); + calc_ss_fractions(); + + return (OK); +} +#endif +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_gas_pressures(void) +/* ---------------------------------------------------------------------- */ +{ + int n_g = 0; + LDBLE lp, V_m = 0; + struct rxn_token *rxn_ptr; + std::vector phase_ptrs; + bool PR = false, pr_done = false; +/* + * moles and partial pressures for gases + */ + if (use.Get_gas_phase_ptr() == NULL) + return (OK); + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME && (gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume) && numerical_fixed_volume) + { + if (iterations > 2) + return calc_fixed_volume_gas_pressures(); + else + return OK; + } + if (iterations > 2 && gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME) + { + gas_phase_ptr->Set_total_moles(0); + } + + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + const cxxGasComp * gas_comp_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + int j; + struct phase *phase_ptr = phase_bsearch(gas_comp_ptr->Get_phase_name().c_str(), &j, FALSE); + if (phase_ptr->in == TRUE) + { + phase_ptrs.push_back(phase_ptr); + if (!PR && phase_ptr->t_c > 0 && phase_ptr->p_c > 0) + PR = true; + n_g++; + } + if (iterations > 2 && gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME) + { + gas_phase_ptr->Set_total_moles(gas_phase_ptr->Get_total_moles() + phase_ptr->moles_x); + } + } + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + if (PR /*&& gas_unknown->gas_phase->total_p > 1 */ && iterations > 0) + { + calc_PR(phase_ptrs, gas_phase_ptr->Get_total_p(), tk_x, 0); + } + } else + { + if (PR) + { + if (gas_phase_ptr->Get_total_moles() > 0) + { + V_m = gas_phase_ptr->Get_volume() / gas_phase_ptr->Get_total_moles(); + if (V_m < 0.016) + { + V_m = 0.016; + } else if (V_m > 1e4) + { + V_m = 1e4; + } + if (V_m < 0.02) + V_m = (8. * gas_phase_ptr->Get_v_m() + V_m) / 9; + else if (V_m < 0.03) + V_m = (6. * gas_phase_ptr->Get_v_m() + V_m) / 7; + else if (V_m < 0.05) + V_m = (4. * gas_phase_ptr->Get_v_m() + V_m) / 5; + else if (V_m < 0.07) + V_m = (2. * gas_phase_ptr->Get_v_m() + V_m) / 3; + else + V_m = (1. * gas_phase_ptr->Get_v_m() + V_m) / 2; + if (iterations > 99 && numerical_fixed_volume == false) + { + //V_m *= 1; /* debug */ + numerical_fixed_volume = true; + //switch_numerical = true; + warning_msg + ("Numerical method failed, switching to numerical derivatives."); + prep(); + //switch_numerical = false; + } + } else + V_m = 1.0; + calc_PR(phase_ptrs, 0, tk_x, V_m); + pr_done = true; + } else + { + gas_phase_ptr->Set_total_p(0); + } + } + + gas_phase_ptr->Set_total_moles(0); + std::vector gas_comps; + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + const cxxGasComp *gas_comp = &(gas_phase_ptr->Get_gas_comps()[i]); + int j; + struct phase *phase_ptr = phase_bsearch(gas_comp->Get_phase_name().c_str(), &j, FALSE); + if (phase_ptr->in == TRUE) + { + lp = -phase_ptr->lk; + for (rxn_ptr = phase_ptr->rxn_x->token + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + lp += rxn_ptr->s->la * rxn_ptr->coef; + } + phase_ptr->p_soln_x = exp(LOG_10 * (lp - phase_ptr->pr_si_f)); + if (!strcmp(phase_ptr->name, "H2O(g)") && phase_ptr->p_soln_x > 90) + phase_ptr->p_soln_x = 90; + + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + phase_ptr->moles_x = phase_ptr->p_soln_x * + gas_unknown->moles / gas_phase_ptr->Get_total_p(); + phase_ptr->fraction_x = + phase_ptr->moles_x / gas_unknown->moles; + } + else + { + if (pr_done) + { + lp = phase_ptr->p_soln_x / gas_phase_ptr->Get_total_p() * + gas_phase_ptr->Get_volume() / V_m; + if (lp > 0) + phase_ptr->moles_x = lp; + if (iterations > 50) + { + lp *= 1.0; /* debug */ + } + } else + { + phase_ptr->moles_x = phase_ptr->p_soln_x * + gas_phase_ptr->Get_volume() / (R_LITER_ATM * tk_x); + gas_phase_ptr->Set_total_p(gas_phase_ptr->Get_total_p() + phase_ptr->p_soln_x); + } + gas_phase_ptr->Set_total_moles(gas_phase_ptr->Get_total_moles() + + phase_ptr->moles_x); + } + } + else + { + phase_ptr->moles_x = 0; + phase_ptr->fraction_x = 0; + } + } + + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME && !PR) + { + /* + * Fixed-volume gas phase reacting with a solution + * Change pressure used in logK to pressure of gas phase + */ + if (gas_phase_ptr->Get_total_p() > 1500) + { + gas_phase_ptr->Set_total_moles(0); + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + const cxxGasComp *gas_comp = &(gas_phase_ptr->Get_gas_comps()[i]); + int j; + struct phase *phase_ptr = phase_bsearch(gas_comp->Get_phase_name().c_str(), &j, FALSE); + if (phase_ptr->in == TRUE) + { + phase_ptr->moles_x *= 1500.0 / gas_phase_ptr->Get_total_p(); + gas_phase_ptr->Set_total_moles(gas_phase_ptr->Get_total_moles() + + phase_ptr->moles_x); + } + } + gas_phase_ptr->Set_total_p(1500.0); + } + } + + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_ss_fractions(void) +/* ---------------------------------------------------------------------- */ +{ + LDBLE moles, n_tot; +/* + * moles and lambdas for solid solutions + */ + if (ss_unknown == NULL) + return (OK); +/* + * Calculate mole fractions and log lambda and derivative factors + */ + if (use.Get_ss_assemblage_ptr() == NULL) + return (OK); + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t i = 0; i < ss_ptrs.size(); i++) + { + cxxSS *ss_ptr = ss_ptrs[i]; + n_tot = 0; + for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[k]); + moles = comp_ptr->Get_moles(); + if (moles < 0) + { + moles = MIN_TOTAL_SS; + comp_ptr->Set_initial_moles(moles); + } + n_tot += moles; + } + ss_ptr->Set_total_moles(n_tot); + for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[k]); + int l; + struct phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + moles = comp_ptr->Get_moles(); + if (moles < 0) + { + moles = MIN_TOTAL_SS; + } + comp_ptr->Set_fraction_x(moles / n_tot); + comp_ptr->Set_log10_fraction_x(log10(moles / n_tot)); + + /* all mb and jacobian items must be in x or phase to be static between models */ + phase_ptr->log10_fraction_x = comp_ptr->Get_log10_fraction_x(); + } + if (ss_ptr->Get_a0() != 0.0 || ss_ptr->Get_a1() != 0) + { + ss_binary(ss_ptr); + } + else + { + ss_ideal(ss_ptr); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +ss_binary(cxxSS *ss_ptr) +/* ---------------------------------------------------------------------- */ +{ + LDBLE nb, nc, n_tot, xb, xc, dnb, dnc, l_a0, l_a1; + LDBLE xb2, xb3, xb4, xc2, xc3; + LDBLE xb1, xc1; +/* + * component 0 is major component + * component 1 is minor component + * xb is the mole fraction of second component (formerly trace) + * xc is the mole fraction of first component (formerly major) +*/ +/* + * Calculate mole fractions and log lambda and derivative factors + */ + n_tot = ss_ptr->Get_total_moles(); + cxxSScomp *comp0_ptr = &(ss_ptr->Get_ss_comps()[0]); + cxxSScomp *comp1_ptr = &(ss_ptr->Get_ss_comps()[1]); + int l; + struct phase *phase0_ptr = phase_bsearch(comp0_ptr->Get_name().c_str(), &l, FALSE); + struct phase *phase1_ptr = phase_bsearch(comp1_ptr->Get_name().c_str(), &l, FALSE); + + nc = comp0_ptr->Get_moles(); + xc = nc / n_tot; + nb = comp1_ptr->Get_moles(); + xb = nb / n_tot; +/* + * In miscibility gap + */ + l_a0 = ss_ptr->Get_a0(); + l_a1 = ss_ptr->Get_a1(); + if (ss_ptr->Get_miscibility() && xb > ss_ptr->Get_xb1() + && xb < ss_ptr->Get_xb2()) + { + xb1 = ss_ptr->Get_xb1(); + xc1 = 1.0 - xb1; + comp0_ptr->Set_fraction_x(xc1); + comp0_ptr->Set_log10_fraction_x(log10(xc1)); + phase0_ptr->log10_fraction_x = + comp0_ptr->Get_log10_fraction_x(); + + comp1_ptr->Set_fraction_x(xb1); + comp1_ptr->Set_log10_fraction_x(log10(xb1)); + phase1_ptr->log10_fraction_x = + comp1_ptr->Get_log10_fraction_x(); + + comp0_ptr->Set_log10_lambda( + xb1 * xb1 * (l_a0 - l_a1 * (3 - 4 * xb1)) / LOG_10); + phase0_ptr->log10_lambda = + comp0_ptr->Get_log10_lambda(); + + comp1_ptr->Set_log10_lambda( + xc1 * xc1 * (l_a0 + l_a1 * (4 * xb1 - 1)) / LOG_10); + phase1_ptr->log10_lambda = + comp1_ptr->Get_log10_lambda(); + + comp0_ptr->Set_dnb(0); + comp0_ptr->Set_dnc(0); + comp1_ptr->Set_dnb(0); + comp1_ptr->Set_dnc(0); + phase0_ptr->dnb = 0; + phase0_ptr->dnc = 0; + phase1_ptr->dnb = 0; + phase1_ptr->dnc = 0; + } + else + { +/* + * Not in miscibility gap + */ + comp0_ptr->Set_fraction_x(xc); + comp0_ptr->Set_log10_fraction_x(log10(xc)); + phase0_ptr->log10_fraction_x = + comp0_ptr->Get_log10_fraction_x(); + + comp1_ptr->Set_fraction_x(xb); + comp1_ptr->Set_log10_fraction_x(log10(xb)); + phase1_ptr->log10_fraction_x = + comp1_ptr->Get_log10_fraction_x(); + + comp0_ptr->Set_log10_lambda( + xb * xb * (l_a0 - l_a1 * (3 - 4 * xb)) / LOG_10); + phase0_ptr->log10_lambda = + comp0_ptr->Get_log10_lambda(); + + comp1_ptr->Set_log10_lambda( + xc * xc * (l_a0 + l_a1 * (4 * xb - 1)) / LOG_10); + phase1_ptr->log10_lambda = + comp1_ptr->Get_log10_lambda(); + + xc2 = xc * xc; + xc3 = xc2 * xc; + xb2 = xb * xb; + xb3 = xb2 * xb; + xb4 = xb3 * xb; + xb4 = xb4; + xc3 = xc3; + + /* used derivation that did not substitute x2 = 1-x1 */ + + /* first component, df1/dn1 */ + dnc = 2 * l_a0 * xb2 + 12 * l_a1 * xc * xb2 + 6 * l_a1 * xb2; + phase0_ptr->dnc = -xb / nc + dnc / n_tot; + + + /* first component, df1/dn2 */ + dnb = + 1 - 2 * l_a0 * xb + 2 * l_a0 * xb2 + 8 * l_a1 * xc * xb - + 12 * l_a1 * xc * xb2 - 2 * l_a1 * xb + 2 * l_a1 * xb2; + phase0_ptr->dnb = dnb / n_tot; + + /* second component, df2/dn1 */ + dnc = + 1 - 2 * l_a0 * xc + 2 * l_a0 * xc2 - 8 * l_a1 * xb * xc + + 12 * l_a1 * xb * xc2 + 2 * l_a1 * xc - 2 * l_a1 * xc2; + phase1_ptr->dnc = dnc / n_tot; + + /* second component, df2/dn2 */ + dnb = 2 * l_a0 * xc2 + 12 * l_a1 * xb * xc2 - 6 * l_a1 * xc2; + phase1_ptr->dnb = -xc / nb + dnb / n_tot; + + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +ss_ideal(cxxSS *ss_ptr) +/* ---------------------------------------------------------------------- */ +{ + LDBLE n_tot, n_tot1; + +/* + * component 0 is major component + * component 1 is minor component + * xb is the mole fraction of second component (formerly trace) + * xc is the mole fraction of first component (formerly major) +*/ +/* + * Calculate mole fractions and log lambda and derivative factors + */ + n_tot = ss_ptr->Get_total_moles(); + +/* + * Ideal solid solution + */ + ss_ptr->Set_dn(1.0 / n_tot); + for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++) + { + cxxSScomp *compk_ptr = &(ss_ptr->Get_ss_comps()[k]); + int l; + struct phase *phasek_ptr = phase_bsearch(compk_ptr->Get_name().c_str(), &l, FALSE); + n_tot1 = 0; + for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++) + { + cxxSScomp *compj_ptr = &(ss_ptr->Get_ss_comps()[j]); + if (j != k) + { + n_tot1 += compj_ptr->Get_moles(); + } + } + compk_ptr->Set_log10_lambda(0); + compk_ptr->Set_log10_lambda(0); + + compk_ptr->Set_dnb(-(n_tot1) / (compk_ptr->Get_moles() * n_tot)); + phasek_ptr->dnb = compk_ptr->Get_dnb(); + + compk_ptr->Set_dn(ss_ptr->Get_dn()); + phasek_ptr->dn = ss_ptr->Get_dn(); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +reset(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Checks deltas (changes to unknowns) to make sure they are reasonable + * Scales deltas if necessary + * Updates unknowns with deltas + */ + + int i; + int converge; + LDBLE up, down; + LDBLE d; + LDBLE factor, f0; + LDBLE sum_deltas; + LDBLE step_up; + LDBLE mu_calc; + LDBLE old_moles; + int warning; +/* + * Calculate interphase mass transfers + */ + cxxPPassemblageComp * comp_ptr; + + step_up = log(step_size_now); + factor = 1.; + + if ((pure_phase_unknown != NULL || ss_unknown != NULL) + && calculating_deriv == FALSE) + { +/* + * Don`t take out more mineral than is present + */ + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == PP || x[i]->type == SS_MOLES) + { + + if (delta[i] < -1e8) + { + delta[i] = -10.; + } + else if (delta[i] > 1e8) + { + delta[i] = 10; + } + if (x[i]->dissolve_only == TRUE) + { + assert (x[i]->type == PP); + //comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name); + comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr; + assert(comp_ptr); + if ((delta[i] < 0.0) + && (-delta[i] > + (comp_ptr->Get_initial_moles() - x[i]->moles))) + { + if ((comp_ptr->Get_initial_moles() - x[i]->moles) != + 0.0) + { + f0 = fabs(delta[i] / + (comp_ptr->Get_initial_moles() - + x[i]->moles)); + if (f0 > factor) + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s, Precipitating too much dissolve_only mineral.\tDelta %e\tCurrent %e\tInitial %e\n", + x[i]->description, + (double) delta[i], + (double) x[i]->moles, + (double) comp_ptr->Get_initial_moles())); + } + factor = f0; + } + } + else + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s, Precipitating dissolve_only mineral.\tDelta %e\n", + x[i]->description, + (double) delta[i])); + } + delta[i] = 0; + } + } + } + if ( /* delta[i] > 0.0 && */ x[i]->moles > 0.0 + && delta[i] > x[i]->moles) + { + f0 = delta[i] / x[i]->moles; + if (f0 > factor) + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s, Removing more than total mineral.\t%f\n", + x[i]->description, (double) f0)); + } + factor = f0; + } + } + else if (delta[i] > 0.0 && x[i]->moles <= 0.0) + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s\tDelta: %e\tMass: %e " + "Dissolving mineral with 0.0 mass.\n ", + x[i]->description, (double) delta[i], + (double) x[i]->moles)); + } + delta[i] = 0.0; + } + else if (x[i]->ss_comp_name != NULL && delta[i] < -x[i]->phase->delta_max) + // Uses delta_max computed in step + // delta_max is the maximum amount of the mineral that could form based + // on the limiting element in the system + { + f0 = -delta[i] / x[i]->phase->delta_max; + if (f0 > factor) + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s, Precipitating too much mineral.\t%f\n", + x[i]->description, (double) f0)); + } + factor = f0; + } + } + } + } + } +/* + * Calculate change in element concentrations due to pure phases and gases + */ + warning = 0; + for (i = 0; i < count_unknowns; i++) + { + + /*if (isnan(delta[i]))*/ + if (!PHR_ISFINITE((double) delta[i])) + { + warning ++; + delta[i] = 0; + } + + } + if (warning > 0) + { + error_string = sformatf( "%d delta equal NaN\n", warning); + warning_msg(error_string); + } + if (pure_phase_unknown != NULL || gas_unknown != NULL + || ss_unknown != NULL) + { + for (i = 0; i < count_unknowns; i++) + { + x[i]->delta = 0.0; + } + + for (i = 0; i < count_sum_delta; i++) + { + *sum_delta[i].target += *sum_delta[i].source * sum_delta[i].coef; + } + +/* + * Apply factor from minerals to deltas + */ + + + for (i = 0; i < count_unknowns; i++) + { + x[i]->delta /= factor; + if (x[i]->type == PP || x[i]->type == SS_MOLES) + delta[i] /= factor; + } + + } + + +/* + * Calc factor for mass balance equations for aqueous unknowns + */ + factor = 1.0; + sum_deltas = 0.0; + for (i = 0; i < count_unknowns; i++) + + { + /* fixes underflow problem on Windows */ + if (delta[i] > 0) + { + sum_deltas += delta[i]; + } + else + { + sum_deltas -= delta[i]; + + } + /*sum_deltas += fabs(delta[i]); */ + if (calculating_deriv == FALSE) + { + up = step_up; + down = up; + if (x[i]->type <= SOLUTION_PHASE_BOUNDARY) + { + up = step_up; + down = 1.3 * up; + } + else if (x[i]->type == MU) + { + up = 100 * mu_x; + down = mu_x; + } + else if (x[i]->type == AH2O) + { + down = up; + if (pitzer_model || sit_model) + { + up = 0.05; + down = -0.03; + } + } + else if (x[i]->type == MH) + { + up = log(pe_step_size_now); + down = 1.3 * up; + } + else if (x[i]->type == MH2O) + { + /* ln gH2O + delta; ln(gH2O*delta); */ + /* + up = log(10.); + down = log(4.); + */ + up = log(1.3); + down = log(1.2); + + } + else if (x[i]->type == PP) + { + continue; + } + else if (x[i]->type == GAS_MOLES) + { + up = 1000. * x[i]->moles; + if (up <= 0.0) + up = 1e-1; + if (up >= 1.0) + up = 1.; + down = x[i]->moles; + } + else if (x[i]->type == SS_MOLES) + { + continue; + } + else if (x[i]->type == EXCH) + { + up = step_up; + down = 1.3 * up; + } + else if (x[i]->type == SURFACE) + { + up = step_up; + down = 1.3 * up; + } + else if (x[i]->type == PITZER_GAMMA) + { + up = step_up; + if (up > 1) + { + up = 0.7; + } + down = 1.3 * up; + } + else if (x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1 + || x[i]->type == SURFACE_CB2) + { + up = step_up; + down = 1.3 * up; + /* + up = 1.3; + down = 1.2; + */ + } + + if (delta[i] > 0.0) + { + f0 = delta[i] / up; + if (f0 > factor) + { + if (debug_model == TRUE) + { + output_msg(sformatf( "%-10.10s\t%f\n", + x[i]->description, (double) f0)); + } + factor = f0; + } + } + else + { + f0 = delta[i] / (-down); + if (f0 > factor) + { + if (debug_model == TRUE) + { + output_msg(sformatf( "%-10.10s\t%f\n", + x[i]->description, (double) f0)); + } + factor = f0; + } + } + } + } + + /*converge=TRUE; */ + + if (debug_model == TRUE) + { + output_msg(sformatf( "\nSum of deltas: %12.6f\n", + (double) sum_deltas)); + output_msg(sformatf( "Factor: %12.4e\n", (double) factor)); + } + factor = 1.0 / factor; + + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type != PP && x[i]->type != SS_MOLES) + delta[i] *= factor; + } + + warning = 0; + for (i = 0; i < count_unknowns; i++) + { + + /*if (isnan(delta[i]))*/ + if (!PHR_ISFINITE((double) delta[i])) + { + warning ++; + delta[i] = 0; + } + + } + if (warning > 0) + { + error_string = sformatf( "%d delta equal NaN after scaling\n", warning); + warning_msg(error_string); + } +/* + * Solution mass balances: MB, ALK, CB, SOLUTION_PHASE_BOUNDARY + */ + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == MB || x[i]->type == ALK || x[i]->type == EXCH + || x[i]->type == SURFACE) + { + /*if ( fabs(delta[i]) >= epsilon ) converge = FALSE; */ + d = delta[i] / LOG_10; + /* surface */ + if (x[i]->type == SURFACE) + { + cxxSurfaceComp *comp_ptr = use.Get_surface_ptr()->Find_comp(x[i]->surface_comp); + old_moles = x[i]->moles; + if (x[i]->phase_unknown != NULL) + { + x[i]->moles = comp_ptr->Get_phase_proportion() * + (x[i]->phase_unknown->moles - + delta[x[i]->phase_unknown->number]); + if (x[i]->phase_unknown->moles - + delta[x[i]->phase_unknown->number] <= + MIN_RELATED_SURFACE) + { + x[i]->moles = 0.0; + if (fabs(x[i]->f) > MIN_RELATED_SURFACE) + { + x[i]->master[0]->s->la -= 5.; + } + } + if (old_moles <= 0 && x[i]->moles > 0) + { + x[i]->master[0]->s->la = log10(x[i]->moles) - 5.; + } + } + else if (comp_ptr->Get_phase_name().size() > 0) + { + /* probably initial surface calculation */ + if (x[i]->moles <= MIN_RELATED_SURFACE) + { + x[i]->moles = 0.0; + if (fabs(x[i]->f) > MIN_RELATED_SURFACE) + { + x[i]->master[0]->s->la -= 5.; + } + } + } + } + /* exch */ + if (x[i]->type == EXCH && x[i]->moles <= MIN_RELATED_SURFACE) + { + x[i]->moles = 0.0; + if (fabs(x[i]->f) > MIN_RELATED_SURFACE) + { + x[i]->master[0]->s->la -= 5.; + } + } + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e " + "%-8s%10.2e\n", x[i]->description, "old la", + (double) x[i]->master[0]->s->la, "new la", + (double) x[i]->master[0]->s->la + (double) d, + "delta", (double) delta[i], "delta/c", (double) d)); + } + x[i]->master[0]->s->la += d; + //if (x[i]->master[0]->s->la < (double) (DBL_MIN_10_EXP + 10)) + // x[i]->master[0]->s->la = (double) (DBL_MIN_10_EXP + 10); + +/* + * Surface charge balance + */ + + } + else if (x[i]->type == SURFACE_CB || x[i]->type == SURFACE_CB1 + || x[i]->type == SURFACE_CB2) + { + d = delta[i] / LOG_10; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + if (x[i]->phase_unknown != NULL) + { + charge_ptr->Set_grams( + (x[i]->phase_unknown->moles - + delta[x[i]->phase_unknown->number])); + if (charge_ptr->Get_grams() <= MIN_RELATED_SURFACE) + { + charge_ptr->Set_grams(0.0); + } + } + if (charge_ptr->Get_grams() <= MIN_RELATED_SURFACE) + { + charge_ptr->Set_grams(0.0); + } + x[i]->related_moles = charge_ptr->Get_grams(); + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e\n", + x[i]->description, "old f*psi", + (double) x[i]->master[0]->s->la, "new f*psi", + (double) x[i]->master[0]->s->la + (double) d, + "delta", (double) d)); + } + + x[i]->master[0]->s->la += d; + + /* recalculate g's for component */ + if (dl_type_x != cxxSurface::NO_DL + && (use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM + || (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC + && x[i]->type == SURFACE_CB2))) + { + if (debug_diffuse_layer == TRUE) + { + output_msg(sformatf( + "\ncharge, old g, new g, dg*delta," + " dg, delta\n")); + } + std::map::iterator jit; + for (jit = charge_ptr->Get_g_map().begin(); jit != charge_ptr->Get_g_map().end(); jit++) + { + if (debug_diffuse_layer == TRUE) + { + output_msg(sformatf( + "\t%12.4e\t%12.4e\t%12.4e\t%12.4e\t%12.4e\n", + + (double) jit->second.Get_g(), + (double) jit->second.Get_g() + + (double) (jit->second.Get_dg() * + delta[i]), + (double) (jit->second.Get_dg() * + delta[i]), + (double) jit->second.Get_dg(), + (double) delta[i])); + } + if (use.Get_surface_ptr()->Get_dl_type() != cxxSurface::DONNAN_DL) + { + jit->second.Set_g(jit->second.Get_g() + + jit->second.Get_dg() * delta[i]); + } + } + if (use.Get_surface_ptr()->Get_dl_type() == cxxSurface::DONNAN_DL) + { + calc_all_donnan(); + } + } + +/* Solution phase boundary */ + } + else if (x[i]->type == SOLUTION_PHASE_BOUNDARY) + { + /*if (fabs(delta[i]) > epsilon) converge=FALSE; */ + d = delta[i] / LOG_10; + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e %-8s%10.2e\n", + x[i]->description, "old la", + (double) x[i]->master[0]->s->la, "new la", + (double) (x[i]->master[0]->s->la + d), "delta", + (double) delta[i], "delta/c", (double) d)); + } + x[i]->master[0]->s->la += d; +/* Charge balance */ + } + else if (x[i]->type == CB) + { + /*if (fabs(delta[i]) > epsilon * mu_x * mass_water_aq_x ) converge=FALSE; */ + d = delta[i] / LOG_10; + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e %-8s%10.2e\n", + x[i]->description, "old la", + (double) x[i]->master[0]->s->la, "new la", + (double) (x[i]->master[0]->s->la + d), "delta", + (double) delta[i], "delta/c", (double) d)); + } + x[i]->master[0]->s->la += d; +/* Ionic strength */ + } + else if (x[i]->type == MU) + { + // using straight ionic strength equation + { + mu_calc = 0.5 * mu_unknown->f / mass_water_aq_x; + } + if (debug_model == TRUE) + { + output_msg(sformatf( "Calculated mu: %e\n", + (double) mu_calc)); + output_msg(sformatf( + "%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e\n", + x[i]->description, "old mu", (double) mu_x, + "new mu", (double) (mu_x + delta[i]), "delta", + (double) delta[i])); + } + d = mu_x + delta[i]; + if (d < 1e-11) + { + delta[i] = sqrt(mu_calc * mu_x) - mu_x; + mu_x = sqrt(mu_calc * mu_x); + } + else + { + mu_x += delta[i]; + } + //if (mu_x <= 1e-8) + //{ + // mu_x = 1e-8; + //} +/* Activity of water */ + } + else if (x[i]->type == AH2O) + { + /*if (pitzer_model == TRUE && full_pitzer == FALSE) continue; */ + /*if (fabs(delta[i]) > epsilon) converge=FALSE; */ + d = delta[i] / LOG_10; + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e %-8s%10.2e\n", + x[i]->description, "old la", + (double) x[i]->master[0]->s->la, "new la", + (double) (x[i]->master[0]->s->la + d), "delta", + (double) delta[i], "delta/c", (double) d)); + } + s_h2o->la += d; + ah2o_x = exp(s_h2o->la * LOG_10); +/* pe */ + } + else if (x[i]->type == MH) + { + /*if (fabs(delta[i]) > epsilon) converge=FALSE; */ + d = delta[i] / LOG_10; + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e %-8s%10.2e\n", + x[i]->description, "old pe", + (double) x[i]->master[0]->s->la, "new pe", + (double) (x[i]->master[0]->s->la + d), "delta", + (double) delta[i], "delta/c", (double) d)); + } + s_eminus->la += d; +/* Mass of water */ + } + else if (x[i]->type == MH2O) + { + if (mass_water_switch == TRUE) + continue; + /*if (fabs(delta[i]) > epsilon * mass_water_aq_x) converge=FALSE; */ + /* ln(gh2o) + delta, log(gh2o) + d, gh2o * 10**d */ + d = exp(delta[i]); + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.2e %-9s%10.2e %-6s%10.2e %-8s%10.2e\n", + x[i]->description, "old MH2O", + (double) mass_water_aq_x, "new MH2O", + (double) (mass_water_aq_x * d), "delta", + (double) delta[i], "10**d/c", (double) d)); + } + mass_water_aq_x *= d; + + mass_water_bulk_x = mass_water_aq_x + mass_water_surfaces_x; + if (debug_model == TRUE && dl_type_x != cxxSurface::NO_DL) + { + output_msg(sformatf( + "mass_water bulk: %e\taq: %e\tsurfaces: %e\n", + (double) mass_water_bulk_x, + (double) mass_water_aq_x, + (double) mass_water_surfaces_x)); + } + x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; +/*appt */ + if (use.Get_surface_ptr() != NULL) + { + if (use.Get_surface_ptr()->Get_debye_lengths() > 0) + x[i]->master[0]->s->moles = mass_water_bulk_x / gfw_water; + } + + if (mass_water_aq_x < 1e-10) + { + error_string = sformatf( + "Mass of water is less than 1e-10 kilogram.\n" + "The aqueous phase may not be stable relative to given masses of minerals."); + warning_msg(error_string); + stop_program = TRUE; + return (TRUE); + } +/* Pure phases */ + } + else if (x[i]->type == PP) + { + //comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name); + comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr; + /*if (fabs(delta[i]) > epsilon) converge=FALSE; */ + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.2e %-9s%10.2e %-6s%10.2e\n", + x[i]->description, "old mass", + (double) x[i]->moles, "new mass", + (double) (x[i]->moles - delta[i]), "delta", + (double) delta[i])); + } + if (equal(x[i]->moles, delta[i], ineq_tol)) + { + x[i]->moles = 0.0; + } + else + { + x[i]->moles -= delta[i]; + } + + if (x[i]->dissolve_only == TRUE) + { + if (equal + (x[i]->moles, comp_ptr->Get_initial_moles(), ineq_tol)) + x[i]->moles = comp_ptr->Get_initial_moles(); + } + /*if (fabs(x[i]->moles) < MIN_RELATED_SURFACE) x[i]->moles = 0.0; */ + } + else if (x[i]->type == GAS_MOLES) + { + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + /*if (fabs(delta[i]) > epsilon) converge=FALSE; */ + /*if (gas_in == TRUE && fabs(residual[i]) > epsilon) converge=FALSE; */ + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.2e %-9s%10.2e %-6s%10.2e\n", + x[i]->description, "old mol", + (double) x[i]->moles, "new mol", + (double) (x[i]->moles + delta[i]), "delta", + (double) delta[i])); + } + x[i]->moles += delta[i]; + if (x[i]->moles < MIN_TOTAL) + x[i]->moles = MIN_TOTAL; + if (x[i] == gas_unknown && gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME && + !calculating_deriv) + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.2e %-9s%10.2e %-6s%10.2e\n", + "Pressure", "old P", + (double) last_patm_x, "new P", + (double) gas_phase_ptr->Get_total_p(), "iter P", + (double) patm_x)); + } + patm_x = gas_phase_ptr->Get_total_p(); + //if (patm_x < 1e-10 && patm_x < p_sat) + //{ + // patm_x = ( 1 * patm_x + p_sat) / 2.0; + //} + if (patm_x > 1500) + patm_x = 1500; + } + last_patm_x = patm_x; + } + else if (x[i]->type == SS_MOLES) + { + + /*if (fabs(delta[i]) > epsilon) converge=FALSE; */ + /*if (x[i]->ss_in == TRUE && fabs(residual[i]) > epsilon) converge=FALSE; */ + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.2e %-9s%10.2e %-6s%10.2e\n", + x[i]->description, "old mol", + (double) x[i]->moles, "new mol", + (double) (x[i]->moles - delta[i]), "delta", + (double) delta[i])); + } + x[i]->moles -= delta[i]; + if (x[i]->moles < MIN_TOTAL_SS && calculating_deriv == FALSE) + x[i]->moles = MIN_TOTAL_SS; + cxxSScomp *comp_ptr = (cxxSScomp *) x[i]->ss_comp_ptr; + comp_ptr->Set_moles(x[i]->moles); +/* Pitzer gamma */ + } + else if (x[i]->type == PITZER_GAMMA) + { + if (full_pitzer == FALSE) + continue; + d = delta[i]; + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.5f %-9s%10.5f %-6s%10.2e %-8s%10.2e\n", + x[i]->description, "old lg", (double) x[i]->s->lg, + "new lg", (double) (x[i]->s->lg + d), "delta", + (double) delta[i], "delta", (double) d)); + } + x[i]->s->lg += d; + } + } +/* + * Reset total molalities in mass balance equations + */ + if (pure_phase_unknown != NULL || gas_unknown != NULL + || ss_unknown != NULL) + { + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == MB || x[i]->type == MH || + x[i]->type == MH2O || + x[i]->type == CB || x[i]->type == EXCH + || x[i]->type == SURFACE) + { + /*if (fabs(x[i]->delta) > epsilon*x[i]->moles) converge = FALSE; */ + if (x[i]->type == SURFACE) + x[i]->delta = 0.0; + if (debug_model == TRUE) + { + output_msg(sformatf( + "%-10.10s %-9s%10.2e %-9s%10.2e %-6s%10.2e\n", + x[i]->description, "old mole", + (double) x[i]->moles, "new mole", + (double) (x[i]->moles + x[i]->delta), "delta", + (double) x[i]->delta)); + } + x[i]->moles += x[i]->delta; + } + } + } + converge = FALSE; + return (converge); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +residuals(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Calculates residuals for all equations + */ + int i, j; + int converge; + + LDBLE l_toler; + LDBLE sum_residual; + LDBLE sinh_constant; + LDBLE sum, sum1; + struct master *master_ptr, *master_ptr1, *master_ptr2; + LDBLE sigmaddl, negfpsirt; + int print_fail; + std::vector cd_psi; + print_fail = FALSE; + sum_residual = 0.0; + sigmaddl = 0; + sum = 0; +/* + * Calculate residuals + */ + converge = TRUE; + l_toler = convergence_tolerance; + + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == MB) + { + residual[i] = x[i]->moles - x[i]->f; + if ((fabs(residual[i]) > l_toler * x[i]->moles && + fabs(residual[i]) > sqrt(fabs(x[i]->moles) * MIN_TOTAL) && + x[i]->moles > MIN_TOTAL) + || + x[i]->moles < 0) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == ALK) + { + residual[i] = x[i]->moles - x[i]->f; + if (fabs(residual[i]) > l_toler * x[i]->moles) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == SOLUTION_PHASE_BOUNDARY) + { + residual[i] = x[i]->f * LOG_10; + if (fabs(residual[i]) > l_toler) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == CB) + { + residual[i] = -x[i]->f; + if (ph_unknown == charge_balance_unknown) + { + residual[i] += x[i]->moles; + } + if (fabs(residual[i]) >= l_toler * mu_x * mass_water_aq_x) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == MU /*&& pitzer_model == FALSE && sit_model == FALSE*/) + { + // Using straight ionic strength equation + { + residual[i] = mass_water_aq_x * mu_x - 0.5 * x[i]->f; + } + if (fabs(residual[i]) > l_toler * mu_x * mass_water_aq_x) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == AH2O) + { + if (dampen_ah2o) + { + // a = 0.017; Y = sum(m(i)); X = Mw (mass of water) + // Aw = (1 - a y/x) 0.5 (tanh(100 (0.95 - ay/x)) + 1) + 0.05 (0.5 (1 - tanh(100(0.95 - ay/x)))) + //residual[i] = exp(s_h2o->la * LOG_10) - ((1.0 - 0.017 * x[i]->f/mass_water_aq_x) * + // 0.5*(tanh(100.0*(0.95 - 0.017*x[i]->f/mass_water_aq_x)) + 1) + + // 0.05*0.5*(1.0 - tanh(100.0*(0.95 - 0.017*x[i]->f/mass_water_aq_x)))) ; + residual[i] = exp(s_h2o->la * LOG_10) - ((1.0 - AH2O_FACTOR * x[i]->f/mass_water_aq_x) * + 0.5*(tanh(100.0*(0.95 - AH2O_FACTOR*x[i]->f/mass_water_aq_x)) + 1) + + 0.05*0.5*(1.0 - tanh(100.0*(0.95 - AH2O_FACTOR*x[i]->f/mass_water_aq_x)))) ; + } + else + { + //residual[i] = mass_water_aq_x * exp(s_h2o->la * LOG_10) - mass_water_aq_x + + // 0.017 * x[i]->f; + residual[i] = mass_water_aq_x * exp(s_h2o->la * LOG_10) - mass_water_aq_x + + AH2O_FACTOR * x[i]->f; + } + + if (pitzer_model || sit_model) + { + residual[i] = pow((LDBLE) 10.0, s_h2o->la) - AW; + if (full_pitzer == FALSE) + { + residual[i] = 0.0; + } + } + if (fabs(residual[i]) > l_toler) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == MH + && (pitzer_model == FALSE || pitzer_pe == TRUE)) + { +#ifdef COMBINE + residual[i] = x[i]->moles - x[i]->f; +#else + residual[i] = (x[i]->moles - 2 * s_h2o->moles) - x[i]->f; + x[i]->f += 2 * s_h2o->moles; +#endif + if (mass_water_switch == TRUE) + { + residual[i] -= + 2 * (mass_oxygen_unknown->moles - mass_oxygen_unknown->f); + } +#ifdef COMBINE +#ifndef COMBINE_CHARGE + if (fabs(residual[i]) > + l_toler * (x[i]->moles + 2 * mass_oxygen_unknown->moles)) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } +#else + if (fabs(residual[i]) > + l_toler * (x[i]->moles + 2 * mass_oxygen_unknown->moles + + charge_balance_unknown->moles)) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } +#endif +#else + if (fabs(residual[i]) > l_toler * x[i]->moles) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } +#endif + } + else if (x[i]->type == MH2O) + { + if (mass_water_switch == TRUE) + continue; +#ifdef COMBINE + residual[i] = x[i]->moles - x[i]->f; +#else + residual[i] = (x[i]->moles - s_h2o->moles) - x[i]->f; + x[i]->f += s_h2o->moles; +#endif + if (fabs(residual[i]) > 0.01 * l_toler * x[i]->moles) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == PP) + { + //cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr; + residual[i] = x[i]->f * LOG_10; + if (comp_ptr->Get_add_formula().size() == 0) + { + if (x[i]->dissolve_only == TRUE) + { + if ((residual[i] > l_toler && x[i]->moles > 0.0) + || (residual[i] < -l_toler + && (comp_ptr->Get_initial_moles() - + x[i]->moles) > 0)) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", + iterations, x[i]->description, i, + residual[i])); + converge = FALSE; + } + } + else + { + if (residual[i] < -l_toler || iterations < 1) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", + iterations, x[i]->description, i, + residual[i])); + converge = FALSE; + } + } + } + else + { + /* if (x[i]->moles > 0.0 && fabs(residual[i]) > l_toler) converge = FALSE; */ + if (residual[i] < -l_toler || iterations < 1) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", + iterations, x[i]->description, i, + residual[i])); + converge = FALSE; + } + } + } + else if (x[i]->type == GAS_MOLES) + { + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME && + (gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume) && numerical_fixed_volume) + { + residual[i] = x[i]->moles - x[i]->phase->moles_x; + } + else + { + residual[i] = gas_phase_ptr->Get_total_p() - x[i]->f; + } + + if (fabs(residual[i]) > l_toler && gas_in == TRUE) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME && + (fabs(last_patm_x - patm_x) > 0.001 || fabs(last_patm_x - gas_phase_ptr->Get_total_p()) > 0.001) + && !calculating_deriv) + { + if (print_fail) + output_msg(sformatf("Failed pressure test %d: %e %e %e\n", iterations, last_patm_x, patm_x, + gas_phase_ptr->Get_total_p())); + converge = FALSE; + } + } + else if (x[i]->type == SS_MOLES) + { + residual[i] = x[i]->f * LOG_10; + if (fabs(residual[i]) > l_toler && x[i]->ss_in == TRUE) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == EXCH) + { + residual[i] = x[i]->moles - x[i]->f; + if (x[i]->moles <= MIN_RELATED_SURFACE) + { + if (fabs(residual[i]) > l_toler) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", + iterations, x[i]->description, i, + residual[i])); + converge = FALSE; + } + } + else if (fabs(residual[i]) > l_toler * x[i]->moles) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == SURFACE) + { + residual[i] = x[i]->moles - x[i]->f; + if (x[i]->moles <= MIN_RELATED_SURFACE) + { + if (fabs(residual[i]) > l_toler) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", + iterations, x[i]->description, i, + residual[i])); + converge = FALSE; + } + } + else if (fabs(residual[i]) < ineq_tol && fabs(residual[i]) < 1e-2*x[i]->moles) + { + } + else if (fabs(residual[i]) > l_toler * x[i]->moles) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == PITZER_GAMMA) + { + if (full_pitzer == FALSE) + continue; + residual[i] = x[i]->s->lg - x[i]->s->lg_pitzer; + if (fabs(residual[i]) > l_toler) + { + /* + fprintf(stderr,"Residuals %d: %s %d %e\n", iterations, x[i]->description, i, residual[i]); + */ + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == SURFACE_CB && use.Get_surface_ptr()->Get_type() == cxxSurface::DDL) + { + /*sinh_constant = 0.1174; */ + sinh_constant = + //sqrt(8 * EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000) * + // tk_x * 1000); + sqrt(8 * eps_r * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000) * + tk_x * 1000); +/* if (x[i]->surface_charge->grams <= MIN_RELATED_SURFACE) { */ + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + if (charge_ptr->Get_grams() == 0) + { + residual[i] = 0.0; + } + else if (dl_type_x != cxxSurface::NO_DL) + { + residual[i] = -x[i]->f; + } + else + { +/* + * sinh_constant is (8 e e0 R T 1000)**1/2 + * = sqrt(8*EPSILON*EPSILON_ZERO*(R_KJ_DEG_MOL*1000)*t_x*1000) + * ~ 0.1174 at 25C + */ + residual[i] = + sinh_constant * sqrt(mu_x) * + sinh(x[i]->master[0]->s->la * LOG_10) - + x[i]->f * F_C_MOL / (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()); + } + if (debug_model == TRUE) + { + output_msg(sformatf( "Charge/Potential\n")); + if (charge_ptr->Get_grams() > 0) + { + output_msg(sformatf( + "\tSum of surface charge %e eq\n", + (double) (x[i]->f + /* F_C_MOL / (x[i]->surface_charge->specific_area * x[i]->surface_charge->grams) */ + ))); + } + else + { + output_msg(sformatf( "\tResidual %e\n", + (double) x[i]->f)); + } + output_msg(sformatf( "\t grams %g\n", + (double) charge_ptr->Get_grams())); + output_msg(sformatf( "\tCharge from potential %e eq\n", + (double) (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams() / F_C_MOL * + sinh_constant * sqrt(mu_x) * + sinh(x[i]->master[0]->s->la * LOG_10)))); + output_msg(sformatf( "\t FPsi/2RT %e\n", + (double) (x[i]->master[0]->s->la * LOG_10))); + output_msg(sformatf( "\t Sinh(FPsi/2RT) %e\n", + sinh(x[i]->master[0]->s->la * LOG_10))); + output_msg(sformatf( "\t Cosh(FPsi/2RT) %e\n", + cosh(x[i]->master[0]->s->la * LOG_10))); + output_msg(sformatf( "\t Sqrt(mu_x) %e\n", + sqrt(mu_x))); + } + if (charge_ptr->Get_grams() > MIN_RELATED_SURFACE + && fabs(residual[i]) > l_toler) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == SURFACE_CB + && use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + if (charge_ptr->Get_grams() == 0) + { + residual[i] = 0.0; + cd_psi.clear(); + cd_psi.push_back(0.0); + cd_psi.push_back(0.0); + cd_psi.push_back(0.0); + } + else + { + /* sum is in moles of charge */ + /*psi = pow(10, x[i]->surface_charge->psi_master->s->la); */ /* = exp(-Fpsi/RT) */ + master_ptr = + surface_get_psi_master(charge_ptr->Get_name().c_str(), + SURF_PSI); + master_ptr1 = + surface_get_psi_master(charge_ptr->Get_name().c_str(), + SURF_PSI1); + master_ptr2 = + surface_get_psi_master(charge_ptr->Get_name().c_str(), + SURF_PSI2); + + cd_psi.clear(); + + cd_psi.push_back(-(master_ptr->s->la * LOG_10) * R_KJ_DEG_MOL * tk_x / + F_KJ_V_EQ); + cd_psi.push_back(-(master_ptr1->s->la * LOG_10) * R_KJ_DEG_MOL * tk_x / + F_KJ_V_EQ); + cd_psi.push_back(-(master_ptr2->s->la * LOG_10) * R_KJ_DEG_MOL * tk_x / + F_KJ_V_EQ); + sum = 0; + for (j = 0; j < x[i]->count_comp_unknowns; j++) + { + sum += + x[i]->comp_unknowns[j]->moles * + x[i]->comp_unknowns[j]->master[0]->s->z; + } + charge_ptr->Set_sigma0( + (x[i]->f + + sum) * F_C_MOL / (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams())); + /* f is in moles */ + /* eqns A-3 */ + residual[i] = + charge_ptr->Get_sigma0() - + charge_ptr->Get_capacitance0() * + (cd_psi[0] - cd_psi[1]); + } + if (charge_ptr->Get_grams() > MIN_RELATED_SURFACE + && fabs(residual[i]) > l_toler) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual A %d: %s %d %e\n", + iterations, x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == SURFACE_CB && use.Get_surface_ptr()->Get_type() == cxxSurface::CCM) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + if (charge_ptr->Get_grams() == 0) + { + residual[i] = 0.0; + } + else if (dl_type_x != cxxSurface::NO_DL) + { + residual[i] = -x[i]->f; + } + else + { + residual[i] = + charge_ptr->Get_capacitance0() * x[i]->master[0]->s->la * 2 * R_KJ_DEG_MOL * + tk_x * LOG_10 / F_KJ_V_EQ - + x[i]->f * F_C_MOL / (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()); + } + if (debug_model == TRUE) + { + output_msg(sformatf( "Charge/Potential\n")); + if (charge_ptr->Get_grams() > 0) + { + output_msg(sformatf( + "\tSum of surface charge %e eq\n", + (double) (x[i]->f + /* F_C_MOL / (x[i]->surface_charge->specific_area * x[i]->surface_charge->grams) */ + ))); + } + else + { + output_msg(sformatf( "\tResidual %e\n", + (double) x[i]->f)); + } + output_msg(sformatf( "\t grams %g\n", + (double) charge_ptr->Get_grams())); + output_msg(sformatf( "\tCharge from potential %e eq\n", + (double) (charge_ptr->Get_capacitance0() * x[i]->master[0]->s->la * 2 * R_KJ_DEG_MOL * + tk_x * LOG_10 / F_KJ_V_EQ))); + output_msg(sformatf( "\t Psi %e\n", + (double) (x[i]->master[0]->s->la * 2 * R_KJ_DEG_MOL * + tk_x * LOG_10 / F_KJ_V_EQ))); + } + if (charge_ptr->Get_grams() > MIN_RELATED_SURFACE + && fabs(residual[i]) > l_toler) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual %d: %s %d %e\n", iterations, + x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == SURFACE_CB1) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + if (charge_ptr->Get_grams() == 0) + { + residual[i] = 0.0; + } + else + { + /* eqns A-4 */ + /*psi = pow(10, x[i]->surface_charge->psi_master1->s->la); */ /* = exp(-Fpsi/RT) */ + charge_ptr->Set_sigma1( + x[i]->f * F_C_MOL / (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams())); + residual[i] = + (charge_ptr->Get_sigma0() + + charge_ptr->Get_sigma1()) - + charge_ptr->Get_capacitance1() * + (cd_psi[1] - cd_psi[2]); + } + if (charge_ptr->Get_grams() > MIN_RELATED_SURFACE + && fabs(residual[i]) > l_toler) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual B %d: %s %d %e\n", + iterations, x[i]->description, i, residual[i])); + converge = FALSE; + } + } + else if (x[i]->type == SURFACE_CB2) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + if (charge_ptr->Get_grams() == 0) + { + residual[i] = 0.0; + } + else if (dl_type_x != cxxSurface::NO_DL) + { + sum = 0; + sum1 = 0; + for (j = 0; j < count_s_x; j++) + { + if (s_x[j]->type == SURF) + { + sum += under(s_x[j]->lm) * s_x[j]->dz[2]; + } + if (s_x[j]->type < H2O) + { + int is = s_x[j]->number; + sum1 += s_x[j]->z * s_x[j]->z * s_diff_layer[is][charge_ptr->Get_name()].Get_g_moles(); + } + } + charge_ptr->Set_sigma2( + sum * F_C_MOL / (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams())); + charge_ptr->Set_sigmaddl( + (x[i]->f - + sum) * F_C_MOL / (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams())); + + residual[i] = + x[i]->f + (charge_ptr->Get_sigma0() + + charge_ptr->Get_sigma1()) * + (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()) / F_C_MOL; + /* residual[i] = sum + (x[i]->surface_charge->sigma0 + x[i]->surface_charge->sigma1) * (x[i]->surface_charge->specific_area * x[i]->surface_charge->grams) / F_C_MOL */ + } + else + { + /* eqns A-6 and A-7 */ + sinh_constant = + //sqrt(8 * EPSILON * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000) * + // tk_x * 1000); + sqrt(8 * eps_r * EPSILON_ZERO * (R_KJ_DEG_MOL * 1000) * + tk_x * 1000); + /* + * sinh_constant is (8 e e0 R T 1000)**1/2 + * = sqrt(8*EPSILON*EPSILON_ZERO*(R_KJ_DEG_MOL*1000)*t_x*1000) + * ~ 0.1174 at 25C + */ + master_ptr2 = + surface_get_psi_master(charge_ptr->Get_name().c_str(), + SURF_PSI2); + negfpsirt = master_ptr2->s->la * LOG_10; + sum = 0; + sum1 = 0; + for (j = 0; j < count_s_x; j++) + { + if (s_x[j]->type < H2O) + { + sum += + under(s_x[j]->lm) * + (exp(s_x[j]->z * negfpsirt) - 1); + sum1 += under(s_x[j]->lm) * s_x[j]->z; + } + } + + /* add fictitious monovalent ion that balances charge */ + //sum += fabs(sum1) * (exp(-sum1 / fabs(sum1) * negfpsirt) - 1); + if (sum1 >= 0) + { + sum += fabs(sum1) * (exp(-negfpsirt) - 1); + } + else + { + sum += fabs(sum1) * (exp(negfpsirt) - 1); + } + + if (sum < 0) + { + sum = -sum; + { + if (print_fail) + output_msg(sformatf( + "Failed Residual C %d: %s %d %e %e\n", + iterations, x[i]->description, i, sum, + l_toler)); + converge = FALSE; + } + /*output_msg(sformatf( "Negative sum, iteration %d\n", iterations)); */ + } + charge_ptr->Set_sigma2( + x[i]->f * F_C_MOL / (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams())); + if ((negfpsirt) < 0) + { + sigmaddl = -0.5 * sinh_constant * sqrt(sum); + } + else + { + sigmaddl = 0.5 * sinh_constant * sqrt(sum); + } + charge_ptr->Set_sigmaddl(sigmaddl); + residual[i] = + (charge_ptr->Get_sigma0() + + charge_ptr->Get_sigma1() + + charge_ptr->Get_sigma2()) + sigmaddl; + } + if (debug_model == TRUE) + { + master_ptr = + surface_get_psi_master(charge_ptr->Get_name().c_str(), + SURF_PSI); + master_ptr1 = + surface_get_psi_master(charge_ptr->Get_name().c_str(), + SURF_PSI1); + master_ptr2 = + surface_get_psi_master(charge_ptr->Get_name().c_str(), + SURF_PSI2); + output_msg(sformatf( "CD_music Charge/Potential 2\n")); + output_msg(sformatf( "\tgrams %g\n", + (double) charge_ptr->Get_grams())); + output_msg(sformatf( "\tCapacitances %g\t%g\n", + (double) charge_ptr->Get_capacitance0(), + charge_ptr->Get_capacitance1())); + output_msg(sformatf( "\t-F/(RT) %g\n", + (double) -F_KJ_V_EQ / (R_KJ_DEG_MOL * tk_x))); + output_msg(sformatf( "\tResidual 0 %14e\n", + (double) residual[master_ptr->unknown->number])); + output_msg(sformatf( "\tResidual 1 %14e\n", + (double) residual[master_ptr1->unknown->number])); + output_msg(sformatf( "\tResidual 2 %14e\n", + (double) residual[master_ptr2->unknown->number])); + output_msg(sformatf( "\texp(-FPsi0/RT) %14e", + (double) pow((LDBLE) 10., master_ptr->s->la))); + output_msg(sformatf( "\tPsi0 %14e\n", + (double) cd_psi[0])); + output_msg(sformatf( "\texp(-FPsi1/RT) %14e", + (double) pow((LDBLE) 10., master_ptr1->s->la))); + output_msg(sformatf( "\tPsi1 %14e\n", + (double) cd_psi[1])); + output_msg(sformatf( "\texp(-FPsi2/RT) %14e", + (double) pow((LDBLE) 10., master_ptr2->s->la))); + output_msg(sformatf( "\tPsi2 %14e\n", + (double) cd_psi[2])); + output_msg(sformatf( "\tf 0 %14e", + (double) master_ptr->unknown->f)); + output_msg(sformatf( "\tsigma 0 %14e\n", + (double) charge_ptr->Get_sigma0())); + output_msg(sformatf( "\tf 1 %14e", + (double) master_ptr1->unknown->f)); + output_msg(sformatf( "\tsigma 1 %14e\n", + (double) charge_ptr->Get_sigma1())); + output_msg(sformatf( "\tf 2 %14e", + (double) master_ptr2->unknown->f)); + output_msg(sformatf( "\tsigma 2 %14e\n", + (double) charge_ptr->Get_sigma2())); + output_msg(sformatf( "\tsigma ddl %14e\n", + (double) sigmaddl)); + output_msg(sformatf( "\texp sum %14e\n", + (double) sum)); + + } + if (charge_ptr->Get_grams() > MIN_RELATED_SURFACE + && fabs(residual[i]) > l_toler) + { + if (print_fail) + output_msg(sformatf( + "Failed Residual D %d: %s %d %e\n", + iterations, x[i]->description, i, residual[i])); + converge = FALSE; + } + } +/* + * Store residuals in array + */ + array[(i + 1) * (count_unknowns + 1) - 1] = residual[i]; + sum_residual += fabs(residual[i]); + } +/* + * Return + */ + if ((pitzer_model == TRUE || sit_model == TRUE) && iterations < 1) + return (OK); + if (converge == TRUE) + { + return (CONVERGED); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set(int initial) +/* ---------------------------------------------------------------------- */ +{ +/* + * Sets initial guesses for unknowns if initial == TRUE + * Revises guesses whether initial is true or not + */ + int i; + cxxSolution *solution_ptr; +/* + * Set initial log concentrations to zero + */ + if (pitzer_model == TRUE) + return (set_pz(initial)); + if (sit_model == TRUE) + return (set_sit(initial)); + iterations = -1; + solution_ptr = use.Get_solution_ptr(); + for (i = 0; i < count_s_x; i++) + { + s_x[i]->lm = LOG_ZERO_MOLALITY; + s_x[i]->lg = 0.0; + } +/* + * Set master species activities + */ + + tc_x = solution_ptr->Get_tc(); + tk_x = tc_x + 273.15; + + patm_x = solution_ptr->Get_patm(); // done in calc_rho_0(tc, pa) + potV_x = solution_ptr->Get_potV(); + +/* + * H+, e-, H2O + */ + mass_water_aq_x = solution_ptr->Get_mass_water(); + mu_x = solution_ptr->Get_mu(); + s_h2o->moles = mass_water_aq_x / gfw_water; + s_h2o->la = log10(solution_ptr->Get_ah2o()); + s_hplus->la = -solution_ptr->Get_ph(); + s_hplus->lm = s_hplus->la; + s_hplus->moles = exp(s_hplus->lm * LOG_10) * mass_water_aq_x; + s_eminus->la = -solution_ptr->Get_pe(); + if (initial == TRUE) + initial_guesses(); + if (dl_type_x != cxxSurface::NO_DL) + initial_surface_water(); + revise_guesses(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +initial_guesses(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Make initial guesses for activities of master species and + * ionic strength + */ + int i; + cxxSolution *solution_ptr; + + solution_ptr = use.Get_solution_ptr(); + mu_x = + s_hplus->moles + + exp((solution_ptr->Get_ph() - 14.) * LOG_10) * mass_water_aq_x; + mu_x /= mass_water_aq_x; + s_h2o->la = 0.0; + for (i = 0; i < count_unknowns; i++) + { + if (x[i] == ph_unknown || x[i] == pe_unknown) + continue; + if (x[i]->type < CB) + { + mu_x += + x[i]->moles / mass_water_aq_x * 0.5 * x[i]->master[0]->s->z * + x[i]->master[0]->s->z; + x[i]->master[0]->s->la = log10(x[i]->moles / mass_water_aq_x); + } + else if (x[i]->type == CB) + { + x[i]->master[0]->s->la = + log10(0.001 * x[i]->moles / mass_water_aq_x); + } + else if (x[i]->type == SOLUTION_PHASE_BOUNDARY) + { + x[i]->master[0]->s->la = + log10(0.001 * x[i]->moles / mass_water_aq_x); + } + else if (x[i]->type == EXCH) + { + if (x[i]->moles <= 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else + { + x[i]->master[0]->s->la = log10(x[i]->moles); + } + } + else if (x[i]->type == SURFACE) + { + if (x[i]->moles <= 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else + { + x[i]->master[0]->s->la = log10(0.1 * x[i]->moles); + } + } + else if (x[i]->type == SURFACE_CB) + { + x[i]->master[0]->s->la = 0.0; + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +revise_guesses(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Revise la's of master species + */ + int i; + int l_iter, max_iter, repeat, fail; + LDBLE weight, f; + LDBLE d; + + max_iter = 10; + gammas(mu_x); + l_iter = 0; + repeat = TRUE; + fail = FALSE;; + while (repeat == TRUE) + { + l_iter++; + if (debug_set == TRUE) + { + output_msg(sformatf( "\nBeginning set iteration %d.\n", + l_iter)); + } + if (l_iter == max_iter + 1) + { + log_msg(sformatf( + "Did not converge in set, iteration %d.\n", + iterations)); + fail = TRUE; + } + if (l_iter > 2 * max_iter) + { + log_msg(sformatf( + "Did not converge with relaxed criteria in set.\n")); + return (OK); + } + molalities(TRUE); + mb_sums(); + if (state < REACTION) + { + sum_species(); + } + else + { + for (i = 0; i < count_unknowns; i++) + { + x[i]->sum = x[i]->f; + } + } +/* debug + if (debug_set == TRUE) { + pr.species = TRUE; + pr.all = TRUE; + print_species(); + } + */ + repeat = FALSE; + for (i = 0; i < count_unknowns; i++) + { + if (x[i] == ph_unknown || x[i] == pe_unknown) + continue; + if (x[i]->type == MB || +/* x[i]->type == ALK || */ + x[i]->type == CB || + x[i]->type == SOLUTION_PHASE_BOUNDARY || + x[i]->type == EXCH || x[i]->type == SURFACE) + { + + if (debug_set == TRUE) + { + output_msg(sformatf( + "\n\t%5s at beginning of set %d: %e\t%e\t%e\n", + x[i]->description, l_iter, (double) x[i]->sum, + (double) x[i]->moles, + (double) x[i]->master[0]->s->la)); + } + if (fabs(x[i]->moles) < 1e-30) + x[i]->moles = 0; + + f = fabs(x[i]->sum); + /*if (isnan(f) || !_finite(f))*/ + if (!PHR_ISFINITE((double) f)) + { + f = 0; + } + if (f == 0 && x[i]->moles == 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + continue; + } + else if (f == 0) + { + repeat = TRUE; + x[i]->master[0]->s->la += 5; + if (x[i]->master[0]->s->la < -999.) + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else if (fail == TRUE && f < 1.5 * fabs(x[i]->moles)) + { + continue; + } + else if (f > 1.5 * fabs(x[i]->moles) + || f < 1e-5 * fabs(x[i]->moles)) + { + weight = (f < 1e-5 * fabs(x[i]->moles)) ? 0.3 : 1.0; + if (x[i]->moles <= 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else + { + repeat = TRUE; + d = 0; +#ifdef SKIP + d = weight * log10(fabs(x[i]->moles / x[i]->sum)); + double d1 = d; +#else + // avoid underflows and overflows + if (x[i]->moles > 1e101 || x[i]->moles < 1e-101 || + x[i]->sum > 1e101 || x[i]->sum < 1e-101) + { + LDBLE d1 = log10(x[i]->moles); + LDBLE d2 = log10(x[i]->sum); + LDBLE d3 = d1 - d2; + if (d3 > DBL_MAX_10_EXP/2) + { + d = pow(10.0, DBL_MAX_10_EXP/2.); + } + else if (d3 < DBL_MIN_10_EXP/2.) + { + d = pow(10.0, DBL_MIN_10_EXP/2.); + } + } + else + { + d = fabs(x[i]->moles / x[i]->sum); + } + LDBLE d1; + if (d > 0) + { + d1 = weight * log10(d); + if (PHR_ISFINITE((double) d1) /*&& d1 < 5.0*/) + { + x[i]->master[0]->s->la += d1; + } + else + { + warning_msg("Adjustment to la in revise_guesses was NaN\n"); + x[i]->master[0]->s->la += 5.0; + } + } + else + { + x[i]->master[0]->s->la += 5.0; + } +#endif + } + if (debug_set == TRUE) + { + output_msg(sformatf( + "\t%5s not converged in set %d: %e\t%e\t%e\n", + x[i]->description, l_iter, + (double) x[i]->sum, (double) x[i]->moles, + (double) x[i]->master[0]->s->la)); + } + } + } + else if (x[i]->type == ALK) + { + f = total_co2; + if (fail == TRUE && f < 1.5 * fabs(x[i]->moles)) + { + continue; + } + if (f > 1.5 * fabs(x[i]->moles) + || f < 1e-5 * fabs(x[i]->moles)) + { + repeat = TRUE; + weight = (f < 1e-5 * fabs(x[i]->moles)) ? 0.3 : 1.0; + x[i]->master[0]->s->la += weight * + log10(fabs(x[i]->moles / x[i]->sum)); + if (debug_set == TRUE) + { + output_msg(sformatf( + "%s not converged in set. %e\t%e\t%e\n", + x[i]->description, (double) x[i]->sum, + (double) x[i]->moles, + (double) x[i]->master[0]->s->la)); + } + } + } + } + } + log_msg(sformatf( "Iterations in revise_guesses: %d\n", l_iter)); + mu_x = mu_unknown->f * 0.5 / mass_water_aq_x; + if (mu_x <= 1e-8) + { + mu_x = 1e-8; + } + gammas(mu_x); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +sum_species(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Calculates total alk, total carbon, total co2, electrical balance, + * total hydrogen, and total oxygen. + * + * Sorts species for summing and printing based on valence state and + * concentrations. + * + * Sums total valence states and stores in master[i]->total. + */ + int i, j; + struct master *master_ptr; +/* + * Set global variables + */ + ph_x = -s_hplus->la; + solution_pe_x = -s_eminus->la; + ah2o_x = exp(s_h2o->la * LOG_10); + + density_x = 1.0; + if (s_o2 != NULL) + s_o2->moles = under(s_o2->lm) * mass_water_aq_x; + if (s_h2 != NULL) + s_h2->moles = under(s_h2->lm) * mass_water_aq_x; + +/* + * Calculate sums + */ + total_alkalinity = 0.0; + total_carbon = 0.0; + total_co2 = 0.0; + cb_x = 0.0; + total_ions_x = 0.0; + total_o_x = 0.0; + total_h_x = 0.0; + for (i = 0; i < count_s_x; i++) + { + if (s_x[i]->type == EX) + continue; + if (s_x[i]->type == SURF) + continue; + cb_x += s_x[i]->z * s_x[i]->moles; + total_ions_x += fabs(s_x[i]->z * s_x[i]->moles); + total_alkalinity += s_x[i]->alk * s_x[i]->moles; + total_carbon += s_x[i]->carbon * s_x[i]->moles; + total_co2 += s_x[i]->co2 * s_x[i]->moles; + + total_h_x += s_x[i]->h * s_x[i]->moles; + total_o_x += s_x[i]->o * s_x[i]->moles; + + if (use.Get_surface_ptr() != NULL) + { + if (use.Get_surface_ptr()->Get_debye_lengths() > 0 && state >= REACTION + && s_x[i]->type == H2O) + { + total_h_x -= 2 * mass_water_surfaces_x / gfw_water; + total_o_x -= mass_water_surfaces_x / gfw_water; + } + } + } +/* + * Sum valence states, put in master->total + */ + for (i = 0; i < count_master; i++) + { + master[i]->total = 0.0; + master[i]->total_primary = 0.0; + } + for (i = 0; i < count_species_list; i++) + { + if (species_list[i].master_s->secondary != NULL) + { + master_ptr = species_list[i].master_s->secondary; + } + else + { + master_ptr = species_list[i].master_s->primary; + } + master_ptr->total += species_list[i].s->moles * species_list[i].coef; + } +/* + * Calculate mass-balance sums + */ + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == MB || + x[i]->type == SOLUTION_PHASE_BOUNDARY || + x[i]->type == EXCH || + x[i]->type == SURFACE || + (x[i]->type == CB && x[i] != ph_unknown && x[i] != pe_unknown)) + { + x[i]->sum = 0.0; + for (j = 0; x[i]->master[j] != NULL; j++) + { + x[i]->sum += x[i]->master[j]->total; + } + } + else if (x[i]->type == ALK) + { + x[i]->sum = total_co2; + } + } +/* + * Calculate total element concentrations + */ + for (i = 0; i < count_master; i++) + { + master[i]->elt->primary->total_primary += master[i]->total; + } + /* + * Calculate isotope ratios + */ + calculate_values(); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +surface_model(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Use extra iterative loop to converge on g_factors + */ + int i, debug_diffuse_layer_save, debug_model_save; + cxxSolution *solution_ptr; + LDBLE prev_aq_x; +/* + * Allocate space for g factors for diffuse layer in surface complexation + */ + debug_diffuse_layer_save = debug_diffuse_layer; + debug_model_save = debug_model; + if (last_model.force_prep == TRUE) + { + same_model = FALSE; + } + else + { + same_model = check_same_model(); + } + if (dl_type_x != cxxSurface::NO_DL && same_model == FALSE) + { + s_diff_layer.clear(); + for (i = 0; i < count_s; i++) + { + std::map < std::string, cxxSpeciesDL > dl; + s_diff_layer.push_back(dl); + for (size_t j = 0; j < use.Get_surface_ptr()->Get_surface_charges().size(); j++) + { + cxxSpeciesDL species_dl; + std::string name = use.Get_surface_ptr()->Get_surface_charges()[j].Get_name(); + s_diff_layer.back()[name] = species_dl; + } + } + } + if (state >= REACTION && dl_type_x != cxxSurface::NO_DL) + { + if (use.Get_mix_ptr() != NULL) + { + mass_water_bulk_x = 0.0; + std::map::const_iterator cit; + for (cit = use.Get_mix_ptr()->Get_mixComps().begin(); cit != use.Get_mix_ptr()->Get_mixComps().end(); cit++) + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, cit->first); + mass_water_bulk_x += solution_ptr->Get_mass_water() * cit->second; + } + } + else + { + mass_water_bulk_x = use.Get_solution_ptr()->Get_mass_water(); + } + for (i = 0; i < (int) use.Get_surface_ptr()->Get_surface_charges().size(); i++) + { + cxxSurfaceCharge *charge_ptr = &(use.Get_surface_ptr()->Get_surface_charges()[i]); + mass_water_bulk_x += charge_ptr->Get_mass_water(); + if (use.Get_surface_ptr()->Get_debye_lengths() > 0) + charge_ptr->Set_mass_water(0.0); + } + } + prep(); + k_temp(tc_x, patm_x); + if (use.Get_surface_ptr()->Get_dl_type() == cxxSurface::DONNAN_DL) + { + initial_surface_water(); + calc_init_donnan(); + } + else + { + calc_init_g(); + } + if (state >= REACTION && !use.Get_surface_ptr()->Get_new_def()) + { + set(FALSE); + } + else + { + set(TRUE); + } + if (model() == ERROR) + return (ERROR); + g_iterations = 0; + + if (use.Get_surface_ptr()->Get_dl_type() == cxxSurface::DONNAN_DL) + { + do + { + g_iterations++; + prev_aq_x = mass_water_aq_x; + k_temp(tc_x, patm_x); + gammas(mu_x); + molalities(TRUE); + mb_sums(); + if (model() == ERROR) + return (ERROR); + if (!use.Get_surface_ptr()->Get_related_phases() + && !use.Get_surface_ptr()->Get_related_rate()) + initial_surface_water(); + if (debug_model == TRUE) + { + output_msg(sformatf( + "Surface_model (Donnan approximation): %d g_iterations, %d model iterations\n", + g_iterations, iterations)); + } + } + while ((calc_all_donnan() == FALSE + || fabs(1 - prev_aq_x / mass_water_aq_x) > 1e-6) + && g_iterations < itmax); + } + else + { + do + { + g_iterations++; + if (g_iterations > itmax - 10) + { + debug_model = TRUE; + debug_diffuse_layer = TRUE; + } + + gammas(mu_x); + molalities(TRUE); + mb_sums(); + if (model() == ERROR) + return (ERROR); + if (!use.Get_surface_ptr()->Get_related_phases() + && !use.Get_surface_ptr()->Get_related_rate()) + initial_surface_water(); + if (debug_model == TRUE) + { + output_msg(sformatf( + "Surface_model (full integration): %d g_iterations, %d iterations\n", + g_iterations, iterations)); + } + } + while (calc_all_g() == FALSE && g_iterations < itmax); + } + if (g_iterations >= itmax) + { + pr.all = TRUE; + pr.surface = TRUE; + pr.species = TRUE; + pr.use = TRUE; + print_all(); + error_msg("Did not converge on g (diffuse layer excess)", STOP); + } + debug_diffuse_layer = debug_diffuse_layer_save; + debug_model = debug_model_save; + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +free_model_allocs(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * free space allocated in model + */ + int i; + if (x != NULL) + { + for (i = 0; i < max_unknowns; i++) + { + unknown_free(x[i]); + } + } + x = (struct unknown **) free_check_null(x); + max_unknowns = 0; + array = (LDBLE *) free_check_null(array); + delta = (LDBLE *) free_check_null(delta); + residual = (LDBLE *) free_check_null(residual); + s_x = (struct species **) free_check_null(s_x); + count_s_x = 0; + sum_mb1 = (struct list1 *) free_check_null(sum_mb1); + count_sum_mb1 = 0; + sum_mb2 = (struct list2 *) free_check_null(sum_mb2); + count_sum_mb2 = 0; + sum_jacob0 = (struct list0 *) free_check_null(sum_jacob0); + count_sum_jacob0 = 0; + sum_jacob1 = (struct list1 *) free_check_null(sum_jacob1); + count_sum_jacob1 = 0; + sum_jacob2 = (struct list2 *) free_check_null(sum_jacob2); + count_sum_jacob2 = 0; + sum_delta = (struct list2 *) free_check_null(sum_delta); + count_sum_delta = 0; + return (OK); +} + +#ifdef SLNQ +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_trivial_eqns(int rows, int cols, LDBLE * matrix) +/* ---------------------------------------------------------------------- */ +{ + int r, i, j; + r = rows; + if (rows == cols) + return (OK); + if (rows > cols) + return (ERROR); + for (i = 0; i < cols; i++) + { + for (j = 0; j < rows; j++) + { + if (matrix[j * (cols + 1) + i] != 0.0) + break; + } + if (j < rows) + continue; + for (j = 0; j < cols + 1; j++) + matrix[r * (cols + 1) + j] = 0.0; + matrix[r * (cols + 1) + i] = 1.0; + r++; + } + if (r == cols) + return (OK); + return (ERROR); +} +#endif +#define ZERO_TOL 1.0e-30 +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +ss_root(LDBLE l_a0, LDBLE l_a1, LDBLE l_kc, LDBLE l_kb, LDBLE xcaq, LDBLE xbaq) +/* ---------------------------------------------------------------------- */ +{ + int i; + LDBLE x0, y0, x1, y1, xb, miny; + +/* + * Bracket answer + */ + x0 = 0.0; + x1 = 0.0; + y0 = ss_f(x0, l_a0, l_a1, l_kc, l_kb, xcaq, xbaq); + miny = fabs(y0); + for (i = 1; i <= 10; i++) + { + x1 = (LDBLE) i / 10; + y1 = ss_f(x1, l_a0, l_a1, l_kc, l_kb, xcaq, xbaq); + if (fabs(y1) < miny) + { + miny = fabs(y1); + } + if (y0 * y1 < 0) + { + break; + } + else + { + x0 = x1; + y0 = y1; + } + } +/* + * Interval halve + */ + if (i > 10) + { + xb = 0.0; + } + else + { + xb = ss_halve(l_a0, l_a1, x0, x1, l_kc, l_kb, xcaq, xbaq); + } + return (xb); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +ss_halve(LDBLE l_a0, LDBLE l_a1, LDBLE x0, LDBLE x1, LDBLE l_kc, LDBLE l_kb, + LDBLE xcaq, LDBLE xbaq) +/* ---------------------------------------------------------------------- */ +{ + int i; + LDBLE l_x, y0, dx, y; + + y0 = ss_f(x0, l_a0, l_a1, l_kc, l_kb, xcaq, xbaq); + dx = (x1 - x0); +/* + * Loop for interval halving + */ + for (i = 0; i < 100; i++) + { + dx *= 0.5; + l_x = x0 + dx; + y = ss_f(l_x, l_a0, l_a1, l_kc, l_kb, xcaq, xbaq); + if (dx < 1e-8 || y == 0) + { + break; + } + if (y0 * y >= 0) + { + x0 = l_x; + y0 = y; + } + } + return (x0 + dx); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +ss_f(LDBLE xb, LDBLE l_a0, LDBLE l_a1, LDBLE l_kc, LDBLE l_kb, LDBLE xcaq, + LDBLE xbaq) +/* ---------------------------------------------------------------------- */ +{ +/* + * Need root of this function to determine xb + */ + LDBLE lb, lc, f, xc, r; + xc = 1 - xb; + if (xb == 0) + xb = 1e-20; + if (xc == 0) + xc = 1e-20; + lc = exp((l_a0 - l_a1 * (-4 * xb + 3)) * xb * xb); + lb = exp((l_a0 + l_a1 * (4 * xb - 1)) * xc * xc); + r = lc * l_kc / (lb * l_kb); + f = xcaq * (xb / r + xc) + xbaq * (xb + r * xc) - 1; + return (f); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +numerical_jacobian(void) +/* ---------------------------------------------------------------------- */ +{ + LDBLE *base; + LDBLE d, d1, d2; + int i, j; + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + if (! + (numerical_deriv || + (use.Get_surface_ptr() != NULL && use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) || + (gas_phase_ptr != NULL && gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME && + (gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume) && numerical_fixed_volume) + )) + return(OK); + + calculating_deriv = TRUE; + gammas(mu_x); + molalities(TRUE); + mb_sums(); + residuals(); +/* + * Clear array, note residuals are in array[i, count_unknowns+1] + */ + + for (i = 0; i < count_unknowns; i++) + { + array[i] = 0.0; + } + for (i = 1; i < count_unknowns; i++) + { + memcpy((void *) &(array[i * (count_unknowns + 1)]), + (void *) &(array[0]), (size_t) count_unknowns * sizeof(LDBLE)); + } + + base = (LDBLE *) PHRQ_malloc((size_t) count_unknowns * sizeof(LDBLE)); + if (base == NULL) + malloc_error(); + for (i = 0; i < count_unknowns; i++) + { + base[i] = residual[i]; + } + d = 0.0001; + d1 = d * log(10.0); + d2 = 0; + for (i = 0; i < count_unknowns; i++) + { + switch (x[i]->type) + { + case MB: + case ALK: + case CB: + case SOLUTION_PHASE_BOUNDARY: + case EXCH: + case SURFACE: + case SURFACE_CB: + case SURFACE_CB1: + case SURFACE_CB2: + x[i]->master[0]->s->la += d; + d2 = d1; + break; + case MH: + s_eminus->la += d; + d2 = d1; + break; + case AH2O: + x[i]->master[0]->s->la += d; + d2 = d1; + break; + case PITZER_GAMMA: + x[i]->s->lg += d; + d2 = d; + break; + case MH2O: + mass_water_aq_x *= (1.0 + d); + x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + d2 = log(1.0 + d); + break; + case MU: + d2 = d * mu_x; + mu_x += d2; + gammas(mu_x); + break; + case PP: + for (j = 0; j < count_unknowns; j++) + { + delta[j] = 0.0; + } + d2 = -1e-8; + delta[i] = d2; + reset(); + d2 = delta[i]; + break; + case SS_MOLES: + if (x[i]->ss_in == FALSE) + continue; + for (j = 0; j < count_unknowns; j++) + { + delta[j] = 0.0; + } + /*d2 = -1e-8; */ + d2 = -d * x[i]->moles; + d2 = -.1 * x[i]->moles; + /* + if (d2 > -1e-10) d2 = -1e-10; + calculating_deriv = FALSE; + */ + delta[i] = d2; + /*fprintf (stderr, "delta before reset %e\n", delta[i]); */ + reset(); + d2 = delta[i]; + /*fprintf (stderr, "delta after reset %e\n", delta[i]); */ + break; + case GAS_MOLES: + if (gas_in == FALSE) + continue; + + d2 = d * x[i]->moles; + if (d2 < 1e-14) + d2 = 1e-14; + x[i]->moles += d2; + break; + } + molalities(TRUE); + mb_sums(); + /* + mb_ss(); + mb_gases(); + */ + residuals(); + //output_msg(sformatf( "%d\n", i)); + for (j = 0; j < count_unknowns; j++) + { + // avoid overflow + if (residual[j] > 1.0e101) + { + LDBLE t = (LDBLE) pow((LDBLE) 10.0, (LDBLE) (DBL_MAX_10_EXP - 50.0)); + if (residual[j] > t) + { + array[j * (count_unknowns + 1) + i] = -pow(10.0, DBL_MAX_10_EXP - 50.0); + } + else + { + array[j * (count_unknowns + 1) + i] = -(residual[j] - base[j]) / d2; + } + } + else if (residual[j] < -1.0e101) + { + LDBLE t = pow((LDBLE) 10.0, (LDBLE) (DBL_MIN_10_EXP + 50.0)); + if (residual[j] < -t) + { + array[j * (count_unknowns + 1) + i] = pow(10.0, DBL_MIN_10_EXP + 50.0); + } + else + { + array[j * (count_unknowns + 1) + i] = -(residual[j] - base[j]) / d2; + } + } + else + { + array[j * (count_unknowns + 1) + i] = -(residual[j] - base[j]) / d2; + if (!PHR_ISFINITE(array[j * (count_unknowns + 1) + i])) + { + //fprintf(stderr, "oops, got NaN: %e, %e, %e, %e\n", residual[j], base[j], d2, array[j * (count_unknowns + 1) + i]); + } + } + + //output_msg(sformatf( "\t%d %e %e %e %e\n", j, array[j*(count_unknowns + 1) + i] , residual[j], base[j], d2)); + } + switch (x[i]->type) + { + case MB: + case ALK: + case CB: + case SOLUTION_PHASE_BOUNDARY: + case EXCH: + case SURFACE: + case SURFACE_CB: + case SURFACE_CB1: + case SURFACE_CB2: + case AH2O: + x[i]->master[0]->s->la -= d; + break; + case MH: + s_eminus->la -= d; + if (array[i * (count_unknowns + 1) + i] == 0) + { + /*output_msg(sformatf( "Zero diagonal for MH\n")); */ + array[i * (count_unknowns + 1) + i] = + under(s_h2->lm) * 2; + } + break; + case PITZER_GAMMA: + x[i]->s->lg -= d; + break; + case MH2O: + mass_water_aq_x /= (1 + d); + x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + break; + case MU: + mu_x -= d2; + gammas(mu_x); + break; + case PP: + delta[i] = -d2; + reset(); + break; + case SS_MOLES: + delta[i] = -d2; + reset(); + break; + case GAS_MOLES: + x[i]->moles -= d2; + break; + } + } + molalities(TRUE); + mb_sums(); + mb_gases(); + mb_ss(); + residuals(); + free_check_null(base); + calculating_deriv = FALSE; + return OK; +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +ineq_init(int l_max_row_count, int l_max_column_count) +/* ---------------------------------------------------------------------- */ +{ + if (normal == NULL) + { + normal = + (LDBLE *) PHRQ_malloc((size_t) count_unknowns * sizeof(LDBLE)); + normal_max = count_unknowns; + if (normal == NULL) + malloc_error(); + } + if (ineq_array == NULL) + { + ineq_array = + (LDBLE *) PHRQ_malloc((size_t) l_max_row_count * l_max_column_count * + sizeof(LDBLE)); + if (ineq_array == NULL) + malloc_error(); + ineq_array_max = l_max_row_count * l_max_column_count; + } + if (back_eq == NULL) + { + back_eq = (int *) PHRQ_malloc((size_t) l_max_row_count * sizeof(int)); + if (back_eq == NULL) + malloc_error(); + back_eq_max = l_max_row_count; + } + if (zero == NULL) + { + zero = (LDBLE *) PHRQ_malloc((size_t) l_max_row_count * sizeof(LDBLE)); + if (zero == NULL) + malloc_error(); + zero_max = l_max_row_count; + } + if (res == NULL) + { + res = (LDBLE *) PHRQ_malloc((size_t) l_max_row_count * sizeof(LDBLE)); + if (res == NULL) + malloc_error(); + res_max = l_max_row_count; + } + if (delta1 == NULL) + { + delta1 = + (LDBLE *) PHRQ_malloc((size_t) l_max_column_count * sizeof(LDBLE)); + if (delta1 == NULL) + malloc_error(); + delta1_max = l_max_column_count; + } + if (cu == NULL) + { + cu = (LDBLE *) PHRQ_malloc((size_t) 3 * l_max_row_count * + sizeof(LDBLE)); + if (cu == NULL) + malloc_error(); + cu_max = 3 * l_max_row_count; + } + if (iu == NULL) + { + iu = (int *) PHRQ_malloc((size_t) 3 * l_max_row_count * sizeof(int)); + if (iu == NULL) + malloc_error(); + iu_max = 3 * l_max_row_count; + } + if (is == NULL) + { + is = (int *) PHRQ_malloc((size_t) 3 * l_max_row_count * sizeof(int)); + if (is == NULL) + malloc_error(); + is_max = 3 * l_max_row_count; + } +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +set_inert_moles(void) +/* ---------------------------------------------------------------------- */ +{ + int j; + if (use.Get_pp_assemblage_ptr() == NULL) return; + + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != PP) continue; + //cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[j]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[j]->pp_assemblage_comp_ptr; + if (comp_ptr->Get_precipitate_only()) + { + x[j]->inert_moles = x[j]->moles; + x[j]->moles = 0; + } + } +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +unset_inert_moles() +/* ---------------------------------------------------------------------- */ +{ + int j; + if (use.Get_pp_assemblage_ptr() == NULL) return; + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != PP) continue; + //cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[j]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[j]->pp_assemblage_comp_ptr; + if (comp_ptr->Get_precipitate_only()) + { + x[j]->moles += x[j]->inert_moles; + x[j]->inert_moles = 0; + } + } +} diff --git a/phreeqcpp/nvector.cpp b/phreeqcpp/nvector.cpp new file mode 100644 index 00000000..010ac16a --- /dev/null +++ b/phreeqcpp/nvector.cpp @@ -0,0 +1,264 @@ +/************************************************************************** + * * + * File : nvector.c * + * Programmers : Radu Serban, LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the implementation file for a generic NVECTOR * + * package. It contains the implementation of the N_Vector * + * kernels listed in nvector.h. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ + +#include "nvector.h" /* generic M_Env and N_Vector */ + +N_Vector +N_VNew(integertype n, M_Env machEnv) +{ + N_Vector v_new; + v_new = machEnv->ops->nvnew(n, machEnv); + return (v_new); +} + +N_Vector_S +N_VNew_S(integertype ns, integertype n, M_Env machEnv) +{ + N_Vector_S vs_new; + vs_new = machEnv->ops->nvnewS(ns, n, machEnv); + return (vs_new); +} + +void +N_VFree(N_Vector v) +{ + v->menv->ops->nvfree(v); +} + +void +N_VFree_S(integertype ns, N_Vector_S vs) +{ + (*vs)->menv->ops->nvfreeS(ns, vs); +} + +N_Vector +N_VMake(integertype n, realtype * v_data, M_Env machEnv) +{ + N_Vector v_new; + v_new = machEnv->ops->nvmake(n, v_data, machEnv); + return (v_new); +} + +void +N_VDispose(N_Vector v) +{ + v->menv->ops->nvdispose(v); +} + +realtype * +N_VGetData(N_Vector v) +{ + realtype *data; + data = v->menv->ops->nvgetdata(v); + return (data); +} + +void +N_VSetData(realtype * v_data, N_Vector v) +{ + v->menv->ops->nvsetdata(v_data, v); +} + +void +N_VLinearSum(realtype a, N_Vector x, realtype b, N_Vector y, N_Vector z) +{ + z->menv->ops->nvlinearsum(a, x, b, y, z); +} + +void +N_VConst(realtype c, N_Vector z) +{ + z->menv->ops->nvconst(c, z); +} + +void +N_VProd(N_Vector x, N_Vector y, N_Vector z) +{ + z->menv->ops->nvprod(x, y, z); +} + +void +N_VDiv(N_Vector x, N_Vector y, N_Vector z) +{ + z->menv->ops->nvdiv(x, y, z); +} + +void +N_VScale(realtype c, N_Vector x, N_Vector z) +{ + z->menv->ops->nvscale(c, x, z); +} + +void +N_VAbs(N_Vector x, N_Vector z) +{ + z->menv->ops->nvabs(x, z); +} + +void +N_VInv(N_Vector x, N_Vector z) +{ + z->menv->ops->nvinv(x, z); +} + +void +N_VAddConst(N_Vector x, realtype b, N_Vector z) +{ + z->menv->ops->nvaddconst(x, b, z); +} + +realtype +N_VDotProd(N_Vector x, N_Vector y) +{ + realtype prod; + prod = y->menv->ops->nvdotprod(x, y); + return (prod); +} + +realtype +N_VMaxNorm(N_Vector x) +{ + realtype norm; + norm = x->menv->ops->nvmaxnorm(x); + return (norm); +} + +realtype +N_VWrmsNorm(N_Vector x, N_Vector w) +{ + realtype norm; + norm = x->menv->ops->nvwrmsnorm(x, w); + return (norm); +} + +realtype +N_VMin(N_Vector x) +{ + realtype minval; + minval = x->menv->ops->nvmin(x); + return (minval); +} + +realtype +N_VWL2Norm(N_Vector x, N_Vector w) +{ + realtype norm; + norm = x->menv->ops->nvwl2norm(x, w); + return (norm); +} + +realtype +N_VL1Norm(N_Vector x) +{ + realtype norm; + norm = x->menv->ops->nvl1norm(x); + return (norm); +} + +void +N_VOneMask(N_Vector x) +{ + x->menv->ops->nvonemask(x); +} + +void +N_VCompare(realtype c, N_Vector x, N_Vector z) +{ + z->menv->ops->nvcompare(c, x, z); +} + +booleantype +N_VInvTest(N_Vector x, N_Vector z) +{ + booleantype flag; + flag = z->menv->ops->nvinvtest(x, z); + return (flag); +} + +booleantype +N_VConstrProdPos(N_Vector c, N_Vector x) +{ + booleantype flag; + flag = x->menv->ops->nvconstrprodpos(c, x); + return (flag); +} + +booleantype +N_VConstrMask(N_Vector c, N_Vector x, N_Vector m) +{ + booleantype flag; + flag = x->menv->ops->nvconstrmask(c, x, m); + return (flag); +} + +realtype +N_VMinQuotient(N_Vector num, N_Vector denom) +{ + realtype quotient; + quotient = num->menv->ops->nvminquotient(num, denom); + return (quotient); +} + +void +N_VPrint(N_Vector x) +{ + x->menv->ops->nvprint(x); +} diff --git a/phreeqcpp/nvector.h b/phreeqcpp/nvector.h new file mode 100644 index 00000000..fa32f832 --- /dev/null +++ b/phreeqcpp/nvector.h @@ -0,0 +1,485 @@ +/************************************************************************** + * * + * File : nvector.h * + * Programmers : Radu Serban, LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the header file for a generic NVECTOR package. * + * It defines the N_Vector and M_Env structures: * + * M_Env has an implementation-dependent 'content' field * + * which contains the data needed to generate a new * + * nvector in that implementation and an 'ops' filed * + * which is a structure listing operations acting on * + * such nvectors. * + * N_Vector has an implementation-dependent 'content' field * + * which contains the description and actual data of * + * the nvector and a 'menv' field which points to the * + * M_Env structure used in creating the nvector. * + * * + * Part I of this file contains type declarations for the * + * the following structures: _generic_M_Env, _generic_N_Vector, * + * and _generic_N_Vector_Ops, as well as references to pointers * + * to such structures (M_Env and N_Vector). * + * * + * Part II of this file contains the prototypes for the vector * + * kernels which operate on N_Vector. * + * * + * A particular implementation of an NVECTOR package must then * + * specify the 'content' fields of M_Env and N_Vector, define * + * the propotypes for kernel operations on those N_Vectors * + * (NOTE: kernel routine names must be unique to that * + * implementation), and finally provide an initialization * + * routine (which generates an M_Env with that particular * + * 'content' field and links the defined vector kernel routines * + * into the 'ops' field). * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ + +class Phreeqc; +#ifndef included_nvector_h +#define included_nvector_h + +#include "sundialstypes.h" /* definition of types */ + +/**************************************************************** + * Generic definitions of machine environment and N_Vector * + ****************************************************************/ + +/* Forward reference for pointer to N_Vector_Ops object */ + typedef struct _generic_N_Vector_Ops *N_Vector_Ops; + +/* Forward reference for pointer to M_Env object */ + typedef struct _generic_M_Env *M_Env; + +/* Forward reference for pointer to N_Vector object */ + typedef struct _generic_N_Vector *N_Vector; + +/* Define array of N_Vectors */ + typedef N_Vector *N_Vector_S; + +/* Structure containing function pointers to vector operations */ + struct _generic_N_Vector_Ops + { + N_Vector(*nvnew) (integertype, M_Env); + N_Vector_S(*nvnewS) (integertype, integertype, M_Env); + void (*nvfree) (N_Vector); + void (*nvfreeS) (integertype, N_Vector_S); + N_Vector(*nvmake) (integertype, realtype *, M_Env); + void (*nvdispose) (N_Vector); + realtype *(*nvgetdata) (N_Vector); + void (*nvsetdata) (realtype *, N_Vector); + void (*nvlinearsum) (realtype, N_Vector, realtype, N_Vector, + N_Vector); + void (*nvconst) (realtype, N_Vector); + void (*nvprod) (N_Vector, N_Vector, N_Vector); + void (*nvdiv) (N_Vector, N_Vector, N_Vector); + void (*nvscale) (realtype, N_Vector, N_Vector); + void (*nvabs) (N_Vector, N_Vector); + void (*nvinv) (N_Vector, N_Vector); + void (*nvaddconst) (N_Vector, realtype, N_Vector); + realtype(*nvdotprod) (N_Vector, N_Vector); + realtype(*nvmaxnorm) (N_Vector); + realtype(*nvwrmsnorm) (N_Vector, N_Vector); + realtype(*nvmin) (N_Vector); + realtype(*nvwl2norm) (N_Vector, N_Vector); + realtype(*nvl1norm) (N_Vector); + void (*nvonemask) (N_Vector); + void (*nvcompare) (realtype, N_Vector, N_Vector); + booleantype(*nvinvtest) (N_Vector, N_Vector); + booleantype(*nvconstrprodpos) (N_Vector, N_Vector); + booleantype(*nvconstrmask) (N_Vector, N_Vector, N_Vector); + realtype(*nvminquotient) (N_Vector, N_Vector); + void (*nvprint) (N_Vector); + }; + +/* A machine environment is a structure with an implementation + dependent 'content' representation (used to generate a new vector + in that implementation), a set of operations defined in the above + structure, and an ID tag */ + struct _generic_M_Env + { + void *content; + struct _generic_N_Vector_Ops *ops; + char tag[8]; + Phreeqc * phreeqc_ptr; + }; + +/* A vector is a structure with an implementation dependent content + representation and a pointer to the machine environment + corresponding to that implementation */ + struct _generic_N_Vector + { + void *content; + struct _generic_M_Env *menv; + }; + +/**************************************************************** + * Functions exported by nvector * + ****************************************************************/ + +/*--------------------------------------------------------------* + * Function : N_VNew * + * Usage : v = N_VNew(n, machEnv); * + *--------------------------------------------------------------* + * Returns a new N_Vector of length n. The parameter machEnv * + * is a pointer to machine environment-specific information. * + * If there is not enough memory for a new N_Vector, then * + * N_VNew returns NULL. * + *--------------------------------------------------------------*/ + + N_Vector N_VNew(integertype n, M_Env machEnv); + +/*--------------------------------------------------------------* + * Function : N_VNew_S * + * Usage : v = N_VNew_S(ns, n, machEnv); * + *--------------------------------------------------------------* + * Returns an array of ns new N_Vectors of length n. The * + * parameter machEnv is a pointer to machine environment * + * specific information. * + * If there is not enough memory for a new array of N_Vectors * + * or for one of the components, then N_VNew_S returns NULL. * + *--------------------------------------------------------------*/ + + N_Vector_S N_VNew_S(integertype ns, integertype n, M_Env machEnv); + +/*--------------------------------------------------------------* + * Function : N_VFree * + * Usage : N_VFree(v); * + *--------------------------------------------------------------* + * Frees the N_Vector v. It is illegal to use v after the call * + * N_VFree(v). * + *--------------------------------------------------------------*/ + + void N_VFree(N_Vector v); + +/*--------------------------------------------------------------* + * Function : N_VFree_S * + * Usage : N_VFree_S(ns, vs); * + *--------------------------------------------------------------* + * Frees the array of ns N_Vectors vs. * + * It is illegal to use vs after the call N_VFree_S(Ns,vs). * + *--------------------------------------------------------------*/ + + void N_VFree_S(integertype ns, N_Vector_S vs); + +/*--------------------------------------------------------------* + * Function : N_VMake * + * Usage : v = N_VMake(n, v_data, machEnv); * + *--------------------------------------------------------------* + * Creates an N_Vector with component array data allocated by * + * the user. * + *--------------------------------------------------------------*/ + + N_Vector N_VMake(integertype n, realtype * v_data, M_Env machEnv); + +/*--------------------------------------------------------------* + * Function : N_VDispose * + * Usage : N_VDispose(v); * + *--------------------------------------------------------------* + * Destroys an N_Vector with component array data allocated by * + * the user. * + *--------------------------------------------------------------*/ + + void N_VDispose(N_Vector v); + +/*--------------------------------------------------------------* + * Function : N_VGetData * + * Usage : v_data = N_VGetData(v); * + *--------------------------------------------------------------* + * Extracts the data component array from the N_Vector v. * + * Note: this routine is used in the solver-specific interfaces * + * to the dense and banded linear solvers, as well as the * + * interfaces to the banded preconditioners provided with * + * SUNDIALS. It needs not be implemented by a user * + * defined NVECTOR module, if these linear solvers are not* + * used. * + *--------------------------------------------------------------*/ + + realtype *N_VGetData(N_Vector v); + +/*--------------------------------------------------------------* + * Function : N_VSetData * + * Usage : N_VSetData(v_data, v); * + *--------------------------------------------------------------* + * Attaches the data component array v_data to the N_Vector v. * + * Note: this routine is used in the solver-specific interfaces * + * to the dense and banded linear solvers, as well as the * + * interfaces to the banded preconditioners provided with * + * SUNDIALS. It needs not be implemented by a user * + * defined NVECTOR module, if these linear solvers are not* + * used. * + *--------------------------------------------------------------*/ + + void N_VSetData(realtype * v_data, N_Vector v); + +/*--------------------------------------------------------------* + * Function : N_VLinearSum * + * Operation : z = a x + b y * + *--------------------------------------------------------------*/ + + void N_VLinearSum(realtype a, N_Vector x, realtype b, N_Vector y, + N_Vector z); + +/*--------------------------------------------------------------* + * Function : N_VConst * + * Operation : z[i] = c for i=0, 1, ..., N-1 * + *--------------------------------------------------------------*/ + + void N_VConst(realtype c, N_Vector z); + +/*--------------------------------------------------------------* + * Function : N_VProd * + * Operation : z[i] = x[i] * y[i] for i=0, 1, ..., N-1 * + *--------------------------------------------------------------*/ + + void N_VProd(N_Vector x, N_Vector y, N_Vector z); + +/*--------------------------------------------------------------* + * Function : N_VDiv * + * Operation : z[i] = x[i] / y[i] for i=0, 1, ..., N-1 * + *--------------------------------------------------------------*/ + + void N_VDiv(N_Vector x, N_Vector y, N_Vector z); + +/*--------------------------------------------------------------* + * Function : N_VScale * + * Operation : z = c x * + *--------------------------------------------------------------*/ + + void N_VScale(realtype c, N_Vector x, N_Vector z); + +/*--------------------------------------------------------------* + * Function : N_VAbs * + * Operation : z[i] = |x[i]|, for i=0, 1, ..., N-1 * + *--------------------------------------------------------------*/ + + void N_VAbs(N_Vector x, N_Vector z); + +/*--------------------------------------------------------------* + * Function : N_VInv * + * Operation : z[i] = 1.0 / x[i] for i = 0, 1, ..., N-1 * + *--------------------------------------------------------------* + * This routine does not check for division by 0. It should be * + * called only with an N_Vector x which is guaranteed to have * + * all non-zero components. * + *--------------------------------------------------------------*/ + + void N_VInv(N_Vector x, N_Vector z); + +/*--------------------------------------------------------------* + * Function : N_VAddConst * + * Operation : z[i] = x[i] + b for i = 0, 1, ..., N-1 * + *--------------------------------------------------------------*/ + + void N_VAddConst(N_Vector x, realtype b, N_Vector z); + +/*--------------------------------------------------------------* + * Function : N_VDotProd * + * Usage : dotprod = N_VDotProd(x, y); * + *--------------------------------------------------------------* + * Returns the value of the ordinary dot product of x and y: * + * -> sum (i=0 to N-1) {x[i] * y[i]} * + * Returns 0.0 if N <= 0. * + *--------------------------------------------------------------*/ + + realtype N_VDotProd(N_Vector x, N_Vector y); + +/*--------------------------------------------------------------* + * Function : N_VMaxNorm * + * Usage : maxnorm = N_VMaxNorm(x); * + *--------------------------------------------------------------* + * Returns the maximum norm of x: * + * -> max (i=0 to N-1) |x[i]| * + * Returns 0.0 if N <= 0. * + *--------------------------------------------------------------*/ + + realtype N_VMaxNorm(N_Vector x); + +/*--------------------------------------------------------------* + * Function : N_VWrmsNorm * + * Usage : wrmsnorm = N_VWrmsNorm(x, w); * + *--------------------------------------------------------------* + * Returns the weighted root mean square norm of x with * + * weight vector w: * + * -> sqrt [(sum (i=0 to N-1) {(x[i] * w[i])^2}) / N] * + * Returns 0.0 if N <= 0. * + *--------------------------------------------------------------*/ + + realtype N_VWrmsNorm(N_Vector x, N_Vector w); + +/*--------------------------------------------------------------* + * Function : N_VMin * + * Usage : min = N_VMin(x); * + *--------------------------------------------------------------* + * Returns the smallest element of x: * + * -> min (i=0 to N-1) x[i] * + * Returns 0.0 if N <= 0. * + *--------------------------------------------------------------*/ + + realtype N_VMin(N_Vector x); + +/*--------------------------------------------------------------* + * Function : N_VWL2Norm * + * Usage : wl2norm = N_VWL2Norm(x, w); * + *--------------------------------------------------------------* + * Returns the weighted Euclidean L2 norm of x with * + * weight vector w: * + * -> sqrt [(sum (i=0 to N-1) {(x[i] * w[i])^2}) ] * + * Returns 0.0 if N <= 0. * + *--------------------------------------------------------------*/ + + realtype N_VWL2Norm(N_Vector x, N_Vector w); + +/*--------------------------------------------------------------* + * Function : N_VL1Norm * + * Usage : l1norm = N_VL1Norm(x); * + *--------------------------------------------------------------* + * Returns the L1 norm of x: * + * -> sum (i=0 to N-1) {ABS(x[i])} * + * Returns 0.0 if N <= 0. * + *--------------------------------------------------------------*/ + + realtype N_VL1Norm(N_Vector x); + +/*--------------------------------------------------------------* + * Function : N_VOneMask * + * Operation : x[i] = 1.0 if |x[i]| != 0. i = 0, 1, ..., N-1 * + * 0.0 otherwise * + *--------------------------------------------------------------*/ + + void N_VOneMask(N_Vector x); + +/*--------------------------------------------------------------* + * Function : N_VCompare * + * Operation : z[i] = 1.0 if |x[i]| >= c i = 0, 1, ..., N-1 * + * 0.0 otherwise * + *--------------------------------------------------------------*/ + + void N_VCompare(realtype c, N_Vector x, N_Vector z); + +/*--------------------------------------------------------------* + * Function : N_VInvTest * + * Operation : z[i] = 1.0 / x[i] with a test for x[i]==0.0 * + * before inverting x[i]. * + *--------------------------------------------------------------* + * This routine returns TRUE if all components of x are * + * non-zero (successful inversion) and returns FALSE * + * otherwise. * + *--------------------------------------------------------------*/ + + booleantype N_VInvTest(N_Vector x, N_Vector z); + +/*--------------------------------------------------------------* + * Function : N_VConstrProdPos * + * Usage : booltest = N_VConstrProdPos(c,x); * + *--------------------------------------------------------------* + * Returns a boolean equal to * + * FALSE if some c[i] != 0.0 and x[i]*c[i] <= 0.0, or * + * TRUE otherwise. * + * * + * This routine is used for constraint checking. * + *--------------------------------------------------------------*/ + + booleantype N_VConstrProdPos(N_Vector c, N_Vector x); + +/*--------------------------------------------------------------* + * Function : N_VConstrMask * + * Operation : m[i] = 1.0 if constraint test fails for x[i] * + * m[i] = 0.0 if constraint test passes for x[i] * + * where the constraint tests are as follows: * + * If c[i] = 2.0, then x[i] must be > 0.0. * + * If c[i] = 1.0, then x[i] must be >= 0.0. * + * If c[i] = -1.0, then x[i] must be <= 0.0. * + * If c[i] = -2.0, then x[i] must be < 0.0. * + *--------------------------------------------------------------* + * This routine returns a boolean FALSE if any element failed * + * the constraint test, TRUE if all passed. It also sets a * + * mask vector m, with elements equal to 1.0 where the * + * corresponding constraint test failed, and equal to 0.0 * + * where the constraint test passed. * + * This routine is specialized in that it is used only for * + * constraint checking. * + *--------------------------------------------------------------*/ + + booleantype N_VConstrMask(N_Vector c, N_Vector x, N_Vector m); + +/*--------------------------------------------------------------* + * Function : N_VMinQuotient * + * Operation : minq = min ( num[i]/denom[i]) over all i such * + * that denom[i] != 0. * + *--------------------------------------------------------------* + * This routine returns the minimum of the quotients obtained * + * by term-wise dividing num[i] by denom[i]. A zero element * + * in denom will be skipped. If no such quotients are found, * + * then the large value 1.e99 is returned. * + *--------------------------------------------------------------*/ + + realtype N_VMinQuotient(N_Vector num, N_Vector denom); + +/*--------------------------------------------------------------* + * Function : N_VPrint * + * Usage : N_VPrint(x); * + *--------------------------------------------------------------* + * Prints the N_Vector x to stdout. * + * This routine is provided as an aid in debugging code which * + * uses this vector package. * + *--------------------------------------------------------------*/ + + void N_VPrint(N_Vector x); + + +#endif +/* +#ifdef __cplusplus +} +#endif +*/ diff --git a/phreeqcpp/nvector_serial.cpp b/phreeqcpp/nvector_serial.cpp new file mode 100644 index 00000000..8760a599 --- /dev/null +++ b/phreeqcpp/nvector_serial.cpp @@ -0,0 +1,1023 @@ +/************************************************************************** + * * + * File : nvector_serial.c * + * Programmers : Scott D. Cohen, Alan C. Hindmarsh, * + * Radu Serban, and Allan G. Taylor, LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the implementation file for a serial implementation * + * of the NVECTOR package. It contains the implementation of * + * the serial machine environment intialization and free * + * routines (and of the Fortran callable interfaces to them) * + * and of the N_Vector kernels listed in nvector_serial.h. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ +#include +#include +#include +#include "nvector_serial.h" +#include "sundialstypes.h" +#include "sundialsmath.h" + +/* WARNING don`t include any headers below here */ + +#define ZERO RCONST(0.0) +#define HALF RCONST(0.5) +#define ONE RCONST(1.0) +#define ONEPT5 RCONST(1.5) + + +/* Private Helper Prototypes */ +/* z=x */ +static void VCopy_Serial(N_Vector x, N_Vector z); +/* z=x+y */ +static void VSum_Serial(N_Vector x, N_Vector y, N_Vector z); +/* z=x-y */ +static void VDiff_Serial(N_Vector x, N_Vector y, N_Vector z); +/* z=-x */ +static void VNeg_Serial(N_Vector x, N_Vector z); +/* z=c(x+y) */ +static void VScaleSum_Serial(realtype c, N_Vector x, N_Vector y, N_Vector z); +/* z=c(x-y) */ +static void VScaleDiff_Serial(realtype c, N_Vector x, N_Vector y, N_Vector z); +/* z=ax+y */ +static void VLin1_Serial(realtype a, N_Vector x, N_Vector y, N_Vector z); +/* z=ax-y */ +static void VLin2_Serial(realtype a, N_Vector x, N_Vector y, N_Vector z); +/* y <- ax+y */ +static void Vaxpy_Serial(realtype a, N_Vector x, N_Vector y); +/* x <- ax */ +static void VScaleBy_Serial(realtype a, N_Vector x); + +/********************* Exported Functions ************************/ + +/* Serial implementation of the machine environment + initialization routine */ + +M_Env +M_EnvInit_Serial(integertype vec_length) +{ + M_Env me; + + /* Create machine environment structure */ + me = (M_Env) malloc(sizeof *me); + if (me == NULL) + return (NULL); + + /* Create serial content of machine environment structure */ + me->content = + (M_EnvSerialContent) malloc(sizeof(struct _M_EnvSerialContent)); + if (me->content == NULL) + { + free(me); + return (NULL); + } + + /* Load serial content of machine environment structure */ + ME_CONTENT_S(me)->length = vec_length; + + /* Attach vector operations */ + me->ops = (N_Vector_Ops) malloc(sizeof(struct _generic_N_Vector_Ops)); + if (me->ops == NULL) + { + free(me->content); + free(me); + return (NULL); + } + + me->ops->nvnew = N_VNew_Serial; + me->ops->nvnewS = N_VNew_S_Serial; + me->ops->nvfree = N_VFree_Serial; + me->ops->nvfreeS = N_VFree_S_Serial; + me->ops->nvmake = N_VMake_Serial; + me->ops->nvdispose = N_VDispose_Serial; + me->ops->nvgetdata = N_VGetData_Serial; + me->ops->nvsetdata = N_VSetData_Serial; + me->ops->nvlinearsum = N_VLinearSum_Serial; + me->ops->nvconst = N_VConst_Serial; + me->ops->nvprod = N_VProd_Serial; + me->ops->nvdiv = N_VDiv_Serial; + me->ops->nvscale = N_VScale_Serial; + me->ops->nvabs = N_VAbs_Serial; + me->ops->nvinv = N_VInv_Serial; + me->ops->nvaddconst = N_VAddConst_Serial; + me->ops->nvdotprod = N_VDotProd_Serial; + me->ops->nvmaxnorm = N_VMaxNorm_Serial; + me->ops->nvwrmsnorm = N_VWrmsNorm_Serial; + me->ops->nvmin = N_VMin_Serial; + me->ops->nvwl2norm = N_VWL2Norm_Serial; + me->ops->nvl1norm = N_VL1Norm_Serial; + me->ops->nvonemask = N_VOneMask_Serial; + me->ops->nvcompare = N_VCompare_Serial; + me->ops->nvinvtest = N_VInvTest_Serial; + me->ops->nvconstrprodpos = N_VConstrProdPos_Serial; + me->ops->nvconstrmask = N_VConstrMask_Serial; + me->ops->nvminquotient = N_VMinQuotient_Serial; + me->ops->nvprint = N_VPrint_Serial; + + /* Attach ID tag */ + strcpy(me->tag, ID_TAG_S); + + return (me); + +} + +/* Serial implementation of the machine environment + free routine */ + +void +M_EnvFree_Serial(M_Env machEnv) +{ + if (machEnv == NULL) + return; + + free(machEnv->content); + free(machEnv->ops); + free(machEnv); +} + +/***************************************************************************/ + +/* BEGIN implementation of vector operations */ + +N_Vector +N_VNew_Serial(integertype n, M_Env machEnv) +{ + N_Vector v; + integertype length; + + if (n <= 0) + return (NULL); + + if (machEnv == NULL) + return (NULL); + + v = (N_Vector) malloc(sizeof *v); + if (v == NULL) + return (NULL); + + v->content = + (N_VectorSerialContent) malloc(sizeof(struct _N_VectorSerialContent)); + if (v->content == NULL) + { + free(v); + return (NULL); + } + + length = ME_CONTENT_S(machEnv)->length; + + NV_CONTENT_S(v)->data = (realtype *) malloc(length * sizeof(realtype)); + if (NV_CONTENT_S(v)->data == NULL) + { + free(v->content); + free(v); + return (NULL); + } + + NV_CONTENT_S(v)->length = length; + + v->menv = machEnv; + + return (v); +} + + +N_Vector_S +N_VNew_S_Serial(integertype ns, integertype n, M_Env machEnv) +{ + N_Vector_S vs; + integertype is, j; + + + if (ns <= 0 || n <= 0) + return (NULL); + + if (machEnv == NULL) + return (NULL); + + vs = (N_Vector_S) malloc(ns * sizeof(N_Vector *)); + if (vs == NULL) + return (NULL); + + for (is = 0; is < ns; is++) + { + vs[is] = N_VNew_Serial(n, machEnv); + if (vs[is] == NULL) + { + for (j = 0; j < is; j++) + N_VFree_Serial(vs[j]); + free(vs); + return (NULL); + } + } + + return (vs); +} + + +void +N_VFree_Serial(N_Vector v) +{ + free(NV_DATA_S(v)); + free(NV_CONTENT_S(v)); + free(v); +} + + +void +N_VFree_S_Serial(integertype ns, N_Vector_S vs) +{ + integertype is; + + for (is = 0; is < ns; is++) + N_VFree_Serial(vs[is]); + free(vs); +} + +N_Vector +N_VMake_Serial(integertype n, realtype * v_data, M_Env machEnv) +{ + N_Vector v; + integertype length; + + if (n <= 0) + return (NULL); + + if (machEnv == NULL) + return (NULL); + + v = (N_Vector) malloc(sizeof *v); + if (v == NULL) + return (NULL); + + v->content = + (N_VectorSerialContent) malloc(sizeof(struct _N_VectorSerialContent)); + if (v->content == NULL) + { + free(v); + return (NULL); + } + + length = ME_CONTENT_S(machEnv)->length; + + NV_CONTENT_S(v)->data = v_data; + + NV_CONTENT_S(v)->length = length; + + v->menv = machEnv; + + return (v); +} + +void +N_VDispose_Serial(N_Vector v) +{ + free(NV_CONTENT_S(v)); + free(v); +} + +realtype * +N_VGetData_Serial(N_Vector v) +{ + realtype *v_data; + v_data = NV_CONTENT_S(v)->data; + return (v_data); +} + +void +N_VSetData_Serial(realtype * v_data, N_Vector v) +{ + NV_CONTENT_S(v)->data = v_data; +} + +void +N_VLinearSum_Serial(realtype a, N_Vector x, realtype b, N_Vector y, + N_Vector z) +{ + integertype i, N; + realtype c, *xd, *yd, *zd; + N_Vector v1, v2; + booleantype test; + + if ((b == ONE) && (z == y)) + { /* BLAS usage: axpy y <- ax+y */ + Vaxpy_Serial(a, x, y); + return; + } + + if ((a == ONE) && (z == x)) + { /* BLAS usage: axpy x <- by+x */ + Vaxpy_Serial(b, y, x); + return; + } + + /* Case: a == b == 1.0 */ + + if ((a == ONE) && (b == ONE)) + { + VSum_Serial(x, y, z); + return; + } + + /* Cases: (1) a == 1.0, b = -1.0, (2) a == -1.0, b == 1.0 */ + + /*if ((test = ((a == ONE) && (b == -ONE))) || ((a == -ONE) && (b == ONE))) { */ + test = ((a == ONE) && (b == -ONE)); + if (test || ((a == -ONE) && (b == ONE))) + { + v1 = test ? y : x; + v2 = test ? x : y; + VDiff_Serial(v2, v1, z); + return; + } + + /* Cases: (1) a == 1.0, b == other or 0.0, (2) a == other or 0.0, b == 1.0 */ + /* if a or b is 0.0, then user should have called N_VScale */ + + /*if ((test = (a == ONE)) || (b == ONE)) { */ + test = (a == ONE); + if (test || (b == ONE)) + { + c = test ? b : a; + v1 = test ? y : x; + v2 = test ? x : y; + VLin1_Serial(c, v1, v2, z); + return; + } + + /* Cases: (1) a == -1.0, b != 1.0, (2) a != 1.0, b == -1.0 */ + + /*if ((test = (a == -ONE)) || (b == -ONE)) { */ + test = (a == -ONE); + if (test || (b == -ONE)) + { + c = test ? b : a; + v1 = test ? y : x; + v2 = test ? x : y; + VLin2_Serial(c, v1, v2, z); + return; + } + + /* Case: a == b */ + /* catches case both a and b are 0.0 - user should have called N_VConst */ + + if (a == b) + { + VScaleSum_Serial(a, x, y, z); + return; + } + + /* Case: a == -b */ + + if (a == -b) + { + VScaleDiff_Serial(a, x, y, z); + return; + } + + /* Do all cases not handled above: + (1) a == other, b == 0.0 - user should have called N_VScale + (2) a == 0.0, b == other - user should have called N_VScale + (3) a,b == other, a !=b, a != -b */ + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + yd = NV_DATA_S(y); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = a * (*xd++) + b * (*yd++); +} + + +void +N_VConst_Serial(realtype c, N_Vector z) +{ + integertype i, N; + realtype *zd; + + N = NV_LENGTH_S(z); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = c; +} + + +void +N_VProd_Serial(N_Vector x, N_Vector y, N_Vector z) +{ + integertype i, N; + realtype *xd, *yd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + yd = NV_DATA_S(y); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = (*xd++) * (*yd++); +} + + +void +N_VDiv_Serial(N_Vector x, N_Vector y, N_Vector z) +{ + integertype i, N; + realtype *xd, *yd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + yd = NV_DATA_S(y); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = (*xd++) / (*yd++); +} + + +void +N_VScale_Serial(realtype c, N_Vector x, N_Vector z) +{ + integertype i, N; + realtype *xd, *zd; + + if (z == x) + { /* BLAS usage: scale x <- cx */ + VScaleBy_Serial(c, x); + return; + } + + if (c == ONE) + { + VCopy_Serial(x, z); + } + else if (c == -ONE) + { + VNeg_Serial(x, z); + } + else + { + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + zd = NV_DATA_S(z); + for (i = 0; i < N; i++) + *zd++ = c * (*xd++); + } +} + + +void +N_VAbs_Serial(N_Vector x, N_Vector z) +{ + integertype i, N; + realtype *xd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++, xd++, zd++) + *zd = ABS(*xd); +} + + +void +N_VInv_Serial(N_Vector x, N_Vector z) +{ + integertype i, N; + realtype *xd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = ONE / (*xd++); +} + + +void +N_VAddConst_Serial(N_Vector x, realtype b, N_Vector z) +{ + integertype i, N; + realtype *xd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = (*xd++) + b; +} + + +realtype +N_VDotProd_Serial(N_Vector x, N_Vector y) +{ + integertype i, N; + realtype sum = ZERO, *xd, *yd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + yd = NV_DATA_S(y); + + for (i = 0; i < N; i++) + sum += (*xd++) * (*yd++); + + return (sum); +} + + +realtype +N_VMaxNorm_Serial(N_Vector x) +{ + integertype i, N; + realtype max = ZERO, *xd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + + for (i = 0; i < N; i++, xd++) + { + if (ABS(*xd) > max) + max = ABS(*xd); + } + + return (max); +} + + +realtype +N_VWrmsNorm_Serial(N_Vector x, N_Vector w) +{ + integertype i, N; + realtype sum = ZERO, prodi, *xd, *wd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + wd = NV_DATA_S(w); + + for (i = 0; i < N; i++) + { + prodi = (*xd++) * (*wd++); + sum += prodi * prodi; + } + + return (RSqrt(sum / N)); +} + + +realtype +N_VMin_Serial(N_Vector x) +{ + integertype i, N; + realtype min, *xd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + + min = xd[0]; + + xd++; + for (i = 1; i < N; i++, xd++) + { + if ((*xd) < min) + min = *xd; + } + + return (min); +} + + +realtype +N_VWL2Norm_Serial(N_Vector x, N_Vector w) +{ + integertype i, N; + realtype sum = ZERO, prodi, *xd, *wd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + wd = NV_DATA_S(w); + + for (i = 0; i < N; i++) + { + prodi = (*xd++) * (*wd++); + sum += prodi * prodi; + } + + return (RSqrt(sum)); +} + + +realtype +N_VL1Norm_Serial(N_Vector x) +{ + integertype i, N; + realtype sum = ZERO, *xd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + + for (i = 0; i < N; i++) + sum += ABS(xd[i]); + + return (sum); +} + + +void +N_VOneMask_Serial(N_Vector x) +{ + integertype i, N; + realtype *xd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + + for (i = 0; i < N; i++, xd++) + { + if (*xd != ZERO) + *xd = ONE; + } +} + + +void +N_VCompare_Serial(realtype c, N_Vector x, N_Vector z) +{ + integertype i, N; + realtype *xd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++, xd++, zd++) + { + *zd = (ABS(*xd) >= c) ? ONE : ZERO; + } +} + + +booleantype +N_VInvTest_Serial(N_Vector x, N_Vector z) +{ + integertype i, N; + realtype *xd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + { + if (*xd == ZERO) + return (FALSE); + *zd++ = ONE / (*xd++); + } + + return (TRUE); +} + + +booleantype +N_VConstrProdPos_Serial(N_Vector c, N_Vector x) +{ + integertype i, N; + realtype *xd, *cd; + booleantype test; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + cd = NV_DATA_S(c); + + test = TRUE; + + for (i = 0; i < N; i++, xd++, cd++) + { + if (*cd != ZERO) + { + if ((*xd) * (*cd) <= ZERO) + { + test = FALSE; + break; + } + } + } + return (test); +} + + +booleantype +N_VConstrMask_Serial(N_Vector c, N_Vector x, N_Vector m) +{ + integertype i, N; + booleantype test; + realtype *cd, *xd, *md; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + cd = NV_DATA_S(c); + md = NV_DATA_S(m); + + test = TRUE; + + for (i = 0; i < N; i++, cd++, xd++, md++) + { + *md = ZERO; + if (*cd == ZERO) + continue; + if (*cd > ONEPT5 || (*cd) < -ONEPT5) + { + if ((*xd) * (*cd) <= ZERO) + { + test = FALSE; + *md = ONE; + } + continue; + } + if ((*cd) > HALF || (*cd) < -HALF) + { + if ((*xd) * (*cd) < ZERO) + { + test = FALSE; + *md = ONE; + } + } + } + return (test); +} + + +realtype +N_VMinQuotient_Serial(N_Vector num, N_Vector denom) +{ + booleantype notEvenOnce; + integertype i, N; + realtype *nd, *dd, min; + + N = NV_LENGTH_S(num); + nd = NV_DATA_S(num); + dd = NV_DATA_S(denom); + min = 0; + + notEvenOnce = TRUE; + + for (i = 0; i < N; i++, nd++, dd++) + { + if (*dd == ZERO) + continue; + else + { + if (notEvenOnce) + { + min = *nd / *dd; + notEvenOnce = FALSE; + } + else + min = MIN(min, (*nd) / (*dd)); + } + } + if (notEvenOnce) + min = 1.e99; + + return (min); +} + + +void +N_VPrint_Serial(N_Vector x) +{ + integertype N; + realtype *xd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + +#if !defined(R_SO) + for (integertype i = 0; i < N; i++) + printf("%11.8g\n", (double) (*xd++)); + + printf("\n"); +#endif +} + + +/***************** Private Helper Functions **********************/ + + +static void +VCopy_Serial(N_Vector x, N_Vector z) +{ + integertype i, N; + realtype *xd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = *xd++; +} + + +static void +VSum_Serial(N_Vector x, N_Vector y, N_Vector z) +{ + integertype i, N; + realtype *xd, *yd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + yd = NV_DATA_S(y); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = (*xd++) + (*yd++); +} + + +static void +VDiff_Serial(N_Vector x, N_Vector y, N_Vector z) +{ + integertype i, N; + realtype *xd, *yd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + yd = NV_DATA_S(y); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = (*xd++) - (*yd++); +} + + +static void +VNeg_Serial(N_Vector x, N_Vector z) +{ + integertype i, N; + realtype *xd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = -(*xd++); +} + + +static void +VScaleSum_Serial(realtype c, N_Vector x, N_Vector y, N_Vector z) +{ + integertype i, N; + realtype *xd, *yd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + yd = NV_DATA_S(y); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = c * ((*xd++) + (*yd++)); +} + + +static void +VScaleDiff_Serial(realtype c, N_Vector x, N_Vector y, N_Vector z) +{ + integertype i, N; + realtype *xd, *yd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + yd = NV_DATA_S(y); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = c * ((*xd++) - (*yd++)); +} + + +static void +VLin1_Serial(realtype a, N_Vector x, N_Vector y, N_Vector z) +{ + integertype i, N; + realtype *xd, *yd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + yd = NV_DATA_S(y); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = a * (*xd++) + (*yd++); +} + + +static void +VLin2_Serial(realtype a, N_Vector x, N_Vector y, N_Vector z) +{ + integertype i, N; + realtype *xd, *yd, *zd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + yd = NV_DATA_S(y); + zd = NV_DATA_S(z); + + for (i = 0; i < N; i++) + *zd++ = a * (*xd++) - (*yd++); +} + +static void +Vaxpy_Serial(realtype a, N_Vector x, N_Vector y) +{ + integertype i, N; + realtype *xd, *yd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + yd = NV_DATA_S(y); + + if (a == ONE) + { + for (i = 0; i < N; i++) + *yd++ += (*xd++); + return; + } + + if (a == -ONE) + { + for (i = 0; i < N; i++) + *yd++ -= (*xd++); + return; + } + + for (i = 0; i < N; i++) + *yd++ += a * (*xd++); +} + +static void +VScaleBy_Serial(realtype a, N_Vector x) +{ + integertype i, N; + realtype *xd; + + N = NV_LENGTH_S(x); + xd = NV_DATA_S(x); + + for (i = 0; i < N; i++) + *xd++ *= a; +} diff --git a/phreeqcpp/nvector_serial.h b/phreeqcpp/nvector_serial.h new file mode 100644 index 00000000..8a5ecb90 --- /dev/null +++ b/phreeqcpp/nvector_serial.h @@ -0,0 +1,369 @@ +#ifndef _INC_NVECTOR_SERIAL_H +#define _INC_NVECTOR_SERIAL_H +/************************************************************************** + * * + * File : nvector_serial.h * + * Programmers : Scott D. Cohen, Alan C. Hindmarsh, * + * : Radu Serban, and Allan G. Taylor, LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the header file for a serial implementation of the * + * NVECTOR package. * + * * + * Part I of this file contains declarations which are specific * + * to the particular machine environment in which this version * + * of the vector package is to be used. This includes the * + * typedef for the 'content' fields of the structures M_Env and * + * N_Vector (M_EnvSerialContent and N_VectorSerialContent, * + * respectively). * + * * + * Part II of this file defines accessor macros that allow the * + * user to use efficiently the type N_Vector without making * + * explicit references to its underlying representation. * + * * + * Part III of this file contains the prototype for the * + * initialization routine specific to this implementation * + * (M_EnvInit_Serial) as well as prototypes for the vector * + * kernels which operate on the serial N_Vector. These * + * prototypes are unique to this particular implementation of * + * the vector package. * + * * + * NOTES: * + * * + * The definitions of the generic M_Env and N_Vector structures * + * are in the header file nvector.h. * + * * + * The definitions of the types realtype and integertype are in * + * the header file sundialstypes.h and these may be changed * + * according to the user's needs. The sundialstypes.h file also * + * contains the definition for the type booleantype. * + * * + * N_Vector arguments to arithmetic kernels need not be * + * distinct. Thus, for example, the call * + * N_VLinearSum_Serial(a,x,b,y,y); y <- ax+by * + * is legal. * + * * + * This version of nvector is for the ordinary sequential * + * machine environment. In the documentation given below, N is * + * the length of all N_Vector parameters and x[i] denotes the * + * ith component of the N_Vector x, where 0 <= i <= N-1. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ +#ifndef included_nvector_serial_h +#define included_nvector_serial_h + +#include "nvector.h" /* Generic M_Env and N_Vector type definitions */ +#include "sundialstypes.h" + + +/**************************************************************** + * PART I: * + * Serial implementaion of M_Env and N_Vector * + ****************************************************************/ + +/* The serial implementation of the machine environment has + ID tag 'serial' */ +#define ID_TAG_S "serial" + +/* The serial implementation of the machine environment 'content' + structure contains the length of vectors */ + + struct _M_EnvSerialContent + { + integertype length; + }; + + typedef struct _M_EnvSerialContent *M_EnvSerialContent; + +/* The serial implementation of the N_Vector 'content' + structure contains the length of the vector and a pointer + to an array of realtype components */ + + struct _N_VectorSerialContent + { + integertype length; + realtype *data; + }; + + typedef struct _N_VectorSerialContent *N_VectorSerialContent; + +/**************************************************************** + * * + * PART II: Macros * + * NV_MAKE_S, NV_DISPOSE_S, NVS_MAKE_S, NVS_DISPOSE_S * + * ME_CONTENT_S, NV_CONTENT_S * + * NV_DATA_S, NV_LENGTH_S, NV_Ith_S * + *--------------------------------------------------------------* + * In the descriptions below, the following user declarations * + * are assumed: * + * * + * M_Env machenv; * + * N_Vector v, *vs; * + * realtype *v_data, **vs_data, r; * + * integertype v_len, s_len, i; * + * * + * (1) NV_MAKE_S, NV_DISPOSE_S * + * * + * These companion routines are used to create and * + * destroy an N_Vector with a component array v_data * + * allocated by the user. * + * * + * The call NV_MAKE_S(v, v_data, machenv) makes v an * + * N_Vector with component array v_data. The length of the * + * array is taken from machenv. * + * NV_MAKE_S stores the pointer v_data so that changes * + * made by the user to the elements of v_data are * + * simultaneously reflected in v. There is no copying of * + * elements. * + * * + * The call NV_DISPOSE_S(v) frees all memory associated * + * with v except for its component array. This memory was * + * allocated by the user and, therefore, should be * + * deallocated by the user. * + * * + * (2) NVS_MAKE_S, NVS_DISPOSE_S * + * * + * These companion routines are used to create and destroy * + * an array of N_Vectors with component vs_data allocated * + * by the user. * + * * + * The call NVS_MAKE_S(vs, vs_data, s_len, machenv) makes * + * vs an array of s_len N_Vectors, each with component * + * array vs_data[i] and array length taken from machenv. * + * NVS_MAKE_S stores the pointers vs_data[i] so that * + * changes made by the user to the elements of vs_data are * + * simultaneously reflected in vs. There is no copying of * + * elements. * + * * + * The call NVS_DISPOSE_S(vs) frees all memory associated * + * with vs except for its components' component array. * + * This memory was allocated by the user and, therefore, * + * should be deallocated by the user. * + * * + * (3) ME_CONTENT_S, NV_CONTENT_S * + * * + * These routines give access to the contents of the serial * + * machine environment and N_Vector, respectively. * + * * + * The assignment m_cont = ME_CONTENT_S(machenv) sets * + * m_cont to be a pointer to the serial machine * + * environment content structure. * + * * + * The assignment v_cont = NV_CONTENT_S(v) sets * + * v_cont to be a pointer to the serial N_Vector content * + * structure. * + * * + * (4) NV_DATA_S, NV_LENGTH_S * + * * + * These routines give individual access to the parts of * + * the content of a serial N_Vector. * + * * + * The assignment v_data=NV_DATA_S(v) sets v_data to be * + * a pointer to the first component of v. The assignment * + * NV_DATA_S(v)=v_data sets the component array of v to * + * be v_data by storing the pointer v_data. * + * * + * The assignment v_len=NV_LENGTH_S(v) sets v_len to be * + * the length of v. The call NV_LENGTH_S(v)=len_v sets * + * the length of v to be len_v. * + * * + * (5) NV_Ith_S * + * * + * In the following description, the components of an * + * N_Vector are numbered 0..N-1, where N is the length of * + * v. * + * * + * The assignment r=NV_Ith_S(v,i) sets r to be the value of * + * the ith component of v. The assignment NV_Ith_S(v,i)=r * + * sets the value of the ith component of v to be r. * + * * + * Notes.. * + * * + * Users who use the macros (1) and/or (2) must * + * #include since these macros expand to calls to * + * malloc and free. * + * * + * When looping over the components of an N_Vector v, it is * + * more efficient to first obtain the component array via * + * v_data=NV_DATA_S(v) and then access v_data[i] within the * + * loop than it is to use NV_Ith_S(v,i) within the loop. * + * * + * NV_MAKE_S and NV_DISPOSE_S are similar to N_VNew_Serial and * + * N_VFree_Serial, while NVS_MAKE_S and NVS_DISPOSE_S are * + * similar to N_VNew_S_Serial and N_VFree_S_Serial. The * + * difference is one of responsibility for component memory * + * allocation and deallocation. N_VNew_Serial allocates memory * + * for the N_Vector components and N_VFree_Serial frees the * + * component memory allocated by N_VNew_Serial. For NV_MAKE_S * + * and NV_DISPOSE_S, the component memory is allocated and * + * freed by the user of this package. Similar remarks hold for * + * NVS_MAKE_S, NVS_DISPOSE_S and N_VNew_S_Serial, * + * N_VFree_S_Serial. * + * * + ****************************************************************/ + +#define NV_MAKE_S(v, v_data, machenv) \ + v = (N_Vector) malloc(sizeof(*v)); \ + v->content = (N_VectorSerialContent) malloc(sizeof(struct _N_VectorSerialContent)); \ + v->content->data = v_data; \ + v->content->length = machenv->content->v_len; \ + v->menv = machenv + +#define NV_DISPOSE_S(v) \ + PHRQ_free((N_VectorSerialContent)(v->content)); \ + PHRQ_free(v) + +#define NVS_MAKE_S(vs, vs_data, s_len, machenv) \ + vs = (N_Vector_S) malloc(s_len*sizeof(N_Vector *)); \ + for ((int)is=0; iscontent) ) + +#define NV_CONTENT_S(v) ( (N_VectorSerialContent)(v->content) ) + +#define NV_LENGTH_S(v) ( NV_CONTENT_S(v)->length ) + +#define NV_DATA_S(v) ( NV_CONTENT_S(v)->data ) + +#define NV_Ith_S(v,i) ( NV_DATA_S(v)[i] ) + + +/**************************************************************** + * PART III: * + * Functions exported by nvector_serial * + ****************************************************************/ + +/*--------------------------------------------------------------* + * Routine : M_EnvInit_Serial * + *--------------------------------------------------------------* + * This function sets the content field of the machine * + * environment for the serial implementation to a structure of * + * type _MEnvSerialContent and attaches the vector operations * + * defined for this implementation. * + * * + * If successful, M_EnvInit_Serial returns a pointer of type * + * M_Env. This pointer should in turn be passed in any user * + * calls to N_VNew, or uses of the macros NV_MAKE_S and * + * NVS_MAKE_S. * + * * + *--------------------------------------------------------------* + * * + * vec_length is the length of the vector. * + * * + *--------------------------------------------------------------*/ + + M_Env M_EnvInit_Serial(integertype vec_length); + +/*--------------------------------------------------------------* + * Function M_EnvFree_Serial * + *--------------------------------------------------------------* + * Function to free the block of machine-dependent environment * + * information created by M_EnvInit_Serial. * + * Its only argument is the pointer machenv returned by * + * M_EnvInit_Serial. * + * * + *--------------------------------------------------------------*/ + + void M_EnvFree_Serial(M_Env machenv); + +/*--------------------------------------------------------------* + * Serial implementations of the vector operations * + * * + * For a complete description of each of the following routines * + * see the header file nvector.h * + *--------------------------------------------------------------*/ + + N_Vector N_VNew_Serial(integertype n, M_Env machEnv); + N_Vector_S N_VNew_S_Serial(integertype ns, integertype n, M_Env machEnv); + void N_VFree_Serial(N_Vector v); + void N_VFree_S_Serial(integertype ns, N_Vector_S vs); + N_Vector N_VMake_Serial(integertype n, realtype * v_data, M_Env machEnv); + void N_VDispose_Serial(N_Vector v); + realtype *N_VGetData_Serial(N_Vector v); + void N_VSetData_Serial(realtype * v_data, N_Vector v); + void N_VLinearSum_Serial(realtype a, N_Vector x, realtype b, N_Vector y, + N_Vector z); + void N_VConst_Serial(realtype c, N_Vector z); + void N_VProd_Serial(N_Vector x, N_Vector y, N_Vector z); + void N_VDiv_Serial(N_Vector x, N_Vector y, N_Vector z); + void N_VScale_Serial(realtype c, N_Vector x, N_Vector z); + void N_VAbs_Serial(N_Vector x, N_Vector z); + void N_VInv_Serial(N_Vector x, N_Vector z); + void N_VAddConst_Serial(N_Vector x, realtype b, N_Vector z); + realtype N_VDotProd_Serial(N_Vector x, N_Vector y); + realtype N_VMaxNorm_Serial(N_Vector x); + realtype N_VWrmsNorm_Serial(N_Vector x, N_Vector w); + realtype N_VMin_Serial(N_Vector x); + realtype N_VWL2Norm_Serial(N_Vector x, N_Vector w); + realtype N_VL1Norm_Serial(N_Vector x); + void N_VOneMask_Serial(N_Vector x); + void N_VCompare_Serial(realtype c, N_Vector x, N_Vector z); + booleantype N_VInvTest_Serial(N_Vector x, N_Vector z); + booleantype N_VConstrProdPos_Serial(N_Vector c, N_Vector x); + booleantype N_VConstrMask_Serial(N_Vector c, N_Vector x, N_Vector m); + realtype N_VMinQuotient_Serial(N_Vector num, N_Vector denom); + void N_VPrint_Serial(N_Vector x); + + + +#endif +/* +#ifdef __cplusplus +} +#endif +*/ +#endif /* _INC_NVECTOR_SERIAL_H */ diff --git a/phreeqcpp/parse.cpp b/phreeqcpp/parse.cpp new file mode 100644 index 00000000..cf149a4d --- /dev/null +++ b/phreeqcpp/parse.cpp @@ -0,0 +1,1053 @@ +#include "Phreeqc.h" +#include "phqalloc.h" + + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +parse_eq(char *eqn, struct elt_list **elt_ptr, int association) +/* ---------------------------------------------------------------------- */ +/* + * function to break equation up into component species + * returns species name, coefficient, and charge in global variable "rxn_list". + * Also returns pointer to elt_list structure array with list of elements + * and coefficients in species. + * rxn_list global variable, output with contiguous rxn_list structures, + * which is the reaction. Target species is first in + * list, coefficient is -1.0. + * + * Argurments: + * *eqn input, pointer to equation to be parsed. + * **elt_ptr output, pointer to contiguous elt_list structures, + * which is list of elements and coefficients in the + * target species. + * association input, TRUE or FALSE if reaction is an association reaction + */ +{ + int i; + LDBLE coef, l_z; + char c; + char *ptr; + char token[MAX_LENGTH]; + + paren_count = 0; +/* + * Remove white space + */ + squeeze_white(eqn); +/* + * Check for illegal characters + */ + for (i = 0; (c = eqn[i]) != '\0'; i++) + { + if (islegit(c) == FALSE) + { + error_string = sformatf( "Character is not allowed,\ + %c (octal: %o).", c, c); + error_msg(error_string, CONTINUE); + return (ERROR); + } + } + +/* + * Find coefficients, name, and charge for each species for lhs + */ + count_trxn = 0; + trxn.dz[0] = trxn.dz[1] = trxn.dz[2] = 0.0; + ptr = eqn; + c = ptr[0]; + for (;;) + { + if (c == '=') + break; + if (c == '\0') + { + error_string = sformatf( "Equation has no equal sign.\n\t%s", eqn); + error_msg(error_string, CONTINUE); + return (ERROR); + } + if (get_species(&ptr) == ERROR) + { + return (ERROR); + } + c = ptr[0]; + if (association == FALSE) + { + trxn.token[count_trxn].coef *= -1.0; + } + count_trxn++; + } +/* + * Get coefficient, name, and charge of species for dissociation reaction + */ + ptr++; + if (association == TRUE) + { + if (get_species(&ptr) == ERROR) + { + return (ERROR); + } + trxn.token[count_trxn].coef *= -1.0; + /* Swap species into first structure position */ + const char * char_ptr = trxn.token[0].name; + coef = trxn.token[0].coef; + l_z = trxn.token[0].z; + trxn.token[0].name = trxn.token[count_trxn].name; + trxn.token[0].coef = trxn.token[count_trxn].coef; + trxn.token[0].z = trxn.token[count_trxn].z; + trxn.token[count_trxn].name = char_ptr; + trxn.token[count_trxn].coef = coef; + trxn.token[count_trxn].z = l_z; + count_trxn++; + } +/* + * Get reaction species from rhs of equation + */ + c = ptr[0]; + for (;;) + { + if (c == '\0') + break; + if (get_species(&ptr) == ERROR) + { + return (ERROR); + } + c = ptr[0]; + if (association == TRUE) + { + trxn.token[count_trxn].coef *= -1.0; + } + count_trxn++; + } +/* + * Sort list of reaction species + */ + trxn_sort(); +/* + * Get elements in species or mineral formula + */ + count_elts = 0; + strcpy(token, trxn.token[0].name); + replace("(s)", "", token); + replace("(S)", "", token); + replace("(g)", "", token); + replace("(G)", "", token); + char *char_ptr = token; + + if (get_elts_in_species(&char_ptr, trxn.token[0].coef) == ERROR) + { + return (ERROR); + } +/* + * Sort elements in reaction and combine + */ + qsort(elt_list, (size_t) count_elts, (size_t) sizeof(struct elt_list), + elt_list_compare); + if (elt_list_combine() == ERROR) + return (ERROR); +/* + * Malloc space and store element data for return + */ + *elt_ptr = + (struct elt_list *) PHRQ_malloc((size_t) (count_elts + 1) * + sizeof(struct elt_list)); + if (*elt_ptr == NULL) + { + malloc_error(); + } + else + { + for (i = 0; i < count_elts; i++) + { + (*elt_ptr)[i].elt = elt_list[i].elt; + (*elt_ptr)[i].coef = -elt_list[i].coef; + } + (*elt_ptr)[count_elts].elt = NULL; + } +/* + * Debugging print of parsed equation + trxn_print(); + */ + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_eqn(int association) +/* ---------------------------------------------------------------------- */ +/* + * Check that equation is balanced reaction. Uses array "trxn.token" and + * assumes "count_trxn" is set to number of species in reaction. + * Charge and elements are checked. + * + * Arguments: + * + * association input, TRUE or FALSE if reaction is an association reaction. + */ +{ + int i; + int oops = 0; + LDBLE sumcharge; + + paren_count = 0; + count_elts = 0; +/* + * Check that coefficient of first species is -1.0 + */ + if (equal(trxn.token[0].coef, -1.0, TOL) == FALSE) + { + if (association == TRUE) + { + error_string = sformatf( + "Coefficient of first species on rhs is not equal to 1.0."); + error_msg(error_string, CONTINUE); + } + else + { + error_string = sformatf( + "Coefficient of mineral (first on lhs) is not equal to 1.0."); + error_msg(error_string, CONTINUE); + } + return (ERROR); + } +/* + * Go through all species in the reaction; sum the charge and store elements + */ + sumcharge = 0.0; + for (i = 0; i < count_trxn; i++) + { + sumcharge += (trxn.token[i].coef) * (trxn.token[i].z); + char * temp_name = string_duplicate(trxn.token[i].name); + char *t_ptr = temp_name; + if (get_elts_in_species(&t_ptr, trxn.token[i].coef) == ERROR) + { + free_check_null(temp_name); + return (ERROR); + } + free_check_null(temp_name); + } +/* + * Sort elements in reaction and combine + */ + qsort(elt_list, (size_t) count_elts, (size_t) sizeof(struct elt_list), + elt_list_compare); + if (elt_list_combine() == ERROR) + return (ERROR); +/* + * Check charge + */ + if (equal(sumcharge, 0.0, TOL) == FALSE) + { + error_string = sformatf( "Equation is not charge balanced, right - left = %7.4f moles charge", sumcharge); + error_msg(error_string, CONTINUE); + oops++; + } +/* + * Check mass balance + */ + for (i = 0; i < count_elts; i++) + { + if ((equal(elt_list[i].coef, 0.0, TOL) == FALSE) && + strncmp((elt_list[i].elt)->name, "e", MAX_LENGTH) != 0) + { + error_string = sformatf( + "Equation does not balance for element, %s: right - left = %7.4f moles", + (elt_list[i].elt)->name, elt_list[i].coef); + error_msg(error_string, CONTINUE); + oops++; + } + } + if (oops == 0) + { + return (OK); + } + else + { + return (ERROR); + } +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_charge(char *charge, LDBLE * l_z) +/* ---------------------------------------------------------------------- */ +/* + * Function takes character string and calculates the charge on + * the species. Charge can be in two forms: (1) string of "+" or "-" + * or (2) + or - followed by an integer. Charge is reduced to form (2) + * and stored in the pointer location *charge. + * + * Arguments: + * *charge input, string containing charge + * output, string containing charge of the form + or - + * followed by an integer, if integer greater than 1. + * *z output, value of charge. + * + * Returns: + * ERROR, + * OK. + */ +{ + int i; + char *ptr; + char c, c1; +/* + * Charge is zero + */ + if ((c = charge[0]) == '\0') + { + *l_z = 0.0; + return (OK); + } +/* + * Error check for + or - at start of string + */ + if (c != '+' && c != '-') + { + error_string = sformatf( + "Character string for charge does not start with + or -,\t%s.", + charge); + error_msg(error_string, CONTINUE); + return (ERROR); + } +/* + * Count string of +'s or -'s + */ + i = 0; + while (c == (c1 = charge[i++])); + i--; + if (c1 == '\0') + { + if (c == '-') + i = -i; + } + else + { +/* + * + or - followed by a number + */ + errno = 0; + i = strtol(charge, &ptr, 0); +/* + * Truncate fractional part of charge if all zeros + */ + if (*ptr != '\0') + { + if (*ptr == '.') + { + while (*(++ptr) != '\0') + { + if (*ptr != '0') + { + *l_z = strtod(charge, &ptr); + return (OK); + } + } +/* + * Non-numeric characters + */ + } + else + { + error_string = sformatf( + "Error in character string for charge, %s.", charge); + error_msg(error_string, CONTINUE); + return (ERROR); + } + } + } +/* + * Charge is zero, must have had +0 or -0 in eqn + */ + if (i == 0) + { + charge[0] = '\0'; + } +/* + * Charge is +1 or -1, single + or - + */ + if (abs(i) == 1) + { + charge[0] = c; + charge[1] = '\0'; + } +/* + * Abs(z)>1, set charge to + or - plus integer + */ + if (abs(i) > 1) + { + if (sprintf(charge, "%-+d", i) == EOF) + { + error_string = sformatf( + "Error converting charge to character string, %s.", + charge); + error_msg(error_string, CONTINUE); + return (ERROR); + } + } + *l_z = i; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_coef(LDBLE * coef, char **eqnaddr) +/* ---------------------------------------------------------------------- */ +/* + * Function reads through eqn and determines the coefficient of the next + * species. + * + * Arguments: + * *coef output, coefficient of next species. + * + * **eqnaddr input, pointer to a position in eqn to start parsing + * output, pointer to next position after coefficient + * + * Returns: + * ERROR, + * OK. + */ +{ + int i; + char c, c1; + char *ptr, *ptr1, *rest; + char token[MAX_LENGTH];; + + rest = *eqnaddr; + ptr = *eqnaddr; /* address of a position in eqn */ + c = *ptr; /* character in eqn */ + *coef = 0.0; +/* + * No leading sign or number + */ + if (isalpha((int) c) || + (c == '(') || (c == ')') || (c == '[') || (c == ']')) + { + *coef = 1.0; + return (OK); + } +/* + * Leading +, no digits + */ + c1 = *(ptr + 1); + if (c == '+' && + (isalpha((int) c1) || + (c1 == '(') || (c1 == ')') || (c1 == '[') || (c1 == ']'))) + { + *eqnaddr = ++ptr; + *coef = 1.0; + return (OK); + } +/* + * Leading -, no digits + */ + if (c == '-' && + (isalpha((int) c1) || + (c1 == '(') || (c1 == ')') || (c1 == '[') || (c1 == ']'))) + { + *eqnaddr = ++ptr; + *coef = -1.0; + return (OK); + } + i = 0; +/* + * Has number coefficient + */ + if (isdigit((int) c) || c == '+' || c == '-' || c == '.') + { + while (isdigit((int) c) || c == '+' || c == '-' || c == '.') + { + token[i++] = c; + if (i >= MAX_LENGTH) + { + error_string = sformatf( + "Coefficient has more than MAX_LENGTH characters."); + error_msg(error_string, CONTINUE); + return (ERROR); + } + c = *(++ptr); + } + token[i] = '\0'; + *eqnaddr = ptr; + errno = 0; + *coef = strtod(token, &ptr1); + if ((errno == ERANGE) || (*ptr1 != '\0')) + { + error_string = sformatf( + "Error converting coefficient in get_coef, %s.", token); + error_msg(error_string, CONTINUE); + return (ERROR); + } + return (OK); + } +/* + * None of the above, unknown construct + */ + error_string = sformatf( + "Illegal equation construct detected in get_coef.\n\t%s.", rest); + error_msg(error_string, CONTINUE); + return (ERROR); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_elt(char **t_ptr, char *element, int *i) +/* ---------------------------------------------------------------------- */ +/* + * Function reads an element name out of the equation string. + * An element name is composed of a capital letter followed by any number + * of lower case characters. + * + * Arguments: + * **t_ptr input, points to position in the equation to begin + * output, points to next character of equation after + * element name. + * *element input pointer to place to return element character string + */ +{ + char c; + + c = *(*t_ptr)++; + if (c == '\0') + { + error_string = sformatf( + "Empty string in get_elt. Expected an element name."); + error_msg(error_string, CONTINUE); + return (ERROR); + } +/* + * Load name into char array element + */ + element[0] = c; + *i = 1; + if (c == '[') + { + while ((c = (**t_ptr)) != ']') + { + element[*i] = c; + (*i)++; + (*t_ptr)++; + if ((c = (**t_ptr)) == ']') + { + element[*i] = c; + (*i)++; + (*t_ptr)++; + break; + } + else if (**t_ptr == '\0') + { + error_msg("No ending bracket (]) for element name", CONTINUE); + input_error++; + break; + } + } + while (islower((int) (c = (**t_ptr))) || c == '_') + { + element[*i] = c; + (*i)++; + (*t_ptr)++; + } + } + else + { + while (islower((int) (c = (**t_ptr))) || c == '_') + { + element[*i] = c; + (*i)++; + (*t_ptr)++; + } + } + element[*i] = '\0'; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_elts_in_species(char **t_ptr, LDBLE coef) +/* ---------------------------------------------------------------------- */ +{ +/* + * Makes a list of elements with their coefficients, stores elements + * in elt_list at position count_elts. Global variable count_elts is + * updated with each stored element. Also uses static global variable + * paren_count. + * + * Arguments: + * **t_ptr input, point in token string to start looking + * output, is next position to start looking + * coef input, coefficient to multiply subscripts by + */ + int i, count, l; + char c, c1; + LDBLE d; + char element[MAX_LENGTH]; + + while (((c = **t_ptr) != '+') && (c != '-') && (c != '\0')) + { + /* close parenthesis */ + if (c == ')') + { + paren_count--; + if (paren_count < 0) + { + error_string = sformatf( "Too many right parentheses."); + error_msg(error_string, CONTINUE); + return (ERROR); + } + (*t_ptr)++; + return (OK); + } + c1 = *((*t_ptr) + 1); + /* beginning of element name */ + if (isupper((int) c) || (c == 'e' && c1 == '-') || (c == '[')) + { +/* + * Get new element and subscript + */ + if (get_elt(t_ptr, element, &l) == ERROR) + { + return (ERROR); + } + if (count_elts >= max_elts) + { + space((void **) ((void *) &elt_list), count_elts, &max_elts, + sizeof(struct elt_list)); + } + elt_list[count_elts].elt = element_store(element); + if (get_num(t_ptr, &d) == ERROR) + { + return (ERROR); + } + elt_list[count_elts].coef = d * coef; + count_elts++; +/* + * Expand working space for elements if necessary + */ + if (count_elts >= max_elts) + { + space((void **) ((void *) &elt_list), count_elts, &max_elts, + sizeof(struct elt_list)); + } + continue; + } +/* + * Open parentheses + */ + if (c == '(') + { + count = count_elts; + if (c1 == ')') + { + error_string = sformatf( "Empty parentheses."); + warning_msg(error_string); + } + paren_count++; + (*t_ptr)++; + if (get_elts_in_species(t_ptr, coef) == ERROR) + { + return (ERROR); + } + if (get_num(t_ptr, &d) == ERROR) + { + return (ERROR); + } + for (i = count; i < count_elts; i++) + { + elt_list[i].coef *= d; + } + continue; + } +/* + * Colon + */ + if (c == ':') + { + count = count_elts; + (*t_ptr)++; + if (get_num(t_ptr, &d) == ERROR) + { + return (ERROR); + } + if (get_elts_in_species(t_ptr, coef) == ERROR) + { + return (ERROR); + } + for (i = count; i < count_elts; i++) + { + elt_list[i].coef *= d; + } + continue; + } +/* + * Not beginning of element and not opening paren + */ + error_string = sformatf( + "Parsing error in get_elts_in_species, unexpected character, %c.", + c); + error_msg(error_string, CONTINUE); + input_error++; + return (ERROR); + } + if (paren_count != 0) + { + error_string = sformatf( "Unbalanced parentheses."); + error_msg(error_string, CONTINUE); + input_error++; + return (ERROR); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +get_secondary(char **t_ptr, char *element, int *i) +/* ---------------------------------------------------------------------- */ +/* + * Function reads an element name out of the equation string. + * An element name is composed of a capital letter followed by any number + * of lower case characters. + * + * Arguments: + * **t_ptr input, points to position in the equation to begin + * output, points to next character of equation after + * element name. + * *element input pointer to place to return element character string + */ +{ + int j; + char c; + char *ptr; + + c = *(*t_ptr)++; + if (c == '\0') + { + error_string = sformatf( + "Empty string in get_elt. Expected an element name."); + error_msg(error_string, CONTINUE); + input_error++; + return (ERROR); + } +/* + * Load name into char array element + */ + element[0] = c; + *i = 1; + if (c == '[') + { + while ((c = (**t_ptr)) != ']') + { + element[*i] = c; + (*i)++; + (*t_ptr)++; + if ((c = (**t_ptr)) == ']') + { + element[*i] = c; + (*i)++; + (*t_ptr)++; + c = (**t_ptr); + break; + } + else if ((c = (**t_ptr)) == '\0') + { + error_msg("Did not find ending bracket (])", CONTINUE); + input_error++; + return (ERROR); + } + } + while (islower((int) (c = (**t_ptr))) || c == '_') + { + element[*i] = c; + (*i)++; + (*t_ptr)++; + } + } + else + { + while (islower((int) (c = (**t_ptr))) || c == '_') + { + element[*i] = c; + (*i)++; + (*t_ptr)++; + } + } +/* + * Check if secondary master species element + */ + j = *i; + ptr = *t_ptr; + if (c == '(') + { + /* copy parenthesis */ + element[*i] = c; + (*i)++; + (*t_ptr)++; + /* copy number */ + for (;;) + { + c = **t_ptr; + if (isdigit((int) c) || c == '-' || c == '.') + { + element[*i] = c; + (*i)++; + (*t_ptr)++; + } + else if (c == '+') + { + (*t_ptr)++; + } + else + { + break; + } + } + /* go back to before parenthesis */ + if (c != ')') + { + *i = j; + *t_ptr = ptr; + /* put in closing parenthesis */ + } + else + { + element[*i] = c; + (*i)++; + (*t_ptr)++; + } + } + element[*i] = '\0'; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_secondary_in_species(char **t_ptr, LDBLE coef) +/* ---------------------------------------------------------------------- */ +{ +/* + * Makes a list of elements with their coefficients, stores elements + * in elt_list at position count_elts. Global variable count_elts is + * updated with each stored element. Also uses static global variable + * paren_count. + * + * Arguments: + * **t_ptr input, point in token string to start looking + * output, is next position to start looking + * coef input, coefficient to multiply subscripts by + */ + int i, count, l; + char c, c1; + LDBLE d; + char element[MAX_LENGTH]; + + while (((c = **t_ptr) != '+') && (c != '-') && (c != '\0')) + { + /* close parenthesis */ + if (c == ')') + { + paren_count--; + if (paren_count < 0) + { + error_string = sformatf( "Too many right parentheses."); + error_msg(error_string, CONTINUE); + input_error++; + return (ERROR); + } + (*t_ptr)++; + return (OK); + } + c1 = *((*t_ptr) + 1); + /* beginning of element name */ + if (isupper((int) c) || c == '[' || (c == 'e' && c1 == '-')) + { +/* + * Get new element and subscript + */ + if (get_secondary(t_ptr, element, &l) == ERROR) + { + return (ERROR); + } + elt_list[count_elts].elt = element_store(element); + if (get_num(t_ptr, &d) == ERROR) + { + return (ERROR); + } + elt_list[count_elts].coef = d * coef; + count_elts++; +/* + * Expand working space for elements if necessary + */ + if (count_elts >= max_elts) + { + space((void **) ((void *) &elt_list), count_elts, &max_elts, + sizeof(struct elt_list)); + } + continue; + } +/* + * Open parentheses + */ + if (c == '(') + { + count = count_elts; + if (c1 == ')') + { + error_string = sformatf( "Empty parentheses."); + warning_msg(error_string); + } + paren_count++; + (*t_ptr)++; + if (get_secondary_in_species(t_ptr, coef) == ERROR) + { + return (ERROR); + } + if (get_num(t_ptr, &d) == ERROR) + { + return (ERROR); + } + for (i = count; i < count_elts; i++) + { + elt_list[i].coef *= d; + } + continue; + } +/* + * Colon + */ + if (c == ':') + { + count = count_elts; + (*t_ptr)++; + if (get_num(t_ptr, &d) == ERROR) + { + return (ERROR); + } + if (get_secondary_in_species(t_ptr, coef) == ERROR) + { + return (ERROR); + } + for (i = count; i < count_elts; i++) + { + elt_list[i].coef *= d; + } + continue; + } +/* + * Not beginning of element and not opening paren + */ + error_string = sformatf( + "Parsing error in get_secondary_in_species, unexpected character, %c.", + c); + error_msg(error_string, CONTINUE); + return (ERROR); + } + if (paren_count != 0) + { + error_string = sformatf( "Unbalanced parentheses."); + error_msg(error_string, CONTINUE); + return (ERROR); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_num(char **t_ptr, LDBLE * num) +/* ---------------------------------------------------------------------- */ +/* + * Function reads through a string looking for leading numeric field + * if no numeric field is found, the number is set to 1.0. + * + * Arguments: + * + * **t_ptr input, points to a position in a character string from which + * a number is to be extracted. + * output, points to next position to be parsed. + * *num address where the number is to be stored. + * + * Returns: + * ERROR, + * OK. + */ +{ + int i, decimal; + char c; + char *ptr1; + char token[MAX_LENGTH]; + + *num = 1.0; + i = 0; + c = **t_ptr; + decimal = 0; + if (isdigit((int) c) || (c == '.')) + { + while (isdigit((int) c) || (c == '.')) + { + if (c == '.') + decimal++; + if (decimal > 1) + break; + token[i++] = c; + /* check number length */ + if (i >= MAX_LENGTH) + { + error_string = sformatf( + "Number was greater than MAX_LENGTH characters."); + error_msg(error_string, CONTINUE); + input_error++; + return (ERROR); + } + c = *(++(*t_ptr)); + } + token[i] = '\0'; + errno = 0; + *num = strtod(token, &ptr1); + if (errno == ERANGE) + { + error_string = sformatf( "Converting number in get_num, %s.", token); + input_error++; + error_msg(error_string, CONTINUE); + return (ERROR); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_species(char **ptr) +/* ---------------------------------------------------------------------- */ +{ +/* Function reads next species out of the equation, including optional + * preceding coefficient and optional trailing charge. Data are + * store in trxn.token[count]. + * + * Arguments: + * **ptr input, points to the position in the equation to pick up the species. + * output, points to the next character after the species charge. + * + */ + char string[MAX_LENGTH]; + int l; + + if (count_trxn + 1 >= max_trxn) + { + space((void **) ((void *) &(trxn.token)), count_trxn + 1, &max_trxn, + sizeof(struct rxn_token_temp)); + } + /* coefficient */ + if (get_coef(&(trxn.token[count_trxn].coef), ptr) == ERROR) + { + return (ERROR); + } + /* name and charge */ + if (get_token(ptr, string, &trxn.token[count_trxn].z, &l) == ERROR) + { + return (ERROR); + } + trxn.token[count_trxn].name = string_hsave(string); + /* + trxn.token[count_trxn].z = 0; + trxn.token[count_trxn].s = NULL; + trxn.token[count_trxn].unknown = NULL; + */ + return (OK); +} diff --git a/phreeqcpp/phast.xsd b/phreeqcpp/phast.xsd new file mode 100644 index 00000000..1e6c660e --- /dev/null +++ b/phreeqcpp/phast.xsd @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/phreeqcpp/phqalloc.cpp b/phreeqcpp/phqalloc.cpp new file mode 100644 index 00000000..3823f397 --- /dev/null +++ b/phreeqcpp/phqalloc.cpp @@ -0,0 +1,306 @@ +#define INCLUDE_PHRQALLOC_H +#include "Phreeqc.h" +#include +#include +#include + +#if defined(PHREEQCI_GUI) +#define _CRTDBG_MAP_ALLOC +#include +#endif +#if defined(USE_PHRQ_ALLOC) +/* ---------------------------------------------------------------------- */ +#if !defined(NDEBUG) +void * Phreeqc:: +PHRQ_malloc(size_t size, const char *szFileName, int nLine) +#else +void *Phreeqc:: +PHRQ_malloc(size_t size) +#endif +/* ---------------------------------------------------------------------- */ +{ + PHRQMemHeader *p; + + assert((s_pTail == NULL) || (s_pTail->pNext == NULL)); + + p = (PHRQMemHeader *) malloc(sizeof(PHRQMemHeader) + size); + + if (p == NULL) + return NULL; +#if !defined(NDEBUG) + memset(p, 0, sizeof(PHRQMemHeader) + size); +#endif + p->pNext = NULL; + + if ((p->pPrev = s_pTail) != NULL) + { + s_pTail->pNext = p; + } + + p->size = sizeof(PHRQMemHeader) + size; +#if !defined(NDEBUG) + p->szFileName = (char *) malloc(strlen(szFileName) + 1); + if (p->szFileName) + strcpy(p->szFileName, szFileName); + p->nLine = nLine; +#endif + + s_pTail = p; + p++; + return ((void *) (p)); +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +PHRQ_free(void *ptr) +/* ---------------------------------------------------------------------- */ +{ + PHRQMemHeader *p; + + assert((s_pTail == NULL) || (s_pTail->pNext == NULL)); + + if (ptr == NULL) + return; + + p = (PHRQMemHeader *) ptr - 1; + + if (p->pNext != NULL) + { + p->pNext->pPrev = p->pPrev; + } + else + { + /* Handle special case when (p == s_pTail) */ + assert(s_pTail != NULL); + assert(p == s_pTail); + s_pTail = p->pPrev; + } + + if (p->pPrev) + { + p->pPrev->pNext = p->pNext; + } + +#if !defined(NDEBUG) + free(p->szFileName); +#endif + + free(p); +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +PHRQ_free_all(void) +/* ---------------------------------------------------------------------- */ +{ + assert((s_pTail == NULL) || (s_pTail->pNext == NULL)); + + std::ostringstream ostrm; + + if (s_pTail == NULL) + { +#if !defined(NDEBUG) + output_msg("No memory leaks\n"); +#endif + return; + } + while (s_pTail->pPrev != NULL) + { + s_pTail = s_pTail->pPrev; +#if !defined(NDEBUG) + ostrm.clear(); + ostrm << s_pTail->pNext->szFileName << "(" << s_pTail->pNext->nLine; + ostrm << ") " << (void *) (s_pTail->pNext + 1) << ": freed in PHRQ_free_all\n"; + output_msg(ostrm.str().c_str()); + free(s_pTail->pNext->szFileName); +#endif + free(s_pTail->pNext); + } + +#if !defined(NDEBUG) + ostrm.clear(); + ostrm << s_pTail->szFileName << "(" << s_pTail->nLine; + ostrm << ") " << (void *) (s_pTail + 1) << ": freed in PHRQ_free_all\n"; + output_msg(ostrm.str().c_str()); + free(s_pTail->szFileName); +#endif + free(s_pTail); + s_pTail = NULL; +} + +/* ---------------------------------------------------------------------- */ +void * Phreeqc:: +PHRQ_calloc(size_t num, size_t size +#if !defined(NDEBUG) + , const char *szFileName, int nLine +#endif + ) +/* ---------------------------------------------------------------------- */ +{ + PHRQMemHeader *p; + + assert((s_pTail == NULL) || (s_pTail->pNext == NULL)); + + p = (PHRQMemHeader *) malloc(sizeof(PHRQMemHeader) + size * num); + + if (p == NULL) + return NULL; + + p->pNext = NULL; + + if ((p->pPrev = s_pTail) != NULL) + { + s_pTail->pNext = p; + } + + p->size = sizeof(PHRQMemHeader) + size * num; + +#if !defined(NDEBUG) + p->szFileName = (char *) malloc(strlen(szFileName) + 1); + if (p->szFileName) + strcpy(p->szFileName, szFileName); + p->nLine = nLine; +#endif + + s_pTail = p; + p++; + return memset(p, 0, size * num); +} + +/* ---------------------------------------------------------------------- */ +void * Phreeqc:: +PHRQ_realloc(void *ptr, size_t size +#if !defined(NDEBUG) + , const char *szFileName, int nLine +#endif + ) +/* ---------------------------------------------------------------------- */ +{ + PHRQMemHeader *p; + size_t new_size; + size_t old_size; + + if (ptr == NULL) + { + return PHRQ_malloc(size +#if !defined(NDEBUG) + , szFileName, nLine +#endif + ); + } + + assert((s_pTail == NULL) || (s_pTail->pNext == NULL)); + + p = (PHRQMemHeader *) ptr - 1; + + new_size = sizeof(PHRQMemHeader) + size; + + old_size = p->size; + p = (PHRQMemHeader *) realloc(p, new_size); + if (p != NULL) + { + p->size = new_size; +#if !defined(NDEBUG) + if (new_size > old_size) + { + memset((char *) p + old_size, 0, new_size - old_size); + } +#endif + } + + if (p == NULL) + return NULL; + + if (p->pPrev != NULL) + { + p->pPrev->pNext = p; + } + + if (p->pNext != NULL) + { + p->pNext->pPrev = p; + } + else + { + s_pTail = p; + } + +#if !defined(NDEBUG) + free(p->szFileName); + p->szFileName = (char *) malloc(strlen(szFileName) + 1); + if (p->szFileName) + strcpy(p->szFileName, szFileName); + p->nLine = nLine; +#endif + + p++; + return ((void *) (p)); +} +#else /* USE_PHRQ_ALLOC */ +/* ---------------------------------------------------------------------- */ +void *Phreeqc:: +#if !defined(NDEBUG) +PHRQ_malloc(size_t size, const char *szFileName, int nLine) +#else +PHRQ_malloc(size_t size) +#endif +/* ---------------------------------------------------------------------- */ +{ +#if !defined(NDEBUG) && defined(WIN32_MEMORY_DEBUG) + return _malloc_dbg(size, _NORMAL_BLOCK, szFileName, nLine); +#else + return malloc(size); +#endif +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +PHRQ_free(void *ptr) +/* ---------------------------------------------------------------------- */ +{ +#if !defined(NDEBUG) && defined(WIN32_MEMORY_DEBUG) + _free_dbg(ptr, _NORMAL_BLOCK); +#else + free(ptr); +#endif +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +PHRQ_free_all(void) +/* ---------------------------------------------------------------------- */ +{ +} + +/* ---------------------------------------------------------------------- */ +void * Phreeqc:: +#if !defined(NDEBUG) +PHRQ_calloc(size_t num, size_t size, const char *szFileName, int nLine) +#else +PHRQ_calloc(size_t num, size_t size) +#endif +/* ---------------------------------------------------------------------- */ +{ +#if !defined(NDEBUG) && defined(WIN32_MEMORY_DEBUG) + return _calloc_dbg(num, size, _NORMAL_BLOCK, szFileName, nLine); +#else + return calloc(num, size); +#endif +} + +/* ---------------------------------------------------------------------- */ +void * Phreeqc:: +#if !defined(NDEBUG) +PHRQ_realloc(void *ptr, size_t size, const char *szFileName, int nLine) +#else +PHRQ_realloc(void *ptr, size_t size) +#endif +/* ---------------------------------------------------------------------- */ +{ +#if !defined(NDEBUG) && defined(WIN32_MEMORY_DEBUG) + return _realloc_dbg(ptr, size, _NORMAL_BLOCK, szFileName, nLine); +#else + return realloc(ptr, size); +#endif +} +#endif /* USE_PHRQ_ALLOC */ diff --git a/phreeqcpp/phqalloc.h b/phreeqcpp/phqalloc.h new file mode 100644 index 00000000..12e0454d --- /dev/null +++ b/phreeqcpp/phqalloc.h @@ -0,0 +1,47 @@ +#if !defined (INCLUDE_PHRQALLOC_H) +#define INCLUDE_PHRQALLOC_H + +#if defined (USE_PHRQ_ALLOC) + +#if !defined(NDEBUG) +void *PHRQ_malloc(size_t, const char *, int); +void *PHRQ_calloc(size_t, size_t, const char *, int); +void *PHRQ_realloc(void *, size_t, const char *, int); +#else +extern void *PHRQ_malloc(size_t); +extern void *PHRQ_calloc(size_t, size_t); +extern void *PHRQ_realloc(void *, size_t); +#endif + +extern void PHRQ_free(void *); +extern void PHRQ_free_all(void); + +#if !defined(NDEBUG) +#define PHRQ_malloc(s) PHRQ_malloc(s, __FILE__, __LINE__) +#define PHRQ_calloc(c, s) PHRQ_calloc(c, s, __FILE__, __LINE__) +#define PHRQ_realloc(p, s) PHRQ_realloc(p, s, __FILE__, __LINE__) +#endif + +#else /* defined (USE_PHRQ_ALLOC) */ + +#if !defined(NDEBUG) +void *PHRQ_malloc(size_t, const char *, int); +void *PHRQ_calloc(size_t, size_t, const char *, int); +void *PHRQ_realloc(void *, size_t, const char *, int); +#else +extern void *PHRQ_malloc(size_t); +extern void *PHRQ_calloc(size_t, size_t); +extern void *PHRQ_realloc(void *, size_t); +#endif +void PHRQ_free(void *); +void PHRQ_free_all(void); + +#if !defined(NDEBUG) +#define PHRQ_malloc(s) PHRQ_malloc(s, __FILE__, __LINE__) +#define PHRQ_calloc(c, s) PHRQ_calloc(c, s, __FILE__, __LINE__) +#define PHRQ_realloc(p, s) PHRQ_realloc(p, s, __FILE__, __LINE__) +#endif + +#endif /* defined (USE_PHRQ_ALLOC) */ + +#endif /* !defined (INCLUDE_PHRQALLOC_H) */ diff --git a/phreeqcpp/phreeqc.Makefile b/phreeqcpp/phreeqc.Makefile new file mode 100644 index 00000000..ad676f3a --- /dev/null +++ b/phreeqcpp/phreeqc.Makefile @@ -0,0 +1,237 @@ +# +# Make file for PHREEQC +# +# $(CURDIR) is current directory +TOPDIR:=$(CURDIR)/.. +PROGRAM=phreeqc +EXE=$(TOPDIR)/bin/$(PROGRAM) +SRC=. + +# Do not print commands before executing +#.SILENT: + +# Provides compatibility with GNU make +#.SUFFIXES: + +# Change to pawd if using automounter +PWD=pwd + +# Change to C compiler on your system +CC=g++ + +# Change to C compiler options on your system +#CCFLAGS=-O3 -fno-signed-zeros -Wall -ansi -pedantic -std=c99 -DHAVE_ISFINITE -DNDEBUG # -frounding-math # -pg +CCFLAGS=-O3 -Wall -ansi -pedantic -DHAVE_ISFINITE -DNDEBUG # -frounding-math # -pg -std=c99 + +# Remove the following definition if you do not have +# gmp (Gnu Multiple Precision) package on your system +INVERSE_CL1MP=TRUE + +LOADFLAGS= -lm # -pg +LOADFLAGS+=$(call ld-option, -Wl$(comma)--hash-style=sysv) + +#.c.o : +# ${CC} ${CCFLAGS} -c -o $@ $< +%.o : $(SRC)/%.c + ${CC} ${CCFLAGS} -c -o $@ $< +%.o : $(SRC)/%.cpp + ${CC} ${CCFLAGS} -c -o $@ $< + +# Location to copy scripts on installation +BINDIR=$(HOME)/bin + +OBJECTS= \ + advection.o \ + basic.o \ + basicsubs.o \ + cl1.o \ + cvdense.o \ + cvode.o \ + dense.o \ + dw.o \ + input.o \ + integrate.o \ + inverse.o \ + isotopes.o \ + kinetics.o \ + main.o \ + mainsubs.o \ + model.o \ + nvector.o \ + nvector_serial.o \ + p2clib.o \ + parse.o \ + phqalloc.o \ + phreeqc_files.o \ + pitzer.o \ + pitzer_structures.o \ + prep.o \ + print.o \ + read.o \ + readtr.o \ + sit.o \ + smalldense.o \ + spread.o \ + step.o \ + structures.o \ + sundialsmath.o \ + tally.o \ + tidy.o \ + transport.o \ + utilities.o + +ifdef INVERSE_CL1MP + LOADFLAGS += /z/parkplace/usr/lib/libgmp.a + CCFLAGS += -DINVERSE_CL1MP + OBJECTS += cl1mp.o +endif + +all: $(EXE) + +install: +# +# Create directory for binary and scripts if necessary +# + if [ ! -d $(BINDIR) ]; \ + then \ + mkdir $(BINDIR); \ + echo Created directory $(BINDIR); \ + fi +# +# Put path name of current directory into script for +# locating data files, put script in top directory, +# put symbolic link in BINDIR +# + cd $(TOPDIR); dir1=`$(PWD)`/bin; cd $(BINDIR); if [ `$(PWD)` = $$dir1 ]; \ + then \ + echo "Can not install to $(BINDIR). Choose another directory."; \ + exit 4 ; \ + fi + cd $(TOPDIR); \ + rm -f $(BINDIR)/$(PROGRAM); \ + rm -f $(PROGRAM); \ + sed "s?TOPDIR=.\{0,80\}?TOPDIR=`$(PWD)`?" bin/$(PROGRAM).orig > $(PROGRAM); \ + chmod 755 $(PROGRAM) + cd $(TOPDIR); dir1=`$(PWD)`; cd $(BINDIR); if [ `$(PWD)` != $$dir1 ]; then \ + ln -s $$dir1/$(PROGRAM) $(BINDIR); \ + echo Symbolic link for $(PROGRAM) has been placed in $(BINDIR). ; \ + fi +# +# Check that all necessary files are in place. +# + if [ -f $(BINDIR)/$(PROGRAM) -a \ + -f $(TOPDIR)/bin/$(PROGRAM) -a \ + -f $(TOPDIR)/$(PROGRAM) ]; \ + then echo "Installation complete."; \ + else echo "Installation incomplete."; \ + for FILE in $(BINDIR)/$(PROGRAM) \ + $(TOPDIR)/bin/$(PROGRAM) $(TOPDIR)/$(PROGRAM) ; \ + do \ + if [ ! -f $$FILE ]; then echo $$FILE is missing.; fi; \ + done; \ + fi + echo "Add directory $(BINDIR) to PATH." + +clean: + rm -f $(BINDIR)/$(PROGRAM) + rm -f $(TOPDIR)/bin/$(PROGRAM) + rm -f $(TOPDIR)/src/*.o + rm -f $(SUN_DIR)/bin/$(PROGRAM) + rm -f $(SUN_DIR)/src/*.o + rm -f $(TOPDIR)/src/$(PROGRAM) + rm -f $(DEBUG_DIR)/*.o + echo Removed object and executable files generated by make. + +$(EXE): $(OBJECTS) + echo $(TOPDIR) + $(CC) -o $(EXE) $(OBJECTS) $(LOADFLAGS) # -L/z/parkplace/home/dlpark/packages/efence -lefence + echo Compilation complete, $(EXE). + +advection.o: $(SRC)/advection.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +basic.o: $(SRC)/basic.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h $(SRC)/p2c.h $(SRC)/basic.h + +basicsubs.o: $(SRC)/basicsubs.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +cl1.o: $(SRC)/cl1.c $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqtype.h + +cl1mp.o: $(SRC)/cl1mp.c $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqtype.h + +cvdense.o: $(SRC)/cvdense.c $(SRC)/cvdense.h $(SRC)/cvode.h $(SRC)/sundialstypes.h $(SRC)/phrqtype.h $(SRC)/nvector.h $(SRC)/dense.h $(SRC)/smalldense.h $(SRC)/sundialsmath.h $(SRC)/output.h $(SRC)/phqalloc.h + +cvode.o: $(SRC)/cvode.c $(SRC)/cvode.h $(SRC)/sundialstypes.h $(SRC)/phrqtype.h $(SRC)/nvector.h $(SRC)/sundialsmath.h $(SRC)/output.h $(SRC)/kinetics.h $(SRC)/phqalloc.h + +dense.o: $(SRC)/dense.c $(SRC)/sundialstypes.h $(SRC)/phrqtype.h $(SRC)/sundialsmath.h $(SRC)/dense.h $(SRC)/smalldense.h $(SRC)/output.h $(SRC)/phqalloc.h + +dw.o: $(SRC)/dw.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phrqproto.h $(SRC)/output.h $(SRC)/pitzer.h + +input.o: $(SRC)/input.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/input.h $(SRC)/output.h $(SRC)/phrqproto.h $(SRC)/phqalloc.h + +integrate.o: $(SRC)/integrate.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +inverse.o: $(SRC)/inverse.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +isotopes.o: $(SRC)/isotopes.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +kinetics.o: $(SRC)/kinetics.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h $(SRC)/sundialstypes.h $(SRC)/cvode.h $(SRC)/nvector.h $(SRC)/cvdense.h $(SRC)/dense.h $(SRC)/smalldense.h $(SRC)/nvector_serial.h $(SRC)/kinetics.h + +main.o: $(SRC)/main.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/output.h $(SRC)/phrqproto.h $(SRC)/input.h + +mainsubs.o: $(SRC)/mainsubs.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h $(SRC)/input.h + +model.o: $(SRC)/model.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +nvector.o: $(SRC)/nvector.c $(SRC)/nvector.h $(SRC)/sundialstypes.h $(SRC)/phrqtype.h $(SRC)/output.h + +nvector_serial.o: $(SRC)/nvector_serial.c $(SRC)/nvector_serial.h $(SRC)/nvector.h $(SRC)/sundialstypes.h $(SRC)/phrqtype.h $(SRC)/sundialsmath.h $(SRC)/output.h $(SRC)/phqalloc.h + +output.o: $(SRC)/output.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/output.h $(SRC)/phrqproto.h $(SRC)/phqalloc.h + +p2clib.o: $(SRC)/p2clib.c $(SRC)/p2c.h $(SRC)/output.h + +parse.o: $(SRC)/parse.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +phqalloc.o: $(SRC)/phqalloc.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/output.h + +phreeqc_files.o: $(SRC)/phreeqc_files.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h $(SRC)/input.h + +pitzer.o: $(SRC)/pitzer.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h $(SRC)/pitzer_structures.h $(SRC)/pitzer.h + +pitzer_structures.o: $(SRC)/pitzer_structures.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h $(SRC)/pitzer_structures.h $(SRC)/pitzer.h + +prep.o: $(SRC)/prep.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +print.o: $(SRC)/print.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h $(SRC)/pitzer.h + +read.o: $(SRC)/read.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +readtr.o: $(SRC)/readtr.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +sit.o: $(SRC)/sit.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h $(SRC)/pitzer_structures.h $(SRC)/pitzer.h + +smalldense.o: $(SRC)/smalldense.c $(SRC)/smalldense.h $(SRC)/sundialstypes.h $(SRC)/phrqtype.h $(SRC)/sundialsmath.h $(SRC)/output.h $(SRC)/phqalloc.h + +spread.o: $(SRC)/spread.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +step.o: $(SRC)/step.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +structures.o: $(SRC)/structures.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +sundialsmath.o: $(SRC)/sundialsmath.c $(SRC)/sundialsmath.h $(SRC)/sundialstypes.h $(SRC)/phrqtype.h $(SRC)/output.h + +tally.o: $(SRC)/tally.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +tidy.o: $(SRC)/tidy.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +transport.o: $(SRC)/transport.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +utilities.o: $(SRC)/utilities.c $(SRC)/global.h $(SRC)/phrqtype.h $(SRC)/global_structures.h $(SRC)/phqalloc.h $(SRC)/output.h $(SRC)/phrqproto.h + +-include $(CURDIR)/distribution.mk + +#ld-option +# Usage: ldflags += $(call ld-option, -Wl$(comma)--hash-style=sysv) +comma=, +ld-option = $(shell if $(CC) $(1) \ + -nostdlib -o /dev/null -xc /dev/null \ + > /dev/null 2>&1 ; then echo "$(1)" ; else echo "$(2)"; fi) diff --git a/phreeqcpp/phreex.ico b/phreeqcpp/phreex.ico new file mode 100644 index 00000000..8044dda4 Binary files /dev/null and b/phreeqcpp/phreex.ico differ diff --git a/phreeqcpp/pitzer.cpp b/phreeqcpp/pitzer.cpp new file mode 100644 index 00000000..2becc326 --- /dev/null +++ b/phreeqcpp/pitzer.cpp @@ -0,0 +1,2653 @@ +#include "Phreeqc.h" +#include "phqalloc.h" +#include "Exchange.h" +#include "Solution.h" +#define PITZER_LISTS +#define PITZER + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +pitzer_init(void) +/* ---------------------------------------------------------------------- */ +{ + int i; +/* + * Initialization for pitzer + */ + pitzer_model = FALSE; + max_pitz_param = 100; + count_pitz_param = 0; + use_etheta = TRUE; + space((void **) ((void *) &pitz_params), INIT, &max_pitz_param, + sizeof(struct pitz_param *)); + + max_theta_param = 100; + count_theta_param = 0; + space((void **) ((void *) &theta_params), INIT, &max_theta_param, + sizeof(struct theta_param *)); + + ICON = TRUE; + OTEMP = -100.; + OPRESS = -100.; + for (i = 0; i < 23; i++) + { + BK[i] = 0.0; + DK[i] = 0.0; + } + pitzer_pe = FALSE; + VP = 0; + DW0 = 0; + return OK; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +pitzer_tidy(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Make lists of species for cations, anions, neutral + */ + const char *string1, *string2; + int i, j, order; + int i0, i1, i2; + int count_pos, count_neg, count_neut, count[3], jj; + LDBLE z0, z1; + struct pitz_param *pzp_ptr; + struct theta_param *theta_param_ptr; + /* + * Ensure new parameters are calculated + */ + OTEMP = -100.; + OPRESS = -100.; + /* + * allocate pointers to species structures + */ + if (spec != NULL) + spec = (struct species **) free_check_null(spec); + spec = + (struct species **) + PHRQ_malloc((size_t) (3 * count_s * sizeof(struct species *))); + if (spec == NULL) + malloc_error(); + for (i = 0; i < 3 * count_s; i++) + spec[i] = NULL; + cations = spec; + neutrals = &(spec[count_s]); + anions = &(spec[2 * count_s]); + MAXCATIONS = count_s; + FIRSTANION = 2 * count_s; + MAXNEUTRAL = count_s; + count_cations = 0; + count_anions = 0; + count_neutrals = 0; + if (itmax < 200) + itmax = 200; + /* + * allocate other arrays for Pitzer + */ + if (IPRSNT != NULL) + IPRSNT = (int *) free_check_null(IPRSNT); + IPRSNT = (int *) PHRQ_malloc((size_t) (3 * count_s * sizeof(int))); + if (IPRSNT == NULL) + malloc_error(); + if (M != NULL) + M = (LDBLE *) free_check_null(M); + M = (LDBLE *) PHRQ_malloc((size_t) (3 * count_s * sizeof(LDBLE))); + if (M == NULL) + malloc_error(); + if (LGAMMA != NULL) + LGAMMA = (LDBLE *) free_check_null(LGAMMA); + LGAMMA = (LDBLE *) PHRQ_malloc((size_t) (3 * count_s * sizeof(LDBLE))); + if (LGAMMA == NULL) + malloc_error(); + + + for (i = 0; i < count_s; i++) + { + if (s[i] == s_eminus) + continue; + if (s[i] == s_h2o) + continue; + if (s[i]->type == EX || s[i]->type == SURF) + continue; + if (s[i]->z < -.001) + { + anions[count_anions++] = s[i]; + } + else if (s[i]->z > .001) + { + cations[count_cations++] = s[i]; + } + else + { + neutrals[count_neutrals++] = s[i]; + } + } + /* + * Add etheta to parameter list in case theta not defined for + * cation-cation or anion-anion pair + * Remove old TYPE_ETHETA definitions + */ + j = 0; + for (i = 0; i < count_pitz_param; i++) + { + if (pitz_params[i]->type == TYPE_ETHETA) + { + pitz_params[i] = + (struct pitz_param *) free_check_null(pitz_params[i]); + } + else + { + pitz_params[j++] = pitz_params[i]; + } + } + count_pitz_param = j; + for (i = 0; i < count_cations - 1; i++) + { + for (j = i + 1; j < count_cations; j++) + { + sprintf(line, "%s %s 1", spec[i]->name, spec[j]->name); + pzp_ptr = pitz_param_read(line, 2); + pzp_ptr->type = TYPE_ETHETA; + if (count_pitz_param >= max_pitz_param) + { + space((void **) ((void *) &pitz_params), count_pitz_param, + &max_pitz_param, sizeof(struct pitz_param *)); + } + pitz_params[count_pitz_param++] = pzp_ptr; + + } + } + for (i = 2 * count_s; i < 2 * count_s + count_anions - 1; i++) + { + for (j = i + 1; j < 2 * count_s + count_anions; j++) + { + sprintf(line, "%s %s 1", spec[i]->name, spec[j]->name); + pzp_ptr = pitz_param_read(line, 2); + pzp_ptr->type = TYPE_ETHETA; + if (count_pitz_param >= max_pitz_param) + { + space((void **) ((void *) &pitz_params), count_pitz_param, + &max_pitz_param, sizeof(struct pitz_param *)); + } + pitz_params[count_pitz_param] = pzp_ptr; + count_pitz_param++; + } + } + /* + * put species numbers in pitz_params + */ + for (i = 0; i < count_pitz_param; i++) + { + for (j = 0; j < 3; j++) + { + if (pitz_params[i]->species[j] == NULL) + continue; + pitz_params[i]->ispec[j] = ISPEC(pitz_params[i]->species[j]); + if ((j < 2 && pitz_params[i]->ispec[j] == -1) || + (j == 2 + && (pitz_params[i]->type == TYPE_PSI + || pitz_params[i]->type == TYPE_ZETA) + && pitz_params[i]->ispec[j] == -1)) + { + input_error++; + error_string = sformatf( + "Species for Pitzer parameter not defined in SOLUTION_SPECIES, %s", + pitz_params[i]->species[j]); + error_msg(error_string, CONTINUE); + return (ERROR); + } + } + } + /* + * MacInnes data + */ + string1 = string_hsave("K+"); + string2 = string_hsave("Cl-"); + IC = ISPEC(string2); + for (i = 0; i < count_pitz_param; i++) + { + if ((pitz_params[i]->species[0] == string1 && + pitz_params[i]->species[1] == string2) || + (pitz_params[i]->species[0] == string2 && + pitz_params[i]->species[1] == string1) ) + { + switch (pitz_params[i]->type) + { + case TYPE_B0: + mcb0 = pitz_params[i]; + break; + case TYPE_B1: + mcb1 = pitz_params[i]; + break; + case TYPE_C0: + mcc0 = pitz_params[i]; + break; + case TYPE_B2: + case TYPE_THETA: + case TYPE_LAMDA: + case TYPE_ZETA: + case TYPE_PSI: + case TYPE_ETHETA: + case TYPE_ALPHAS: + case TYPE_MU: + case TYPE_ETA: + case TYPE_Other: + default: + break; + } + } + } + if (mcb0 == NULL && mcb1 == NULL && mcc0 == NULL && ICON == TRUE) + { + error_string = sformatf( + "No KCl interaction parameters, turning off MacInnis scaling."); + warning_msg(error_string); + ICON = FALSE; + } + /* + * Set alpha values + */ + for (i = 0; i < count_pitz_param; i++) + { + z0 = fabs(spec[pitz_params[i]->ispec[0]]->z); + z1 = fabs(spec[pitz_params[i]->ispec[1]]->z); + if (equal(z0, 1.0, 1e-8) || equal(z1, 1.0, 1e-8)) + { + order = 1; + } + else if (equal(z0, 2.0, 1e-8) && equal(z1, 2.0, 1e-8)) + { + order = 2; + } + else + { + order = 3; + } + if (pitz_params[i]->type == TYPE_B1) + { + switch (order) + { + case 1: + case 3: + pitz_params[i]->alpha = 2.0; + break; + case 2: + pitz_params[i]->alpha = 1.4; + break; + } + } + else if (pitz_params[i]->type == TYPE_B2) + { + switch (order) + { + case 1: + pitz_params[i]->alpha = 12.0; + break; + case 2: + pitz_params[i]->alpha = 12.0; + break; + case 3: + pitz_params[i]->alpha = 50.0; + break; + } + } + } + /* + * Add specific alphas + */ + for (i = 0; i < count_pitz_param; i++) + { + if (pitz_params[i]->type == TYPE_ALPHAS) + { + for (j = 0; j < count_pitz_param; j++) + { + if (pitz_params[j]->type != TYPE_B1) + continue; + if (pitz_params[i]->ispec[0] != pitz_params[j]->ispec[0]) + continue; + if (pitz_params[i]->ispec[1] != pitz_params[j]->ispec[1]) + continue; + pitz_params[j]->alpha = pitz_params[i]->a[0]; + break; + } + for (j = 0; j < count_pitz_param; j++) + { + if (pitz_params[j]->type != TYPE_B2) + continue; + if (pitz_params[i]->ispec[0] != pitz_params[j]->ispec[0]) + continue; + if (pitz_params[i]->ispec[1] != pitz_params[j]->ispec[1]) + continue; + pitz_params[j]->alpha = pitz_params[i]->a[1]; + break; + } + } + } + + /* + * Add thetas pointer to etheta pitzer parameters + */ + + if (count_theta_param > 0) + { + for (i = 0; i < count_theta_param; i++) + { + theta_params[i] = + (struct theta_param *) free_check_null(theta_params[i]); + } + } + count_theta_param = 0; + for (i = 0; i < count_pitz_param; i++) + { + if (pitz_params[i]->type == TYPE_ETHETA) + { + z0 = spec[pitz_params[i]->ispec[0]]->z; + z1 = spec[pitz_params[i]->ispec[1]]->z; + theta_param_ptr = theta_param_search(z0, z1); + if (theta_param_ptr == NULL) + { + if (count_theta_param >= max_theta_param) + { + space((void **) ((void *) &theta_params), + count_theta_param, &max_theta_param, + sizeof(struct theta_param *)); + } + theta_params[count_theta_param] = theta_param_alloc(); + theta_param_init(theta_params[count_theta_param]); + theta_params[count_theta_param]->zj = z0; + theta_params[count_theta_param]->zk = z1; + theta_param_ptr = theta_params[count_theta_param]; + count_theta_param++; + } + pitz_params[i]->thetas = theta_param_ptr; + } + } + /* + * Tidy TYPE_MU + */ + + /* Coef for Osmotic coefficient for TYPE_MU */ + + for (i = 0; i < count_pitz_param; i++) + { + if (pitz_params[i]->type == TYPE_MU) + { + i0 = pitz_params[i]->ispec[0]; + i1 = pitz_params[i]->ispec[1]; + i2 = pitz_params[i]->ispec[2]; + count_pos = count_neg = count_neut = 0; + for (j = 0; j <= 2; j++) + { + if (spec[pitz_params[i]->ispec[j]]->z > 0) + { + count_pos++; + } + if (spec[pitz_params[i]->ispec[j]]->z == 0) + { + count_neut++; + } + if (spec[pitz_params[i]->ispec[j]]->z < 0) + { + count_neg++; + } + } + /* All neutral */ + if (count_neut == 3) + { + if (i0 == i1 && i1 == i2) + { + /* type n, n, n */ + pitz_params[i]->os_coef = 1; + continue; + } + else if (i0 == i1 || i1 == i2 || i0 == i2) + { + /* type n, n, n' */ + pitz_params[i]->os_coef = 3; + continue; + } + else + { + /* type n, n', n'' */ + pitz_params[i]->os_coef = 6; + continue; + } + } + /* Two neutral, one anion or cation */ + if (i0 == i1 || i1 == i2 || i0 == i2) + { + /* type n, n, a|c */ + pitz_params[i]->os_coef = 3; + continue; + } + else + { + /* type n, n', a|c */ + pitz_params[i]->os_coef = 6; + continue; + } + } + } + + /* Coef for gammas for TYPE_MU */ + + for (i = 0; i < count_pitz_param; i++) + { + if (pitz_params[i]->type == TYPE_MU) + { + for (j = 0; j <= 2; j++) + { + count[j] = 0; + for (jj = 0; jj <= 2; jj++) + { + if (pitz_params[i]->ispec[j] == pitz_params[i]->ispec[jj]) + { + count[j]++; + } + } + } + for (j = 0; j <= 2; j++) + { + /* cation or anion */ + if (spec[pitz_params[i]->ispec[j]]->z < 0 + || spec[pitz_params[i]->ispec[j]]->z > 0) + { + if (count[0] > 1 || count[1] > 1) + { + pitz_params[i]->ln_coef[j] = 3; + } + else + { + pitz_params[i]->ln_coef[j] = 6; + } + continue; + } + /* Neutral */ + if (count[j] == 3) + { + pitz_params[i]->ln_coef[j] = 1; + } + else if (count[j] == 2) + { + pitz_params[i]->ln_coef[j] = 3; + } + else if (count[j] == 1) + { + if (count[0] > 1 || count[1] > 1) + { + pitz_params[i]->ln_coef[j] = 3; + } + else + { + pitz_params[i]->ln_coef[j] = 6; + } + } + } + } + } + /* Debug TYPE_MU coefficients */ + /* + for (i = 0; i < count_pitz_param; i++) + { + if (pitz_params[i]->type == TYPE_MU) + { + fprintf(stderr, "%s\t%s\t%s\n", pitz_params[i]->species[0], pitz_params[i]->species[1], pitz_params[i]->species[2]); + fprintf(stderr, "%f\t%f\t%f\n", pitz_params[i]->ln_coef[0], pitz_params[i]->ln_coef[1], pitz_params[i]->ln_coef[2]); + fprintf(stderr, "%f\n\n", pitz_params[i]->os_coef); + } + } + */ + /* + * Tidy TYPE_LAMDA + */ + + /* Coef for Osmotic coefficient for TYPE_LAMDA */ + + for (i = 0; i < count_pitz_param; i++) + { + if (pitz_params[i]->type == TYPE_LAMDA) + { + i0 = pitz_params[i]->ispec[0]; + i1 = pitz_params[i]->ispec[1]; + /* All neutral */ + if (i0 == i1) + { + /* type n, n */ + pitz_params[i]->os_coef = 0.5; + pitz_params[i]->ln_coef[0] = 1; + pitz_params[i]->ln_coef[1] = 1; + } + else + { + /* type nn', na, nc */ + pitz_params[i]->os_coef = 1; + pitz_params[i]->ln_coef[0] = 2; + pitz_params[i]->ln_coef[1] = 2; + } + } + } + /* Debug TYPE_LAMDA coefficients */ + /* + for (i = 0; i < count_pitz_param; i++) + { + if (pitz_params[i]->type == TYPE_LAMDA) + { + fprintf(stderr, "%s\t%s\n", pitz_params[i]->species[0], pitz_params[i]->species[1]); + fprintf(stderr, "%f\t%f\n", pitz_params[i]->ln_coef[0], pitz_params[i]->ln_coef[1]); + fprintf(stderr, "%f\n\n", pitz_params[i]->os_coef); + } + } + */ + /* remake map */ + { + pitz_param_map.clear(); + for (int j = 0; j < count_pitz_param; j++) + { + std::set< std::string > header; + for (int i = 0; i < 3; i++) + { + if (pitz_params[j]->species[i] != NULL) header.insert(pitz_params[j]->species[i]); + } + std::ostringstream key_str; + key_str << pitz_params[j]->type << " "; + std::set< std::string >::iterator it = header.begin(); + for(; it != header.end(); ++it) + { + key_str << *it << " "; + } + std::string key = key_str.str().c_str(); + pitz_param_map[key] = j; + } + assert ((int) pitz_param_map.size() == count_pitz_param); + } + return OK; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +ISPEC(const char *name) +/* ---------------------------------------------------------------------- */ +/* + * Find species number in spec for character string species name + */ +{ + int i; + for (i = 0; i < 3 * count_s; i++) + { + if (spec[i] == NULL) + continue; + if (name == spec[i]->name) + { + return (i); + } + } + return (-1); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_pitzer(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Reads advection information + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + /* + * Read advection parameters: + * number of cells; + * number of shifts; + */ + int n; + struct pitz_param *pzp_ptr; + pitz_param_type pzp_type; + + int return_value, opt, opt_save; + char *next_char; + const char *opt_list[] = { + "b0", /* 0 */ + "b1", /* 1 */ + "b2", /* 2 */ + "c0", /* 3 */ + "theta", /* 4 */ + "lamda", /* 5 */ + "zeta", /* 6 */ + "psi", /* 7 */ + "macinnes", /* 8 */ + "macinnis", /* 9 */ + "mac", /* 10 */ + "redox", /* 11 */ + "pe", /* 12 */ + "alphas", /* 13 */ + "mu", /* 14 */ + "eta", /* 15 */ + "etheta", /* 16 */ + "use_etheta", /* 17 */ + "lambda", /* 18 */ + "aphi" /* 19 */ + }; + int count_opt_list = 20; + /* + * Read lines + */ + opt_save = OPTION_ERROR; + return_value = UNKNOWN; + n = -1; + pzp_type = TYPE_Other; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_DEFAULT: + pzp_ptr = pitz_param_read(line, n); + if (pzp_ptr != NULL) + { + pzp_ptr->type = pzp_type; + if (pzp_type == TYPE_APHI) + { + aphi = (struct pitz_param *) free_check_null(aphi); + aphi = pzp_ptr; + } + else + { + pitz_param_store(pzp_ptr, false); + } + } + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in PITZER keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* b0 */ + pzp_type = TYPE_B0; + n = 2; + opt_save = OPTION_DEFAULT; + break; + case 1: /* b1 */ + pzp_type = TYPE_B1; + n = 2; + opt_save = OPTION_DEFAULT; + break; + case 2: /* b2 */ + pzp_type = TYPE_B2; + n = 2; + opt_save = OPTION_DEFAULT; + break; + case 3: /* c0 */ + pzp_type = TYPE_C0; + n = 2; + opt_save = OPTION_DEFAULT; + break; + case 4: /* theta */ + pzp_type = TYPE_THETA; + n = 2; + opt_save = OPTION_DEFAULT; + break; + case 5: /* lamda */ + case 18: /* lambda */ + pzp_type = TYPE_LAMDA; + n = 2; + opt_save = OPTION_DEFAULT; + break; + case 6: /* zeta */ + pzp_type = TYPE_ZETA; + n = 3; + opt_save = OPTION_DEFAULT; + break; + case 7: /* psi */ + pzp_type = TYPE_PSI; + n = 3; + opt_save = OPTION_DEFAULT; + break; + case 13: /* alphas */ + pzp_type = TYPE_ALPHAS; + n = 2; + opt_save = OPTION_DEFAULT; + break; + case 8: /* macinnes */ + case 9: /* macinnis */ + case 10: /* mac */ + opt_save = OPTION_ERROR; + ICON = get_true_false(next_char, TRUE); + break; + case 11: /* redox */ + case 12: /* pe */ + opt_save = OPTION_ERROR; + pitzer_pe = get_true_false(next_char, TRUE); + break; + case 14: /* mu */ + pzp_type = TYPE_MU; + n = 3; + opt_save = OPTION_DEFAULT; + break; + case 15: /* eta */ + pzp_type = TYPE_ETA; + n = 3; + opt_save = OPTION_DEFAULT; + break; + case 16: /* etheta */ + case 17: /* use_etheta */ + opt_save = OPTION_ERROR; + use_etheta = get_true_false(next_char, TRUE); + break; + case 19: /* aphi */ + pzp_type = TYPE_APHI; + n = 0; + opt_save = OPTION_DEFAULT; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + pitzer_model = TRUE; + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +PTEMP(LDBLE TK) +/* ---------------------------------------------------------------------- */ +{ +/* +C +C SUBROUTINE TO CALUCLATE TEMPERATURE DEPENDENCE OF PITZER PARAMETER +C +*/ + LDBLE TR = 298.15; + + if (fabs(TK - OTEMP) < 0.001 && fabs(patm_x - OPRESS) < 0.1) + return OK; + DW0 = rho_0 = calc_rho_0(TK - 273.15, patm_x); + VP = patm_x; +#if !defined(PITZER_LISTS) + int i; + for (i = 0; i < count_pitz_param; i++) + { + calc_pitz_param(pitz_params[i], TK, TR); + } +#else + for (size_t j = 0; j < param_list.size(); j++) + { + int i = param_list[j]; + calc_pitz_param(pitz_params[i], TK, TR); + } + if (aphi) + { + calc_pitz_param(aphi, TK, TR); + } + if (mcb0) + { + calc_pitz_param(mcb0, TK, TR); + } + if (mcb1) + { + calc_pitz_param(mcb1, TK, TR); + } + if (mcc0) + { + calc_pitz_param(mcc0, TK, TR); + } +#endif + calc_dielectrics(TK - 273.15, patm_x); + OTEMP = TK; + OPRESS = patm_x; + return OK; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_pitz_param(struct pitz_param *pz_ptr, LDBLE TK, LDBLE TR) +/* ---------------------------------------------------------------------- */ +{ + LDBLE param; + /* + */ + if (fabs(TK - TR) < 0.001) + { + param = pz_ptr->a[0]; + } + else + { + param = (pz_ptr->a[0] + + pz_ptr->a[1] * (1.e0 / TK - 1.e0 / TR) + + pz_ptr->a[2] * log(TK / TR) + + pz_ptr->a[3] * (TK - TR) + + pz_ptr->a[4] * (TK * TK - TR * TR)) + + pz_ptr->a[5] * (1.e0 / (TK * TK) - 1.e0 / (TR * TR)); + } + pz_ptr->p = param; + switch (pz_ptr->type) + { + case TYPE_B0: + pz_ptr->U.b0 = param; + break; + case TYPE_B1: + pz_ptr->U.b1 = param; + break; + case TYPE_B2: + pz_ptr->U.b2 = param; + break; + case TYPE_C0: + pz_ptr->U.c0 = param; + break; + case TYPE_THETA: + pz_ptr->U.theta = param; + break; + case TYPE_LAMDA: + pz_ptr->U.lamda = param; + break; + case TYPE_ZETA: + pz_ptr->U.zeta = param; + break; + case TYPE_ETHETA: + break; + case TYPE_PSI: + pz_ptr->U.psi = param; + break; + case TYPE_ALPHAS: + break; + case TYPE_MU: + pz_ptr->U.mu = param; + break; + case TYPE_ETA: + pz_ptr->U.eta = param; + break; + case TYPE_APHI: + pz_ptr->U.aphi = param; + break; + case TYPE_Other: + default: + error_msg("Should not be TYPE_Other in function calc_pitz_param", + STOP); + break; + } + return OK; +} +#if !defined(PITZER_LISTS) +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +pitzer(void) +/* ---------------------------------------------------------------------- */ +{ + int i, i0, i1, i2; + LDBLE param, l_alpha, z0, z1; + LDBLE etheta, ethetap; + /* + LDBLE CONV, XI, XX, OSUM, BIGZ, DI, F, XXX, GAMCLM, + CSUM, PHIMAC, OSMOT, BMXP, ETHEAP, CMX, BMX, PHI, + BMXPHI, PHIPHI, AW, A, B; + */ + LDBLE CONV, XX, OSUM, BIGZ, DI, F, F1, F2, F_var, XXX, GAMCLM, CSUM, PHIMAC, OSMOT, + B, B1, B2; + LDBLE I, TK; + /* + C + C INITIALIZE + C + */ + CONV = 1.0 / log(10.0); + XX = 0.0; + OSUM = 0.0; + /*n + I = *I_X; + TK = *TK_X; + */ + I = mu_x; + TK = tk_x; + /* DH_AB(TK, &A, &B); */ + /* + C + C TRANSFER DATA FROM TO M + C + */ + for (i = 0; i < 3 * count_s; i++) + { + IPRSNT[i] = FALSE; + M[i] = 0.0; + if (spec[i] != NULL && spec[i]->in == TRUE) + { + if (spec[i]->type == EX || + spec[i]->type == SURF || spec[i]->type == SURF_PSI) + continue; + M[i] = under(spec[i]->lm); + if (M[i] > MIN_TOTAL) + IPRSNT[i] = TRUE; + } + } + if (ICON == TRUE) + { + IPRSNT[IC] = TRUE; + } + /* + ICON = 0; + M[1] = 1.40070736; + M[4] = 2.52131086E-05; + M[140] = 4.59985435E-09; + */ + + /* + C + C COMPUTE PITZER COEFFICIENTS' TEMPERATURE DEPENDENCE + C + */ + PTEMP(TK); + for (i = 0; i < 2 * count_s + count_anions; i++) + { + LGAMMA[i] = 0.0; + if (IPRSNT[i] == TRUE) + { + XX = XX + M[i] * fabs(spec[i]->z); + OSUM = OSUM + M[i]; + } + } + /* + C + C EQUATION (8) + C + */ + BIGZ = XX; + DI = sqrt(I); + /* + C + C CALCULATE F & GAMCLM + C + */ + B = 1.2; + F = F1 = F2 = -A0 * (DI / (1.0 + B * DI) + 2.0 * log(1.0 + B * DI) / B); + if (patm_x > 1.0) + { + LDBLE pap; + pap = (7e-5 + 1.93e-9 * pow(TK - 250.0, 2.0)) * patm_x; + B1 = B - (pap > 0.2 ? 0.2 : pap); + pap = (9.65e-10 * pow(TK - 263.0, 2.773)) * pow(patm_x, 0.623); + //pap = (-5.22e-4 + 7.19e-8 * pow(TK - 263.0, 2.0)) * pow(patm_x, 0.623); + B2 = B - (pap > 0.2 ? 0.2 : pap); + if (B1 != 0) + F1 = -A0 * (DI / (1.0 + B1 * DI) + 2.0 * log(1.0 + B1 * DI) / B1); + if (B2 != 0) + F2 = -A0 * (DI / (1.0 + B2 * DI) + 2.0 * log(1.0 + B2 * DI) / B2); + } + XXX = 2.0 * DI; + XXX = + (1.0 - (1.0 + XXX - XXX * XXX * 0.5) * exp(-XXX)) / (XXX * XXX); + /*GAMCLM=F+I*2.0e0*(BCX(1,IK,IC)+BCX(2,IK,IC)*XXX)+1.5e0*BCX(4,IK,IC)*I*I; */ + /*GAMCLM=F+I*2.0e0*(mcb0->U.b0 + mcb1->U.b1*XXX) + 1.5e0*mcc0->U.c0*I*I; */ + /*GAMCLM = F + I * 2.0e0 * (mcb0->p + mcb1->p * XXX) + 1.5e0 * mcc0->p * I * I; */ + GAMCLM = F1; + if (mcb0 != NULL) + GAMCLM += I * 2.0 * mcb0->p; + if (mcb1 != NULL) + GAMCLM += I * 2.0 * mcb1->p * XXX; + if (mcc0 != NULL) + GAMCLM += 1.5 * mcc0->p * I * I; + CSUM = 0.0; + OSMOT = -(A0) * pow(I, (LDBLE) 1.5) / (1.0 + B * DI); + /* + * Calculate ethetas + */ + for (i = 0; i < count_theta_param; i++) + { + z0 = theta_params[i]->zj; + z1 = theta_params[i]->zk; + ETHETAS(z0, z1, I, ðeta, ðetap); + theta_params[i]->etheta = etheta; + theta_params[i]->ethetap = ethetap; + } + /* + * Sums for F, LGAMMA, and OSMOT + */ + for (i = 0; i < count_pitz_param; i++) + { + i0 = pitz_params[i]->ispec[0]; + i1 = pitz_params[i]->ispec[1]; + if (IPRSNT[i0] == FALSE || IPRSNT[i1] == FALSE) + continue; + z0 = spec[i0]->z; + z1 = spec[i1]->z; + param = pitz_params[i]->p; + l_alpha = pitz_params[i]->alpha; + F_var = 0; + switch (pitz_params[i]->type) + { + case TYPE_B0: + LGAMMA[i0] += M[i1] * 2.0 * param; + LGAMMA[i1] += M[i0] * 2.0 * param; + OSMOT += M[i0] * M[i1] * param; + break; + case TYPE_B1: + if (param != 0.0) + { + F_var = M[i0] * M[i1] * param * GP(l_alpha * DI) / I; + LGAMMA[i0] += M[i1] * 2.0 * param * G(l_alpha * DI); + LGAMMA[i1] += M[i0] * 2.0 * param * G(l_alpha * DI); + OSMOT += M[i0] * M[i1] * param * exp(-l_alpha * DI); + } + break; + case TYPE_B2: + if (param != 0.0) + { + F_var = M[i0] * M[i1] * param * GP(l_alpha * DI) / I; + LGAMMA[i0] += M[i1] * 2.0 * param * G(l_alpha * DI); + LGAMMA[i1] += M[i0] * 2.0 * param * G(l_alpha * DI); + OSMOT += M[i0] * M[i1] * param * exp(-l_alpha * DI); + } + break; + case TYPE_C0: + CSUM += + M[i0] * M[i1] * pitz_params[i]->p / (2.0 * + sqrt(fabs(z0 * z1))); + LGAMMA[i0] += M[i1] * BIGZ * param / (2.0 * sqrt(fabs(z0 * z1))); + LGAMMA[i1] += M[i0] * BIGZ * param / (2.0 * sqrt(fabs(z0 * z1))); + OSMOT += + M[i0] * M[i1] * BIGZ * param / (2.0 * sqrt(fabs(z0 * z1))); + break; + case TYPE_THETA: + LGAMMA[i0] += 2.0 * M[i1] * (param /*+ ETHETA(z0, z1, I) */ ); + LGAMMA[i1] += 2.0 * M[i0] * (param /*+ ETHETA(z0, z1, I) */ ); + OSMOT += M[i0] * M[i1] * param; + break; + case TYPE_ETHETA: + /* + ETHETAS(z0, z1, I, ðeta, ðetap); + */ + if (use_etheta == TRUE) + { + etheta = pitz_params[i]->thetas->etheta; + ethetap = pitz_params[i]->thetas->ethetap; + F_var = M[i0] * M[i1] * ethetap; + LGAMMA[i0] += 2.0 * M[i1] * etheta; + LGAMMA[i1] += 2.0 * M[i0] * etheta; + OSMOT += M[i0] * M[i1] * (etheta + I * ethetap); + /* + F += M[i0]*M[i1]*ETHETAP(z0, z1, I); + LGAMMA[i0] += 2.0*M[i1]*(ETHETA(z0, z1, I) ); + LGAMMA[i1] += 2.0*M[i0]*(ETHETA(z0, z1, I) ); + OSMOT += M[i0]*M[i1]*(ETHETA(z0, z1, I) + I*ETHETAP(z0, z1, I) ); + */ + } + break; + case TYPE_PSI: + i2 = pitz_params[i]->ispec[2]; + if (IPRSNT[i2] == FALSE) + continue; + LGAMMA[i0] += M[i1] * M[i2] * param; + LGAMMA[i1] += M[i0] * M[i2] * param; + LGAMMA[i2] += M[i0] * M[i1] * param; + OSMOT += M[i0] * M[i1] * M[i2] * param; + break; + case TYPE_LAMDA: + LGAMMA[i0] += M[i1] * param * pitz_params[i]->ln_coef[0]; + LGAMMA[i1] += M[i0] * param * pitz_params[i]->ln_coef[1]; + OSMOT += M[i0] * M[i1] * param * pitz_params[i]->os_coef; + break; + case TYPE_ZETA: + i2 = pitz_params[i]->ispec[2]; + if (IPRSNT[i2] == FALSE) + continue; + LGAMMA[i0] += M[i1] * M[i2] * param; + LGAMMA[i1] += M[i0] * M[i2] * param; + LGAMMA[i2] += M[i0] * M[i1] * param; + OSMOT += M[i0] * M[i1] * M[i2] * param; + break; + case TYPE_MU: + i2 = pitz_params[i]->ispec[2]; + if (IPRSNT[i2] == FALSE) + continue; + + LGAMMA[i0] += M[i1] * M[i2] * param * pitz_params[i]->ln_coef[0]; + LGAMMA[i1] += M[i0] * M[i2] * param * pitz_params[i]->ln_coef[1]; + LGAMMA[i2] += M[i0] * M[i1] * param * pitz_params[i]->ln_coef[2]; + OSMOT += M[i0] * M[i1] * M[i2] * param * pitz_params[i]->os_coef; + break; + case TYPE_ETA: + i2 = pitz_params[i]->ispec[2]; + if (IPRSNT[i2] == FALSE) + continue; + LGAMMA[i0] += M[i1] * M[i2] * param; + LGAMMA[i1] += M[i0] * M[i2] * param; + LGAMMA[i2] += M[i0] * M[i1] * param; + OSMOT += M[i0] * M[i1] * M[i2] * param; + break; + case TYPE_ALPHAS: + break; + case TYPE_Other: + default: + error_msg("TYPE_Other in pitz_param list.", STOP); + break; + } + F += F_var; + F1 += F_var; + F2 += F_var; + } + + /* + * Add F and CSUM terms to LGAMMA + */ + + for (i = 0; i < count_cations; i++) + { + if (!IPRSNT[i]) + continue; + z0 = fabs(spec[i]->z); + F_var = (z0 == 1 ? F1 : (z0 == 2.0 ? F2 : F)); + LGAMMA[i] += z0 * z0 * F_var + z0 * CSUM; + } + for (i = 2 * count_s; i < 2 * count_s + count_anions; i++) + { + if (!IPRSNT[i]) + continue; + z0 = fabs(spec[i]->z); + F_var = (z0 == 1 ? F1 : (z0 == 2.0 ? F2 : F)); + LGAMMA[i] += z0 * z0 * F_var + z0 * CSUM; + } + /* + C + C CONVERT TO MACINNES CONVENTION + C + */ + if (ICON == TRUE) + { + PHIMAC = LGAMMA[IC] - GAMCLM; + /* + C + C CORRECTED ERROR IN PHIMAC, NOVEMBER, 1989 + C + */ + for (i = 0; i < 2 * count_s + count_anions; i++) + { + if (IPRSNT[i] == TRUE) + { + LGAMMA[i] = LGAMMA[i] + spec[i]->z * PHIMAC; + } + } + } + + COSMOT = 1.0 + 2.0 * OSMOT / OSUM; + /* + C + C CALCULATE THE ACTIVITY OF WATER + C + */ + AW = exp(-OSUM * COSMOT / 55.50837); + /* + if (AW > 1.0) + AW = 1.0; + */ + /*s_h2o->la=log10(AW); */ + mu_x = I; + for (i = 0; i < 2 * count_s + count_anions; i++) + { + if (IPRSNT[i] == FALSE) + continue; + /*spec[i]->lg=LGAMMA[i]*CONV; */ + spec[i]->lg_pitzer = LGAMMA[i] * CONV; + /* + output_msg(sformatf( "%d %s:\t%e\t%e\t%e\t%e \n", i, spec[i]->name, M[i], spec[i]->la, spec[i]->lg_pitzer, spec[i]->lg)); + */ + } + /* + output_msg(sformatf( "OSUM: %e\n", OSUM)); + output_msg(sformatf( "OSMOT: %e\n", OSMOT)); + output_msg(sformatf( "COSMOT: %e\n", COSMOT)); + output_msg(sformatf( "F: %e\n", F)); + output_msg(sformatf( "AW: %e\n", AW)); + */ + /* + *I_X = I; + *COSMOT_X = COSMOT; + */ + return (OK); +} +#else +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +pitzer(void) +/* ---------------------------------------------------------------------- */ +{ + int i, i0, i1, i2; + LDBLE param, l_alpha, z0, z1; + LDBLE etheta, ethetap; + /* + LDBLE CONV, XI, XX, OSUM, BIGZ, DI, F, XXX, GAMCLM, + CSUM, PHIMAC, OSMOT, BMXP, ETHEAP, CMX, BMX, PHI, + BMXPHI, PHIPHI, AW, A, B; + */ + LDBLE CONV, XX, OSUM, BIGZ, DI, F, F1, F2, F_var, XXX, GAMCLM, CSUM, PHIMAC, OSMOT, + B, B1, B2; + LDBLE I, TK; + /* + C + C INITIALIZE + C + */ + CONV = 1.0 / log(10.0); + XX = 0.0; + OSUM = 0.0; + I = mu_x; + TK = tk_x; + /* DH_AB(TK, &A, &B); */ + /* + C + C TRANSFER DATA FROM TO M + C + */ + for (size_t j = 0; j < s_list.size(); j++) + { + i = s_list[j]; + IPRSNT[i] = FALSE; + M[i] = 0.0; + if (spec[i] != NULL && spec[i]->in == TRUE) + { + if (spec[i]->type == EX || + spec[i]->type == SURF || spec[i]->type == SURF_PSI) + continue; + M[i] = under(spec[i]->lm); + if (M[i] > MIN_TOTAL) + IPRSNT[i] = TRUE; + } + } + if (ICON == TRUE) + { + IPRSNT[IC] = TRUE; + } + /* + C + C COMPUTE PITZER COEFFICIENTS' TEMPERATURE DEPENDENCE + C + */ + PTEMP(TK); + for (size_t j = 0; j < s_list.size(); j++) + { + int i = s_list[j]; + LGAMMA[i] = 0.0; + XX = XX + M[i] * fabs(spec[i]->z); + OSUM = OSUM + M[i]; + } + /* + C + C EQUATION (8) + C + */ + BIGZ = XX; + DI = sqrt(I); + /* + C + C CALCULATE F & GAMCLM + C + */ + B = 1.2; + F = F1 = F2 = -A0 * (DI / (1.0 + B * DI) + 2.0 * log(1.0 + B * DI) / B); + if (patm_x > 1.0) + { + LDBLE pap = 0.0; + pap = (7e-5 + 1.93e-9 * pow(TK - 250.0, 2.0)) * patm_x; + B1 = B - (pap > 0.2 ? 0.2 : pap); + if (TK > 263.0) + { + pap = (9.65e-10 * pow(TK - 263.0, 2.773)) * pow(patm_x, 0.623); + //pap = (-5.22e-4 + 7.19e-8 * pow(TK - 263.0, 2.0)) * pow(patm_x, 0.623); + } + B2 = B - (pap > 0.2 ? 0.2 : pap); + if (B1 != 0) + F1 = -A0 * (DI / (1.0 + B1 * DI) + 2.0 * log(1.0 + B1 * DI) / B1); + if (B2 != 0) + F2 = -A0 * (DI / (1.0 + B2 * DI) + 2.0 * log(1.0 + B2 * DI) / B2); + } + XXX = 2.0 * DI; + XXX = (1.0 - (1.0 + XXX - XXX * XXX * 0.5) * exp(-XXX)) / (XXX * XXX); + GAMCLM = F1; + if (mcb0 != NULL) + GAMCLM += I * 2.0 * mcb0->p; + if (mcb1 != NULL) + GAMCLM += I * 2.0 * mcb1->p * XXX; + if (mcc0 != NULL) + GAMCLM += 1.5 * mcc0->p * I * I; + CSUM = 0.0; + OSMOT = -(A0) * pow(I, (LDBLE) 1.5) / (1.0 + B * DI); + /* + * Calculate ethetas + */ + if (use_etheta == TRUE) + { + for (i = 0; i < count_theta_param; i++) + { + z0 = theta_params[i]->zj; + z1 = theta_params[i]->zk; + ETHETAS(z0, z1, I, ðeta, ðetap); + theta_params[i]->etheta = etheta; + theta_params[i]->ethetap = ethetap; + } + } + /* + * Sums for F, LGAMMA, and OSMOT + */ + for (size_t j = 0; j < param_list.size(); j++) + { + int i = param_list[j]; + i0 = pitz_params[i]->ispec[0]; + i1 = pitz_params[i]->ispec[1]; + z0 = spec[i0]->z; + z1 = spec[i1]->z; + param = pitz_params[i]->p; + l_alpha = pitz_params[i]->alpha; + F_var = 0; + switch (pitz_params[i]->type) + { + case TYPE_B0: + LGAMMA[i0] += M[i1] * 2.0 * param; + LGAMMA[i1] += M[i0] * 2.0 * param; + OSMOT += M[i0] * M[i1] * param; + break; + case TYPE_B1: + if (param != 0.0) + { + F_var = M[i0] * M[i1] * param * GP(l_alpha * DI) / I; + LGAMMA[i0] += M[i1] * 2.0 * param * G(l_alpha * DI); + LGAMMA[i1] += M[i0] * 2.0 * param * G(l_alpha * DI); + OSMOT += M[i0] * M[i1] * param * exp(-l_alpha * DI); + } + break; + case TYPE_B2: + if (param != 0.0) + { + F_var = M[i0] * M[i1] * param * GP(l_alpha * DI) / I; + LGAMMA[i0] += M[i1] * 2.0 * param * G(l_alpha * DI); + LGAMMA[i1] += M[i0] * 2.0 * param * G(l_alpha * DI); + OSMOT += M[i0] * M[i1] * param * exp(-l_alpha * DI); + } + break; + case TYPE_C0: + CSUM += + M[i0] * M[i1] * pitz_params[i]->p / (2.0 * + sqrt(fabs(z0 * z1))); + LGAMMA[i0] += M[i1] * BIGZ * param / (2.0 * sqrt(fabs(z0 * z1))); + LGAMMA[i1] += M[i0] * BIGZ * param / (2.0 * sqrt(fabs(z0 * z1))); + OSMOT += + M[i0] * M[i1] * BIGZ * param / (2.0 * sqrt(fabs(z0 * z1))); + break; + case TYPE_THETA: + LGAMMA[i0] += 2.0 * M[i1] * (param /*+ ETHETA(z0, z1, I) */ ); + LGAMMA[i1] += 2.0 * M[i0] * (param /*+ ETHETA(z0, z1, I) */ ); + OSMOT += M[i0] * M[i1] * param; + break; + case TYPE_ETHETA: + /* + ETHETAS(z0, z1, I, ðeta, ðetap); + */ + if (use_etheta == TRUE) + { + etheta = pitz_params[i]->thetas->etheta; + ethetap = pitz_params[i]->thetas->ethetap; + F_var = M[i0] * M[i1] * ethetap; + LGAMMA[i0] += 2.0 * M[i1] * etheta; + LGAMMA[i1] += 2.0 * M[i0] * etheta; + OSMOT += M[i0] * M[i1] * (etheta + I * ethetap); + } + break; + case TYPE_PSI: + i2 = pitz_params[i]->ispec[2]; + if (IPRSNT[i2] == FALSE) + continue; + LGAMMA[i0] += M[i1] * M[i2] * param; + LGAMMA[i1] += M[i0] * M[i2] * param; + LGAMMA[i2] += M[i0] * M[i1] * param; + OSMOT += M[i0] * M[i1] * M[i2] * param; + break; + case TYPE_LAMDA: + LGAMMA[i0] += M[i1] * param * pitz_params[i]->ln_coef[0]; + LGAMMA[i1] += M[i0] * param * pitz_params[i]->ln_coef[1]; + OSMOT += M[i0] * M[i1] * param * pitz_params[i]->os_coef; + break; + case TYPE_ZETA: + i2 = pitz_params[i]->ispec[2]; + if (IPRSNT[i2] == FALSE) + continue; + LGAMMA[i0] += M[i1] * M[i2] * param; + LGAMMA[i1] += M[i0] * M[i2] * param; + LGAMMA[i2] += M[i0] * M[i1] * param; + OSMOT += M[i0] * M[i1] * M[i2] * param; + break; + case TYPE_MU: + i2 = pitz_params[i]->ispec[2]; + if (IPRSNT[i2] == FALSE) + continue; + + LGAMMA[i0] += M[i1] * M[i2] * param * pitz_params[i]->ln_coef[0]; + LGAMMA[i1] += M[i0] * M[i2] * param * pitz_params[i]->ln_coef[1]; + LGAMMA[i2] += M[i0] * M[i1] * param * pitz_params[i]->ln_coef[2]; + OSMOT += M[i0] * M[i1] * M[i2] * param * pitz_params[i]->os_coef; + break; + case TYPE_ETA: + i2 = pitz_params[i]->ispec[2]; + if (IPRSNT[i2] == FALSE) + continue; + LGAMMA[i0] += M[i1] * M[i2] * param; + LGAMMA[i1] += M[i0] * M[i2] * param; + LGAMMA[i2] += M[i0] * M[i1] * param; + OSMOT += M[i0] * M[i1] * M[i2] * param; + break; + case TYPE_ALPHAS: + break; + case TYPE_Other: + default: + error_msg("TYPE_Other in pitz_param list.", STOP); + break; + } + F += F_var; + F1 += F_var; + F2 += F_var; + } + + /* + * Add F and CSUM terms to LGAMMA + */ + for (size_t j = 0; j < ion_list.size(); j++) + { + int i = ion_list[j]; + z0 = fabs(spec[i]->z); + F_var = (z0 == 1 ? F1 : (z0 == 2.0 ? F2 : F)); + LGAMMA[i] += z0 * z0 * F_var + z0 * CSUM; + } + /* + C + C CONVERT TO MACINNES CONVENTION + C + */ + if (ICON == TRUE) + { + PHIMAC = LGAMMA[IC] - GAMCLM; + /* + C + C CORRECTED ERROR IN PHIMAC, NOVEMBER, 1989 + C + */ + for (size_t j = 0; j < s_list.size(); j++) + { + int i = s_list[j]; + LGAMMA[i] = LGAMMA[i] + spec[i]->z * PHIMAC; + } + } + + COSMOT = 1.0 + 2.0 * OSMOT / OSUM; + /* + C + C CALCULATE THE ACTIVITY OF WATER + C + */ + AW = exp(-OSUM * COSMOT / 55.50837); + /* + if (AW > 1.0) + AW = 1.0; + */ + /*s_h2o->la=log10(AW); */ + mu_x = I; + for (size_t j = 0; j < s_list.size(); j++) + { + int i = s_list[j]; + spec[i]->lg_pitzer = LGAMMA[i] * CONV; + } + /* + *I_X = I; + *COSMOT_X = COSMOT; + */ + return (OK); +} +#endif +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +G(LDBLE L_Y) +/* ---------------------------------------------------------------------- */ +{ + LDBLE d=0.0; + if (L_Y != 0.0) + { + d = 2.0e0 * (1.0e0 - (1.0e0 + L_Y) * exp(-L_Y)) / (L_Y * L_Y); + } + + return (d); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +GP(LDBLE L_Y) +/* ---------------------------------------------------------------------- */ +{ + LDBLE d=0.0; + if (L_Y != 0.0) + { + d = -2.0e0 * (1.0e0 - (1.0e0 + L_Y + L_Y * L_Y / 2.0e0) * exp(-L_Y)) / + (L_Y * L_Y); + } + return d; +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +ETHETAS(LDBLE ZJ, LDBLE ZK, LDBLE I, LDBLE * etheta, LDBLE * ethetap) +/* ---------------------------------------------------------------------- */ +{ + /* Revised ETHETAS code thanks to Wouter Falkena and the MoReS team, June, 2015 */ + *etheta = 0.0; + *ethetap = 0.0; + + if (ZJ == ZK) + return (OK); + + const LDBLE XCON = 6.0e0 * A0 * sqrt(I); + const LDBLE ZZ = ZJ * ZK; +/* +C +C NEXT 3 ARE EQUATION (A1) +C +*/ + const LDBLE XJK = XCON * ZZ; + const LDBLE XJJ = XCON * ZJ * ZJ; + const LDBLE XKK = XCON * ZK * ZK; + +/* +C +C EQUATION (A3) +C +*/ + LDBLE JAY_XJK; + LDBLE JPRIME_XJK; + ETHETA_PARAMS( XJK, JAY_XJK, JPRIME_XJK ); + + LDBLE JAY_XJJ; + LDBLE JPRIME_XJJ; + ETHETA_PARAMS( XJJ, JAY_XJJ, JPRIME_XJJ ); + + LDBLE JAY_XKK; + LDBLE JPRIME_XKK; + ETHETA_PARAMS( XKK, JAY_XKK, JPRIME_XKK ); + + *etheta = + ZZ * (JAY_XJK - JAY_XJJ / 2.0e0 - JAY_XKK / 2.0e0) / (4.0e0 * I); + *ethetap = + ZZ * (JPRIME_XJK - JPRIME_XJJ / 2.0e0 - + JPRIME_XKK / 2.0e0) / (8.0e0 * I * I) - *etheta / I; + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +ETHETA_PARAMS(LDBLE X, LDBLE& JAY, LDBLE& JPRIME ) +/* ---------------------------------------------------------------------- */ +/* +C +C NUMERICAL APPROXIMATION TO THE INTEGRALS IN THE EXPRESSIONS FOR J0 +C AND J1. CHEBYSHEV APPROXIMATION IS USED. THE CONSTANTS 'AK' ARE +C DEFINED IN BLOCK COMMON. +C +*/ +/* +C +C AK IS USED TO CALCULATE HIGHER ORDER ELECTROSTATIC TERMS IN +C SUBROUTINE PITZER +C +*/ +{ + static const LDBLE AKX[42] = { + 1.925154014814667e0, -.060076477753119e0, -.029779077456514e0, + -.007299499690937e0, 0.000388260636404e0, 0.000636874599598e0, + 0.000036583601823e0, -.000045036975204e0, -.000004537895710e0, + 0.000002937706971e0, 0.000000396566462e0, -.000000202099617e0, + -.000000025267769e0, 0.000000013522610e0, 0.000000001229405e0, + -.000000000821969e0, -.000000000050847e0, 0.000000000046333e0, + 0.000000000001943e0, -.000000000002563e0, -.000000000010991e0, + 0.628023320520852e0, 0.462762985338493e0, 0.150044637187895e0, + -.028796057604906e0, -.036552745910311e0, -.001668087945272e0, + 0.006519840398744e0, 0.001130378079086e0, -.000887171310131e0, + -.000242107641309e0, 0.000087294451594e0, 0.000034682122751e0, + -.000004583768938e0, -.000003548684306e0, -.000000250453880e0, + 0.000000216991779e0, 0.000000080779570e0, 0.000000004558555e0, + -.000000006944757e0, -.000000002849257e0, 0.000000000237816e0 + }; +/* + LDBLE PRECISION AK, BK, DK + COMMON / MX8 / AK(0:20,2),BK(0:22),DK(0:22) +*/ + const LDBLE *AK; + LDBLE L_Z = 0.0; + LDBLE L_DZ = 0.0; + + if ( X <= 1.0e0 ) + { + const LDBLE powX0_2 = pow( X, 0.2 ); + L_Z = 4.0e0 * powX0_2 - 2.0e0; + L_DZ = 0.8e0 * powX0_2 / 2.0e0; + AK = &AKX[0]; + } + else + { + const LDBLE powXmin0_1 = pow( X, -0.1 ); + L_Z = ( 40.0e0 * powXmin0_1 - 22.0e0 ) / 9.0e0; + L_DZ = -4.0e0 * powXmin0_1 / 18.0e0; + AK = &AKX[21]; + } + + BK[20] = AK[20]; + BK[19] = L_Z * AK[20] + AK[19]; + DK[19] = AK[20]; + for ( int i = 18; i >= 0; i-- ) + { + BK[i] = L_Z * BK[i + 1] - BK[i + 2] + AK[i]; + DK[i] = BK[i + 1] + L_Z * DK[i + 1] - DK[i + 2]; + } + + JAY = X / 4.0e0 - 1.0e0 + 0.5e0 * (BK[0] - BK[2]); + JPRIME = X * .25e0 + L_DZ * (DK[0] - DK[2]); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +pitzer_clean_up(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Free all allocated memory, except strings + */ + int i; + for (i = 0; i < count_pitz_param; i++) + { + pitz_params[i] = + (struct pitz_param *) free_check_null(pitz_params[i]); + } + count_pitz_param = 0; + pitz_param_map.clear(); + pitz_params = (struct pitz_param **) free_check_null(pitz_params); + for (i = 0; i < count_theta_param; i++) + { + theta_params[i] = + (struct theta_param *) free_check_null(theta_params[i]); + } + count_theta_param = 0; + theta_params = (struct theta_param **) free_check_null(theta_params); + LGAMMA = (LDBLE *) free_check_null(LGAMMA); + IPRSNT = (int *) free_check_null(IPRSNT); + spec = (struct species **) free_check_null(spec); + aphi = (struct pitz_param *) free_check_null(aphi); + M = (LDBLE *) free_check_null(M); + + return OK; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_pz(int initial) +/* ---------------------------------------------------------------------- */ +{ +/* + * Sets initial guesses for unknowns if initial == TRUE + * Revises guesses whether initial is true or not + */ + int i; + cxxSolution *solution_ptr; +/* + * Set initial log concentrations to zero + */ + iterations = -1; + solution_ptr = use.Get_solution_ptr(); + for (i = 0; i < count_s_x; i++) + { + s_x[i]->lm = LOG_ZERO_MOLALITY; + s_x[i]->lg_pitzer = 0.0; + } + if (initial == TRUE || set_and_run_attempt > 0) + { + for (i = 0; i < count_s_x; i++) + { + s_x[i]->lg = 0.0; + } + } +/* + * Set master species activities + */ + + tc_x = solution_ptr->Get_tc(); + tk_x = tc_x + 273.15; + + patm_x = solution_ptr->Get_patm(); // done in calc_rho_0(tc, pa) + +/* + * H+, e-, H2O + */ + mass_water_aq_x = solution_ptr->Get_mass_water(); + mu_x = solution_ptr->Get_mu(); + s_h2o->moles = mass_water_aq_x / gfw_water; + s_h2o->la = log10(solution_ptr->Get_ah2o()); + AW = pow((LDBLE) 10.0, s_h2o->la); + s_hplus->la = -solution_ptr->Get_ph(); + s_hplus->lm = s_hplus->la; + s_hplus->moles = exp(s_hplus->lm * LOG_10) * mass_water_aq_x; + s_eminus->la = -solution_ptr->Get_pe(); + if (initial == TRUE) + pitzer_initial_guesses(); + if (dl_type_x != cxxSurface::NO_DL) + initial_surface_water(); + pitzer_revise_guesses(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +pitzer_initial_guesses(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Make initial guesses for activities of master species and + * ionic strength + */ + int i; + cxxSolution *solution_ptr; + + solution_ptr = use.Get_solution_ptr(); + mu_x = + s_hplus->moles + + exp((solution_ptr->Get_ph() - 14.) * LOG_10) * mass_water_aq_x; + mu_x /= mass_water_aq_x; + s_h2o->la = 0.0; + for (i = 0; i < count_unknowns; i++) + { + if (x[i] == ph_unknown || x[i] == pe_unknown) + continue; + if (x[i]->type < CB) + { + mu_x += + x[i]->moles / mass_water_aq_x * 0.5 * x[i]->master[0]->s->z * + x[i]->master[0]->s->z; + x[i]->master[0]->s->la = log10(x[i]->moles / mass_water_aq_x); + } + else if (x[i]->type == CB) + { + x[i]->master[0]->s->la = + log10(0.001 * x[i]->moles / mass_water_aq_x); + } + else if (x[i]->type == SOLUTION_PHASE_BOUNDARY) + { + x[i]->master[0]->s->la = + log10(0.001 * x[i]->moles / mass_water_aq_x); + } + else if (x[i]->type == EXCH) + { + if (x[i]->moles <= 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else + { + x[i]->master[0]->s->la = log10(x[i]->moles); + } + } + else if (x[i]->type == SURFACE) + { + if (x[i]->moles <= 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else + { + x[i]->master[0]->s->la = log10(0.1 * x[i]->moles); + } + } + else if (x[i]->type == SURFACE_CB) + { + x[i]->master[0]->s->la = 0.0; + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +pitzer_revise_guesses(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Revise molalities species + */ + int i; + int l_iter, max_iter, repeat, fail; + LDBLE weight, f; + + max_iter = 100; + /* gammas(mu_x); */ + l_iter = 0; + repeat = TRUE; + fail = FALSE; + double d = 2; + double logd = log10(d); + while (repeat == TRUE && fail == FALSE) + { + l_iter++; + if (debug_set == TRUE) + { + output_msg(sformatf( "\nBeginning set iteration %d.\n", + l_iter)); + } + if (l_iter == max_iter + 1) + { + log_msg(sformatf( + "Did not converge in set, iteration %d.\n", + iterations)); + fail = TRUE; + } + if (l_iter > 2 * max_iter) + { + log_msg(sformatf( + "Did not converge with relaxed criteria in set.\n")); + return (OK); + } + molalities(TRUE); + /*pitzer(); */ + /*s_h2o->la = 0.0; */ + /*molalities(TRUE); */ + mb_sums(); + if (state < REACTION) + { + sum_species(); + } + else + { + for (i = 0; i < count_unknowns; i++) + { + x[i]->sum = x[i]->f; + } + } + /*n + if (debug_set == TRUE) { + pr.species = TRUE; + pr.all = TRUE; + print_species(); + } + */ + repeat = FALSE; + for (i = 0; i < count_unknowns; i++) + { + if (x[i] == ph_unknown || x[i] == pe_unknown) + continue; + if (x[i]->type == MB || +/* x[i]->type == ALK || */ + x[i]->type == CB || + x[i]->type == SOLUTION_PHASE_BOUNDARY || + x[i]->type == EXCH || x[i]->type == SURFACE) + { + + if (debug_set == TRUE) + { + output_msg(sformatf( + "\n\t%5s at beginning of set %d: %e\t%e\t%e\n", + x[i]->description, l_iter, (double) x[i]->sum, + (double) x[i]->moles, + (double) x[i]->master[0]->s->la)); + } + if (fabs(x[i]->moles) < 1e-30) + x[i]->moles = 0; + f = fabs(x[i]->sum); + if (f == 0 && x[i]->moles == 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + continue; + } + else if (f == 0) + { + repeat = TRUE; + x[i]->master[0]->s->la += logd; +/*!!!!*/ if (x[i]->master[0]->s->la < -999.) + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else if (f > d * fabs(x[i]->moles) + || f < 1.0/d * fabs(x[i]->moles)) + { + weight = (f < 1.0/d * fabs(x[i]->moles)) ? 0.3 : 1.0; + if (x[i]->moles <= 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else + { + repeat = TRUE; + x[i]->master[0]->s->la += + weight * log10(fabs(x[i]->moles / x[i]->sum)); + } + if (debug_set == TRUE) + { + output_msg(sformatf( + "\t%5s not converged in set %d: %e\t%e\t%e\n", + x[i]->description, l_iter, + (double) x[i]->sum, (double) x[i]->moles, + (double) x[i]->master[0]->s->la)); + } + } + } + else if (x[i]->type == ALK) + { + f = total_co2; + if (fail == TRUE && f < 1.5 * fabs(x[i]->moles)) + { + continue; + } + if (f > 1.5 * fabs(x[i]->moles) + || f < 1.0/d * fabs(x[i]->moles)) + { + repeat = TRUE; + weight = (f < 1.0/d * fabs(x[i]->moles)) ? 0.3 : 1.0; + x[i]->master[0]->s->la += weight * + log10(fabs(x[i]->moles / x[i]->sum)); + if (debug_set == TRUE) + { + output_msg(sformatf( + "%s not converged in set. %e\t%e\t%e\n", + x[i]->description, (double) x[i]->sum, + (double) x[i]->moles, + (double) x[i]->master[0]->s->la)); + } + } + } + } + } + log_msg(sformatf( "Iterations in pitzer_revise_guesses: %d\n", l_iter)); + /*mu_x = mu_unknown->f * 0.5 / mass_water_aq_x; */ + if (mu_x <= 1e-8) + { + mu_x = 1e-8; + } + /*gammas(mu_x); */ + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +jacobian_pz(void) +/* ---------------------------------------------------------------------- */ +{ + LDBLE *base; + LDBLE d, d1, d2; + int i, j; + +Restart: + int pz_max_unknowns = max_unknowns; + //k_temp(tc_x, patm_x); + if (full_pitzer == TRUE) + { + molalities(TRUE); + pitzer(); + residuals(); + } + base = (LDBLE *) PHRQ_malloc((size_t) count_unknowns * sizeof(LDBLE)); + if (base == NULL) + malloc_error(); + for (i = 0; i < count_unknowns; i++) + { + base[i] = residual[i]; + } + d = 0.0001; + d1 = d * log(10.0); + d2 = 0; + for (i = 0; i < count_unknowns; i++) + { + switch (x[i]->type) + { + case MB: + case ALK: + case CB: + case SOLUTION_PHASE_BOUNDARY: + case EXCH: + case SURFACE: + case SURFACE_CB: + case SURFACE_CB1: + case SURFACE_CB2: + x[i]->master[0]->s->la += d; + d2 = d1; + break; + case AH2O: + x[i]->master[0]->s->la += d; + d2 = d1; + break; + case PITZER_GAMMA: + if (!full_pitzer) + continue; + x[i]->s->lg += d; + d2 = d; + break; + case MH2O: + mass_water_aq_x *= (1.0 + d); + x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + d2 = log(1.0 + d); + break; + case MH: + if (pitzer_pe == TRUE) + { + s_eminus->la += d; + d2 = d1; + break; + } + else + { + continue; + } + case GAS_MOLES: + if (gas_in == FALSE) + continue; + d2 = d * x[i]->moles; + if (d2 < 1e-14) + d2 = 1e-14; + x[i]->moles += d2; + break; + case MU: + //continue; + d2 = d * mu_x; + mu_x += d2; + //k_temp(tc_x, patm_x); + gammas(mu_x); + break; + case PP: + case SS_MOLES: + continue; + break; + } + molalities(TRUE); + if (max_unknowns > pz_max_unknowns) + { + base = (LDBLE *) free_check_null(base); + gammas_pz(); + jacobian_sums(); + goto Restart; + } + if (full_pitzer == TRUE) + pitzer(); + mb_sums(); + residuals(); + for (j = 0; j < count_unknowns; j++) + { + array[j * (count_unknowns + 1) + i] = + -(residual[j] - base[j]) / d2; + } + switch (x[i]->type) + { + case MB: + case ALK: + case CB: + case SOLUTION_PHASE_BOUNDARY: + case EXCH: + case SURFACE: + case SURFACE_CB: + case SURFACE_CB1: + case SURFACE_CB2: + case AH2O: + x[i]->master[0]->s->la -= d; + break; + case MH: + s_eminus->la -= d; + if (array[i * (count_unknowns + 1) + i] == 0) + { + array[i * (count_unknowns + 1) + i] = + exp(s_h2->lm * LOG_10) * 2; + } + break; + case PITZER_GAMMA: + x[i]->s->lg -= d; + break; + case MH2O: + mass_water_aq_x /= (1 + d); + x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + break; + case MU: + mu_x -= d2; + //k_temp(tc_x, patm_x); + gammas(mu_x); + break; + case GAS_MOLES: + if (gas_in == FALSE) + continue; + x[i]->moles -= d2; + break; + + } + } + molalities(TRUE); + if (full_pitzer == TRUE) + pitzer(); + mb_sums(); + residuals(); + free_check_null(base); + return OK; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +model_pz(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * model is called after the equations have been set up by prep + * and initial guesses have been made in set. + * + * Here is the outline of the calculation sequence: + * residuals--residuals are calculated, if small we are done + * sum_jacobian--jacobian is calculated + * ineq--inequality solver is called + * reset--estimates of unknowns revised, if changes are small solution + * has been found, usually convergence is found in residuals. + * gammas--new activity coefficients + * molalities--calculate molalities + * mb_sums--calculate mass-balance sums + * mb_gases--decide if gas_phase exists + * mb_ss--decide if solid_solutions exists + * switch_bases--check to see if new basis species is needed + * reprep--rewrite equations with new basis species if needed + * pitzer_revise_guesses--revise unknowns to get initial mole balance + * check_residuals--check convergence one last time + * sum_species--calculate sums of elements from species concentrations + * + * An additional pass through may be needed if unstable phases still exist + * in the phase assemblage. + */ + int l_kode, return_kode; + int r; + int count_infeasible, count_basis_change; + int debug_model_save; + int mass_water_switch_save; + +/* debug_model = TRUE; */ +/* debug_prep = TRUE; */ +/* debug_set = TRUE; */ + /* mass_water_switch == TRUE, mass of water is constant */ + mass_water_switch_save = mass_water_switch; + if (mass_water_switch_save == FALSE && delay_mass_water == TRUE) + { + mass_water_switch = TRUE; + } + debug_model_save = debug_model; + pe_step_size_now = pe_step_size; + step_size_now = step_size; +#ifdef NPP + if (!use.Get_kinetics_in()) status(0, NULL); +#else + status(0, NULL); +#endif + iterations = 0; + gamma_iterations = 0; + count_basis_change = count_infeasible = 0; + stop_program = FALSE; + remove_unstable_phases = FALSE; + if (always_full_pitzer == TRUE) + { + full_pitzer = TRUE; + } + else + { + full_pitzer = FALSE; + } +#if defined(PITZER_LISTS) + //pitzer_make_lists(); +#endif + for (;;) + { + mb_gases(); + mb_ss(); + l_kode = 1; + while ((r = residuals()) != CONVERGED + || remove_unstable_phases == TRUE) + { +#if defined(PHREEQCI_GUI) + PhreeqcIWait(this); +#endif + iterations++; + if (iterations > itmax - 1 && debug_model == FALSE + && pr.logfile == TRUE) + { + set_forward_output_to_log(TRUE); + debug_model = TRUE; + } + if (debug_model == TRUE) + { + output_msg(sformatf( + "\nIteration %d\tStep_size = %f\n", iterations, + (double) step_size_now)); + output_msg(sformatf( "\t\tPe_step_size = %f\n\n", + (double) pe_step_size_now)); + } + /* + * Iterations exceeded + */ + if (iterations > itmax) + { + error_string = sformatf( "Maximum iterations exceeded, %d\n", + itmax); + warning_msg(error_string); + stop_program = TRUE; + break; + } + /* + * Calculate jacobian + */ + gammas_pz(); + jacobian_sums(); + jacobian_pz(); + /* + * Full matrix with pure phases + */ + if (r == OK || remove_unstable_phases == TRUE) + { + return_kode = ineq(l_kode); + if (return_kode != OK) + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "Ineq had infeasible solution, " + "kode %d, iteration %d\n", return_kode, + iterations)); + } + log_msg(sformatf( "Ineq had infeasible solution, " + "kode %d, iteration %d\n", return_kode, + iterations)); + count_infeasible++; + } + if (return_kode == 2) + { + ineq(0); + } + reset(); + } + gammas_pz(); + if (full_pitzer == TRUE) + pitzer(); + if (always_full_pitzer == TRUE) + { + full_pitzer = TRUE; + } + else + { + full_pitzer = FALSE; + } + if (molalities(FALSE) == ERROR) + { + pitzer_revise_guesses(); + } + if (use.Get_surface_ptr() != NULL && + use.Get_surface_ptr()->Get_dl_type() != cxxSurface::NO_DL && + use.Get_surface_ptr()->Get_related_phases()) + initial_surface_water(); + mb_sums(); + mb_gases(); + mb_ss(); +/* + * Switch bases if necessary + */ + if (switch_bases() == TRUE) + { + + count_basis_change++; + count_unknowns -= count_s_x; + reprep(); + full_pitzer = false; + } + /* debug + species_list_sort(); + sum_species(); + print_species(); + print_exchange(); + print_surface(); + */ + if (stop_program == TRUE) + { + break; + } + } +/* + * Check for stop_program + */ + + if (stop_program == TRUE) + { + break; + } + if (check_residuals() == ERROR) + { + stop_program = TRUE; + break; + } + /* remove_unstable_phases is set in check_residuals */ + if (remove_unstable_phases == FALSE && mass_water_switch_save == FALSE + && mass_water_switch == TRUE) + { + log_msg(sformatf( + "\nChanging water switch to FALSE. Iteration %d.\n", + iterations)); + mass_water_switch = FALSE; + continue; + } + gamma_iterations++; + if (gamma_iterations > itmax) + { + error_string = sformatf( "Maximum gamma iterations exceeded, %d\n", + itmax); + warning_msg(error_string); + stop_program = TRUE; + break; + } + if (check_gammas_pz() != TRUE) + { + full_pitzer = TRUE; + continue; + } + if (remove_unstable_phases == FALSE) + break; + if (debug_model == TRUE) + { + output_msg(sformatf( + "\nRemoving unstable phases. Iteration %d.\n", + iterations)); + } + log_msg(sformatf( "\nRemoving unstable phases. Iteration %d.\n", + iterations)); + } + log_msg(sformatf( "\nNumber of infeasible solutions: %d\n", + count_infeasible)); + log_msg(sformatf( "Number of basis changes: %d\n\n", + count_basis_change)); + log_msg(sformatf( "Number of iterations: %d\n", iterations)); + log_msg(sformatf( "Number of gamma iterations: %d\n\n", gamma_iterations)); + debug_model = debug_model_save; + set_forward_output_to_log(FALSE); + if (stop_program == TRUE) + { + return (ERROR); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_gammas_pz(void) +/* ---------------------------------------------------------------------- */ +{ + LDBLE /*old_aw,*/ old_mu, tol; + int converge, i; + + old_mu = mu_x; + pitzer(); + molalities(TRUE); + mb_sums(); + converge = TRUE; + tol = convergence_tolerance * 10.; + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type != PITZER_GAMMA) + continue; + if (fabs(x[i]->s->lg - x[i]->s->lg_pitzer) > tol) + { + converge = FALSE; + } + } + if (fabs(old_mu - mu_x) > tol) + converge = FALSE; + + if ((pow((LDBLE) 10.0, s_h2o->la) - AW) > tol) + converge = FALSE; + return converge; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +gammas_pz() +/* ---------------------------------------------------------------------- */ +{ +/* + * Need exchange gammas for pitzer + */ + int i, j; + LDBLE coef; + /* Initialize */ + k_temp(tc_x, patm_x); +/* + * Calculate activity coefficients + */ + for (i = 0; i < count_s_x; i++) + { + switch (s_x[i]->gflag) + { + case 0: /* uncharged */ + case 1: /* Davies */ + case 2: /* Extended D-H, WATEQ D-H */ + case 3: /* Always 1.0 */ + break; + case 4: /* Exchange */ + /* Now calculated in next loop */ + break; + case 5: /* Always 1.0 */ + break; + case 6: /* Surface */ +/* + * Find moles of sites. + * s_x[i]->equiv is stoichiometric coefficient of sites in species + */ + for (j = 1; s_x[i]->rxn_x->token[j].s != NULL; j++) + { + if (s_x[i]->rxn_x->token[j].s->type == SURF) + { + s_x[i]->alk = + s_x[i]->rxn_x->token[j].s->primary->unknown->moles; + break; + } + } + if (s_x[i]->alk > 0) + { + s_x[i]->lg = log10(s_x[i]->equiv / s_x[i]->alk); + s_x[i]->dg = 0.0; + } + else + { + s_x[i]->lg = 0.0; + s_x[i]->dg = 0.0; + } + break; + case 7: /* LLNL */ + break; + case 8: /* LLNL CO2 */ + break; + case 9: /* activity water */ + s_x[i]->lg = log10(exp(s_h2o->la * LOG_10) * gfw_water); + s_x[i]->dg = 0.0; + break; + } +/* + if (mu_unknown != NULL) { + if (fabs(residual[mu_unknown->number]) > 0.1 && + fabs(residual[mu_unknown->number])/mu_x > 0.5) { + s_x[i]->dg = 0.0; + } + } + */ + } + /* + * calculate exchange gammas + */ + + if (use.Get_exchange_ptr() != NULL) + { + for (i = 0; i < count_s_x; i++) + { + switch (s_x[i]->gflag) + { + case 0: /* uncharged */ + case 1: /* Davies */ + case 2: /* Extended D-H, WATEQ D-H */ + case 3: /* Always 1.0 */ + case 5: /* Always 1.0 */ + case 6: /* Surface */ + case 7: /* LLNL */ + case 8: /* LLNL CO2 */ + case 9: /* activity water */ + break; + case 4: /* Exchange */ + + /* + * Find CEC + * z contains valence of cation for exchange species, alk contains cec + */ + /* !!!!! */ + for (j = 1; s_x[i]->rxn_x->token[j].s != NULL; j++) + { + if (s_x[i]->rxn_x->token[j].s->type == EX) + { + s_x[i]->alk = + s_x[i]->rxn_x->token[j].s->primary->unknown-> + moles; + break; + } + } + /* + * Master species is a dummy variable with meaningless activity and mass + */ + s_x[i]->lg = 0.0; + s_x[i]->dg = 0.0; + if (s_x[i]->primary != NULL) + { + break; + } + /* + * All other species + */ + + /* modific 29 july 2005... */ + if (s_x[i]->equiv != 0 && s_x[i]->alk > 0) + { + s_x[i]->lg = log10(fabs(s_x[i]->equiv) / s_x[i]->alk); + } + if (use.Get_exchange_ptr()->Get_pitzer_exchange_gammas()) + { + /* Assume equal gamma's of solute and exchangeable species... */ + for (j = 1; s_x[i]->rxn_x->token[j].s != NULL; j++) + { + if (s_x[i]->rxn_x->token[j].s->type == EX) + continue; + coef = s_x[i]->rxn_x->token[j].coef; + s_x[i]->lg += coef * s_x[i]->rxn_x->token[j].s->lg; + s_x[i]->dg += coef * s_x[i]->rxn_x->token[j].s->dg; + } + } + } + } + } +/* ...end modific 29 july 2005 */ + + return (OK); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +pitzer_make_lists(void) +/* ---------------------------------------------------------------------- */ +{ + double log_min = log10(MIN_TOTAL); + s_list.clear(); + cation_list.clear(); + neutral_list.clear(); + anion_list.clear(); + ion_list.clear(); + param_list.clear(); + OTEMP = -100.0; + for (int j = 0; j < 3; j++) + { + int min, max; + switch (j) + { + case 0: + min = 0; + max = count_cations; + break; + case 1: + min = count_s; + max = count_s + count_neutrals; + break; + case 2: + min = 2*count_s; + max = 2*count_s + count_anions; + break; + } + for (int i = min; i < max; i++) + { + IPRSNT[i] = FALSE; + M[i] = 0.0; + if ((spec[i] != NULL && spec[i]->in == TRUE) || + (ICON == TRUE && i == IC)) + { + if (spec[i]->type == EX || + spec[i]->type == SURF || spec[i]->type == SURF_PSI) + continue; + IPRSNT[i] = TRUE; + s_list.push_back(i); + if (i < count_s) + { + cation_list.push_back(i); + } + if (i >= count_s && i < 2*count_s) + { + neutral_list.push_back(i); + } + if (i >= 2*count_s) + { + anion_list.push_back(i); + } + if (i < count_s || i >= 2*count_s) + { + ion_list.push_back(i); + } + if (spec[i]->lm > log_min) + { + M[i] = under(spec[i]->lm); + } + } + } + } + if (ICON == TRUE) + { + IPRSNT[IC] = TRUE; + } + for (int i = 0; i < count_pitz_param; i++) + { + /* + TYPE_B0, TYPE_B1, TYPE_B2, TYPE_C0, TYPE_THETA, TYPE_LAMDA, TYPE_ZETA, + TYPE_PSI, TYPE_ETHETA, TYPE_ALPHAS, TYPE_MU, TYPE_ETA, TYPE_Other, + TYPE_SIT_EPSILON, TYPE_SIT_EPSILON_MU + */ + int i0 = pitz_params[i]->ispec[0]; + int i1 = pitz_params[i]->ispec[1]; + if (IPRSNT[i0] == FALSE || IPRSNT[i1] == FALSE) continue; + int i2 = pitz_params[i]->ispec[2]; + if (pitz_params[i]->type == TYPE_PSI || + pitz_params[i]->type == TYPE_ZETA || + pitz_params[i]->type == TYPE_MU || + pitz_params[i]->type == TYPE_ETA) + { + if (IPRSNT[i2] == FALSE) + continue; + } + param_list.push_back(i); + } +} \ No newline at end of file diff --git a/phreeqcpp/pitzer_structures.cpp b/phreeqcpp/pitzer_structures.cpp new file mode 100644 index 00000000..061eb715 --- /dev/null +++ b/phreeqcpp/pitzer_structures.cpp @@ -0,0 +1,362 @@ +#include "Phreeqc.h" +#include "phqalloc.h" + +#include +#include + +/* ********************************************************************** + * + * Routines related to structure "pitz_param" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +struct pitz_param * Phreeqc:: +pitz_param_alloc(void) +/* ---------------------------------------------------------------------- */ +{ + struct pitz_param *pitz_param_ptr; + pitz_param_ptr = + (struct pitz_param *) PHRQ_malloc(sizeof(struct pitz_param)); + if (pitz_param_ptr == NULL) + malloc_error(); + return (pitz_param_ptr); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +pitz_param_init(struct pitz_param *pitz_param_ptr) +/* ---------------------------------------------------------------------- */ +{ + int i; +/* + * Frees all data associated with pitz_param structure. + */ + + if (pitz_param_ptr == NULL) + return (ERROR); + pitz_param_ptr->species[0] = NULL; + pitz_param_ptr->species[1] = NULL; + pitz_param_ptr->species[2] = NULL; + pitz_param_ptr->ispec[0] = -1; + pitz_param_ptr->ispec[1] = -1; + pitz_param_ptr->ispec[2] = -1; + pitz_param_ptr->type = TYPE_Other; + pitz_param_ptr->p = 0.0; + pitz_param_ptr->U.b0 = 0.0; + for (i = 0; i < 6; i++) + { + pitz_param_ptr->a[i] = 0.0; + } + pitz_param_ptr->alpha = 0.0; + pitz_param_ptr->thetas = NULL; + pitz_param_ptr->os_coef = 0.; + for (i = 0; i < 3; i++) + { + pitz_param_ptr->ln_coef[i] = 0.0; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +struct pitz_param * Phreeqc:: +pitz_param_read(char *string, int n) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read pitzer parameter info from string + * n is number of species (character values) + * + */ + int l, i, j, k; + char *ptr; + char token[2 * MAX_LENGTH]; + struct pitz_param pzp, *pzp_ptr; + + if (n != 2 && n != 3 && n != 0) + return (NULL); + if (string == NULL) + return (NULL); + + pitz_param_init(&pzp); + ptr = string; + if (copy_token(token, &ptr, &l) == EMPTY) + return (NULL); + ptr = string; + for (i = 0; i < n; i++) + { + int j = copy_token(token, &ptr, &l); + if (j == EMPTY) + return (NULL); + if (j != UPPER && token[0] != '(') + { + input_error++; + std::ostringstream err; + err << "Wrong number of species for a Pitzer parameter.\n" << line; + error_msg(err.str().c_str(), CONTINUE); + } + pzp.species[i] = string_hsave(token); + } + k = 0; + for (i = 0; i < 6; i++) + { + if (copy_token(token, &ptr, &l) == EMPTY) + break; + j = sscanf(token, SCANFORMAT, &pzp.a[i]); + if (j <= 0) + break; + k++; + } + if (k <= 0) + return (NULL); + pzp_ptr = pitz_param_duplicate(&pzp); + return (pzp_ptr); +} + +/* ---------------------------------------------------------------------- */ +struct pitz_param * Phreeqc:: +pitz_param_duplicate(struct pitz_param *old_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Allocates space and makes duplicate copy of pitz_param structure + */ + struct pitz_param *new_ptr; + + new_ptr = pitz_param_alloc(); + pitz_param_init(new_ptr); +/* + * Copy data + */ + pitz_param_copy(old_ptr, new_ptr); + return (new_ptr); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +pitz_param_copy(struct pitz_param *old_ptr, struct pitz_param *new_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Copies pitz_param data from old_ptr to new location, new_ptr. + * Space for the new_ptr structure must already be malloced. + */ +/* + * Store data for structure pitz_param + */ + memcpy(new_ptr, old_ptr, sizeof(struct pitz_param)); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +pitz_param_store(struct pitz_param *pzp_ptr, bool force_copy) +/* ---------------------------------------------------------------------- */ +{ +/* + * Does linear search of pitz_params for same type and species + * Returns -1 if not found, index number in pitz_params if found + */ + int i; + if (pzp_ptr == NULL) + return; + if (pzp_ptr->type == TYPE_Other) + return; + + std::set< std::string > header; + for (i = 0; i < 3; i++) + { + if (pzp_ptr->species[i] != NULL) header.insert(pzp_ptr->species[i]); + } + + std::ostringstream key_str; + key_str << pzp_ptr->type << " "; + std::set< std::string >::iterator it = header.begin(); + for(; it != header.end(); ++it) + { + key_str << *it << " "; + } + std::string key = key_str.str().c_str(); + std::map< std::string, size_t>::iterator jit = pitz_param_map.find(key); + if (jit != pitz_param_map.end()) + { + if (pzp_ptr->species[2] != NULL) + { + error_string = sformatf( "Redefinition of parameter, %s %s %s\n", + pzp_ptr->species[0], pzp_ptr->species[1], pzp_ptr->species[2]); + } + else + { + error_string = sformatf( "Redefinition of parameter, %s %s\n", + pzp_ptr->species[0], pzp_ptr->species[1]); + } + warning_msg(error_string); + pitz_params[(*jit).second] = (struct pitz_param *) free_check_null(pitz_params[(*jit).second]); + pitz_params[(*jit).second] = pzp_ptr; + } + else + { + if (count_pitz_param >= max_pitz_param) + { + space((void **) ((void *) &pitz_params), + count_pitz_param, &max_pitz_param, + sizeof(struct pitz_param *)); + } + if (force_copy) + { + pitz_params[count_pitz_param] = pitz_param_duplicate(pzp_ptr); + // clean up pointers + // species + for (i = 0; i < 3; i++) + { + if (pzp_ptr->species[i] != NULL) + { + pitz_params[count_pitz_param]->species[i] = string_hsave(pzp_ptr->species[i]); + } + } + // thetas + pitz_params[count_pitz_param]->thetas = NULL; + } + else + { + pitz_params[count_pitz_param] = pzp_ptr; + } + pitz_param_map[key] = count_pitz_param; + count_pitz_param++; + } +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +sit_param_store(struct pitz_param *pzp_ptr, bool force_copy) +/* ---------------------------------------------------------------------- */ +{ +/* + * Does linear search of pitz_params for same type and species + * Returns -1 if not found, index number in pitz_params if found + */ + int i; + if (pzp_ptr == NULL) + return; + if (pzp_ptr->type == TYPE_Other) + return; + + std::set< std::string > header; + for (i = 0; i < 3; i++) + { + if (pzp_ptr->species[i] != NULL) header.insert(pzp_ptr->species[i]); + } + + std::ostringstream key_str; + key_str << pzp_ptr->type << " "; + std::set< std::string >::iterator it = header.begin(); + for(; it != header.end(); ++it) + { + key_str << *it << " "; + } + std::string key = key_str.str().c_str(); + + std::map< std::string, size_t>::iterator jit = sit_param_map.find(key); + if (jit != sit_param_map.end()) + { + if (pzp_ptr->species[2] != NULL) + { + error_string = sformatf( "Redefinition of parameter, %s %s %s\n", + pzp_ptr->species[0], pzp_ptr->species[1], pzp_ptr->species[2]); + } + else + { + error_string = sformatf( "Redefinition of parameter, %s %s\n", + pzp_ptr->species[0], pzp_ptr->species[1]); + } + warning_msg(error_string); + sit_params[(*jit).second] = (struct pitz_param *) free_check_null(sit_params[(*jit).second]); + sit_params[(*jit).second] = pzp_ptr; + } + else + { + if (count_sit_param >= max_sit_param) + { + space((void **) ((void *) &sit_params), + count_sit_param, &max_sit_param, + sizeof(struct pitz_param *)); + } + if (force_copy) + { + sit_params[count_sit_param] = pitz_param_duplicate(pzp_ptr); + // clean up pointers + // species + for (i = 0; i < 3; i++) + { + if (pzp_ptr->species[i] != NULL) + { + sit_params[count_sit_param]->species[i] = string_hsave(pzp_ptr->species[i]); + } + } + // thetas + sit_params[count_sit_param]->thetas = NULL; + } + else + { + sit_params[count_sit_param] = pzp_ptr; + } + sit_param_map[key] = count_sit_param; + count_sit_param++; + } +} + +/* ********************************************************************** + * + * Routines related to structure "theta_parm" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +struct theta_param * Phreeqc:: +theta_param_alloc(void) +/* ---------------------------------------------------------------------- */ +{ + struct theta_param *theta_param_ptr; + theta_param_ptr = + (struct theta_param *) PHRQ_malloc(sizeof(struct theta_param)); + if (theta_param_ptr == NULL) + malloc_error(); + return (theta_param_ptr); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +theta_param_init(struct theta_param *theta_param_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Frees all data associated with theta_param structure. + */ + + if (theta_param_ptr == NULL) + return (ERROR); + theta_param_ptr->zj = 0; + theta_param_ptr->zk = 0; + theta_param_ptr->etheta = 0; + theta_param_ptr->ethetap = 0; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +struct theta_param * Phreeqc:: +theta_param_search(LDBLE zj, LDBLE zk) +/* ---------------------------------------------------------------------- */ +{ +/* + * Does linear search of theta_params for same charge + * Returns NULL if not found, index number in theta_params if found + */ + int i; + for (i = 0; i < count_theta_param; i++) + { + if ((theta_params[i]->zj == zj && theta_params[i]->zk == zk) || + (theta_params[i]->zj == zk && theta_params[i]->zk == zj)) + { + return theta_params[i]; + } + } + return NULL; +} diff --git a/phreeqcpp/prep.cpp b/phreeqcpp/prep.cpp new file mode 100644 index 00000000..c3f29436 --- /dev/null +++ b/phreeqcpp/prep.cpp @@ -0,0 +1,6605 @@ +#include "Utils.h" +#include "Phreeqc.h" +#include "phqalloc.h" +#include +#include +#include "Exchange.h" +#include "GasPhase.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "SS.h" +#include "Solution.h" +#include "cxxKinetics.h" +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +prep(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Input is model defined by the structure use. + * Most of routine is skipped if model, as defined by master.total + * plus use.pure_phases, is same as previous calculation. + * Routine sets up struct unknown for each unknown. + * Determines elements, species, and phases that are in the model. + * Calculates mass-action equations for each species and phase. + * Routine builds a set of lists for calculating mass balance and + * for building jacobian. + */ + cxxSolution *solution_ptr; + + if (state >= REACTION) + { + same_model = check_same_model(); + } + else + { + same_model = FALSE; + last_model.force_prep = TRUE; + } + /*same_model = FALSE; */ +/* + * Initialize s, master, and unknown pointers + */ + solution_ptr = use.Get_solution_ptr(); + if (solution_ptr == NULL) + { + error_msg("Solution needed for calculation not found, stopping.", + STOP); + return ERROR; + } + description_x = (char *) free_check_null(description_x); + description_x = string_duplicate(solution_ptr->Get_description().c_str()); +/* + * Allocate space for unknowns + * Must allocate all necessary space before pointers to + * X are set. + */ + + //if (!same_model && !switch_numerical) + // numerical_fixed_volume = false; + if (same_model == FALSE /*|| switch_numerical*/) + { + clear(); + setup_unknowns(); +/* + * Set unknown pointers, unknown types, validity checks + */ + if (state == INITIAL_SOLUTION) + convert_units(solution_ptr); + setup_solution(); + setup_exchange(); + setup_surface(); + setup_pure_phases(); + setup_gas_phase(); + setup_ss_assemblage(); + setup_related_surface(); + tidy_redox(); + if (get_input_errors() > 0) + { + error_msg("Program terminating due to input errors.", STOP); + } +/* + * Allocate space for array + */ +/* + array = (LDBLE *) PHRQ_malloc( (size_t) (count_unknowns+1) * count_unknowns * sizeof( LDBLE )); + if (array == NULL) malloc_error(); + delta = (LDBLE *) PHRQ_malloc( (size_t) count_unknowns * sizeof( LDBLE )); + if (delta == NULL) malloc_error(); + residual = (LDBLE *) PHRQ_malloc( (size_t) count_unknowns * sizeof( LDBLE )); + if (residual == NULL) malloc_error(); +*/ + array = + (LDBLE *) PHRQ_malloc((size_t) (max_unknowns + 1) * + max_unknowns * sizeof(LDBLE)); + if (array == NULL) + malloc_error(); + delta = (LDBLE *) PHRQ_malloc((size_t) max_unknowns * sizeof(LDBLE)); + if (delta == NULL) + malloc_error(); + residual = + (LDBLE *) PHRQ_malloc((size_t) max_unknowns * sizeof(LDBLE)); + if (residual == NULL) + malloc_error(); + for (int j = 0; j < max_unknowns; j++) + { + residual[j] = 0; + } + +/* + * Build lists to fill Jacobian array and species list + */ + build_model(); + adjust_setup_pure_phases(); + adjust_setup_solution(); + } + else + { +/* + * If model is same, just update masses, don`t rebuild unknowns and lists + */ + quick_setup(); + } + if (get_input_errors() > 0) + { + error_msg("Program stopping due to input errors.", STOP); + } + if (sit_model) sit_make_lists(); + if (pitzer_model) pitzer_make_lists(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +quick_setup(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Routine is used if model is the same as previous model + * Assumes moles of elements, exchangers, surfaces, gases, and solid solutions have + * been accumulated in array master, usually by subroutine step. + * Updates essential information for the model. + */ + int i; + for (i = 0; i < count_master; i++) + { + if (master[i]->s->type == SURF_PSI) + continue; + if (master[i]->s == s_eminus || + master[i]->s == s_hplus || + master[i]->s == s_h2o || master[i]->s == s_h2 + || master[i]->s == s_o2) + continue; + if (master[i]->total > 0) + { + if (master[i]->s->secondary != NULL) + { + master[i]->s->secondary->unknown->moles = master[i]->total; + } + else + { + master[i]->unknown->moles = master[i]->total; + } + } + } +/* + * Reaction: pH for charge balance + */ + ph_unknown->moles = use.Get_solution_ptr()->Get_cb(); +/* + * Reaction: pe for total hydrogen + */ + if (mass_hydrogen_unknown != NULL) + { +/* Use H - 2O linear combination in place of H */ +#define COMBINE + /*#define COMBINE_CHARGE */ +#ifdef COMBINE + mass_hydrogen_unknown->moles = + use.Get_solution_ptr()->Get_total_h() - 2 * use.Get_solution_ptr()->Get_total_o(); +#else + mass_hydrogen_unknown->moles = use.Get_solution_ptr()->total_h; +#endif + } +/* + * Reaction H2O for total oxygen + */ + if (mass_oxygen_unknown != NULL) + { + mass_oxygen_unknown->moles = use.Get_solution_ptr()->Get_total_o(); + } + +/* + * pp_assemblage + */ + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == PP) + { + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + std::map::iterator it; + //it = pp_assemblage_ptr->Get_pp_assemblage_comps().find(x[i]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name); + assert(comp_ptr != NULL); + //assert(it != pp_assemblage_ptr->Get_pp_assemblage_comps().end()); + //cxxPPassemblageComp * comp_ptr = &(it->second); + x[i]->pp_assemblage_comp_ptr = comp_ptr; + x[i]->moles = comp_ptr->Get_moles(); + /* A. Crapsi */ + x[i]->si = comp_ptr->Get_si(); + x[i]->delta = comp_ptr->Get_delta(); + /* End A. Crapsi */ + x[i]->dissolve_only = comp_ptr->Get_dissolve_only() ? TRUE : FALSE; + comp_ptr->Set_delta(0.0); + } + } + // Need to update SIs for gases + adjust_setup_pure_phases(); + +/* + * gas phase + */ + if (gas_unknown != NULL) + { + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + if ((gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME) && + numerical_fixed_volume && + (gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume)) + { + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + gas_unknowns[i]->moles = gc_ptr->Get_moles(); + if (gas_unknowns[i]->moles <= 0) + gas_unknowns[i]->moles = MIN_TOTAL; + gas_unknowns[i]->phase->pr_in = false; + gas_unknowns[i]->phase->pr_phi = 1.0; + gas_unknowns[i]->phase->pr_p = 0; + } + } + else + { + gas_unknown->moles = 0.0; + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + gas_unknown->moles += gc_ptr->Get_moles(); + } + if (gas_unknown->moles <= 0) + gas_unknown->moles = MIN_TOTAL; + gas_unknown->ln_moles = log(gas_unknown->moles); + } + } + +/* + * ss_assemblage + */ + if (ss_unknown != NULL) + { + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == SS_MOLES) + break; + } + + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t j = 0; j < ss_ptrs.size(); j++) + { + for (size_t k = 0; k < ss_ptrs[j]->Get_ss_comps().size(); k++) + { + x[i]->ss_ptr = ss_ptrs[j]; + cxxSScomp *comp_ptr = &(ss_ptrs[j]->Get_ss_comps()[k]); + x[i]->ss_comp_ptr = comp_ptr; + x[i]->moles = comp_ptr->Get_moles(); + if (x[i]->moles <= 0) + { + x[i]->moles = MIN_TOTAL_SS; + comp_ptr->Set_moles(MIN_TOTAL_SS); + } + comp_ptr->Set_initial_moles(x[i]->moles); + x[i]->ln_moles = log(x[i]->moles); + + x[i]->phase->dn = comp_ptr->Get_dn(); + x[i]->phase->dnb = comp_ptr->Get_dnb(); + x[i]->phase->dnc = comp_ptr->Get_dnc(); + x[i]->phase->log10_fraction_x = comp_ptr->Get_log10_fraction_x(); + x[i]->phase->log10_lambda = comp_ptr->Get_log10_lambda(); + i++; + } + } + } +/* + * exchange + */ + // number of moles is set from master->moles above +/* + * surface + */ + if (use.Get_surface_ptr() != NULL) + { + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == SURFACE) + { + break; + } + } + for (; i < count_unknowns; i++) + { + if (x[i]->type == SURFACE_CB) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + x[i]->related_moles = charge_ptr->Get_grams(); + x[i]->mass_water = charge_ptr->Get_mass_water(); +#ifdef DEBUG + /* test that charge and surface match */ + cxxSurfaceComp *comp_ptr = use.Get_surface_ptr()->Find_comp(x[i]->surface_comp); + char * temp_formula = string_duplicate(comp_ptr->Get_formula().c_str()); + char * ptr = temp_formula; + copy_token(token, &ptr, &l); + char * ptr1 = token; + get_elt(&ptr1, name, &l); + ptr1 = strchr(name, '_'); + if (ptr1 != NULL) + ptr1[0] = '\0'; + if (strcmp(name, charge_ptr->Get_name().c_str()) != 0) + { + free_check_null(temp_formula); + error_string = sformatf( + "Internal error: Surface charge name %s does not match surface component name %s\nTry alphabetical order for surfaces in SURFACE", + charge_ptr->Get_name().c_str(), + comp_ptr->Get_formula().c_str()); + error_msg(error_string, STOP); + } + free_check_null(temp_formula); +#endif + /* moles picked up from master->total */ + } + else if (x[i]->type == SURFACE_CB1 || x[i]->type == SURFACE_CB2) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + x[i]->related_moles = charge_ptr->Get_grams(); + x[i]->mass_water = charge_ptr->Get_mass_water(); + } + else if (x[i]->type == SURFACE) + { + /* moles picked up from master->total + except for surfaces related to kinetic minerals ... */ + cxxSurfaceComp *comp_ptr = use.Get_surface_ptr()->Find_comp(x[i]->surface_comp); + if (comp_ptr->Get_rate_name().size() > 0) + { + cxxNameDouble::iterator lit; + for (lit = comp_ptr->Get_totals().begin(); lit != comp_ptr->Get_totals().end(); lit++) + { + struct element *elt_ptr = element_store(lit->first.c_str()); + struct master *master_ptr = elt_ptr->master; + if (master_ptr->type != SURF) + continue; + if (strcmp_nocase(x[i]->description, lit->first.c_str()) == 0) + { + x[i]->moles = lit->second; + } + } + } + } + else + { + break; + } + + } + } + save_model(); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_gas_phase(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Put coefficients into lists to sum iaps to test for equilibrium + * Put coefficients into lists to build jacobian for + * sum of partial pressures equation and + * mass balance equations for elements contained in gases + */ + int row, col; + struct master *master_ptr; + struct rxn_token *rxn_ptr; + struct unknown *unknown_ptr; + LDBLE coef, coef_elt; + + if (gas_unknown == NULL) + return (OK); + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME && + (gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume) && + numerical_fixed_volume) + { + return build_fixed_volume_gas(); + } + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + int k; + struct phase *phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str() , &k, FALSE); + assert(phase_ptr); +/* + * Determine elements in gas component + */ + count_elts = 0; + paren_count = 0; + if (phase_ptr->rxn_x == NULL) + continue; + add_elt_list(phase_ptr->next_elt, 1.0); +#ifdef COMBINE + change_hydrogen_in_elt_list(0); +#endif +/* + * Build mass balance sums for each element in gas + */ + if (debug_prep == TRUE) + { + output_msg(sformatf( "\n\tMass balance summations %s.\n\n", + phase_ptr->name)); + } + + /* All elements in gas */ + for (int j = 0; j < count_elts; j++) + { + unknown_ptr = NULL; + if (strcmp(elt_list[j].elt->name, "H") == 0) + { + unknown_ptr = mass_hydrogen_unknown; + } + else if (strcmp(elt_list[j].elt->name, "O") == 0) + { + unknown_ptr = mass_oxygen_unknown; + } + else + { + if (elt_list[j].elt->primary->in == TRUE) + { + unknown_ptr = elt_list[j].elt->primary->unknown; + } + else if (elt_list[j].elt->primary->s->secondary != NULL) + { + unknown_ptr = + elt_list[j].elt->primary->s->secondary->unknown; + } + } + if (unknown_ptr != NULL) + { + coef = elt_list[j].coef; + store_mb(&(phase_ptr->moles_x), &(unknown_ptr->f), coef); + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\n", + unknown_ptr->description, (double) coef)); + } + } + } + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + /* Total pressure of gases */ + store_mb(&(phase_ptr->p_soln_x), &(gas_unknown->f), 1.0); + } +/* + * Build jacobian sums for mass balance equations + */ + if (debug_prep == TRUE) + { + output_msg(sformatf( "\n\tJacobian summations %s.\n\n", + phase_ptr->name)); + } + for (int j = 0; j < count_elts; j++) + { + unknown_ptr = NULL; + if (strcmp(elt_list[j].elt->name, "H") == 0) + { + unknown_ptr = mass_hydrogen_unknown; + } + else if (strcmp(elt_list[j].elt->name, "O") == 0) + { + unknown_ptr = mass_oxygen_unknown; + } + else + { + if (elt_list[j].elt->primary->in == TRUE) + { + unknown_ptr = elt_list[j].elt->primary->unknown; + } + else if (elt_list[j].elt->primary->s->secondary != NULL) + { + unknown_ptr = + elt_list[j].elt->primary->s->secondary->unknown; + } + } + if (unknown_ptr == NULL) + { + continue; + } + if (debug_prep == TRUE) + { + output_msg(sformatf( "\n\t%s.\n", + unknown_ptr->description)); + } + row = unknown_ptr->number * (count_unknowns + 1); + coef_elt = elt_list[j].coef; + for (rxn_ptr = phase_ptr->rxn_x->token + 1; + rxn_ptr->s != NULL; rxn_ptr++) + { + + if (rxn_ptr->s->secondary != NULL + && rxn_ptr->s->secondary->in == TRUE) + { + master_ptr = rxn_ptr->s->secondary; + } + else if (rxn_ptr->s->primary != NULL && rxn_ptr->s->primary->in == TRUE) + { + master_ptr = rxn_ptr->s->primary; + } + else + { + master_ptr = master_bsearch_primary(rxn_ptr->s->name); + master_ptr->s->la = -999.0; + } + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%s\n", + master_ptr->s->name)); + } + if (master_ptr->unknown == NULL) + { + continue; + } + if (master_ptr->in == FALSE) + { + error_string = sformatf( + "Element, %s, in phase, %s, is not in model.", + master_ptr->elt->name, phase_ptr->name); + error_msg(error_string, CONTINUE); + input_error++; + } + col = master_ptr->unknown->number; + coef = coef_elt * rxn_ptr->coef; + store_jacob(&(phase_ptr->moles_x), + &(array[row + col]), coef); + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d\n", + master_ptr->s->name, (double) coef, + row / (count_unknowns + 1), col)); + } + } + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + /* derivative wrt total moles of gas */ + store_jacob(&(phase_ptr->fraction_x), + &(array[row + gas_unknown->number]), coef_elt); + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d\n", + "gas moles", (double) elt_list[j].coef, + row / (count_unknowns + 1), + gas_unknown->number)); + } + } + } +/* + * Build jacobian sums for sum of partial pressures equation + */ + if (gas_phase_ptr->Get_type() != cxxGasPhase::GP_PRESSURE) + continue; + if (debug_prep == TRUE) + { + output_msg(sformatf( "\n\tPartial pressure eqn %s.\n\n", + phase_ptr->name)); + } + unknown_ptr = gas_unknown; + row = unknown_ptr->number * (count_unknowns + 1); + for (rxn_ptr = phase_ptr->rxn_x->token + 1; rxn_ptr->s != NULL; rxn_ptr++) + { + if (rxn_ptr->s != s_eminus && rxn_ptr->s->in == FALSE) + { + error_string = sformatf( + "Element in species, %s, in phase, %s, is not in model.", + rxn_ptr->s->name, phase_ptr->name); + warning_msg(error_string); + } + else + { + if (rxn_ptr->s->secondary != NULL + && rxn_ptr->s->secondary->in == TRUE) + { + master_ptr = rxn_ptr->s->secondary; + } + else if (rxn_ptr->s->primary != NULL && rxn_ptr->s->primary->in == TRUE) + { + master_ptr = rxn_ptr->s->primary; + } + else + { + master_ptr = master_bsearch_primary(rxn_ptr->s->name); + if (master_ptr && master_ptr->s) + { + master_ptr->s->la = -999.0; + } + } + if (master_ptr == NULL) + { + error_string = sformatf( + "Master species for %s, in phase, %s, is not in model.", + rxn_ptr->s->name, phase_ptr->name); + error_msg(error_string, CONTINUE); + input_error++; + } + else + { + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%s\n", master_ptr->s->name)); + } + if (master_ptr->unknown == NULL) + { + assert(false); + continue; + } + if (master_ptr->in == FALSE) + { + error_string = sformatf( + "Element, %s, in phase, %s, is not in model.", + master_ptr->elt->name, phase_ptr->name); + warning_msg(error_string); + } + col = master_ptr->unknown->number; + coef = rxn_ptr->coef; + store_jacob(&(phase_ptr->p_soln_x), &(array[row + col]), coef); + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d\n", + master_ptr->s->name, (double) coef, + row / (count_unknowns + 1), col)); + } + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_ss_assemblage(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Put coefficients into lists to sum iaps to test for equilibrium + * Put coefficients into lists to build jacobian for + * mass action equation for component + * mass balance equations for elements contained in solid solutions + */ + bool stop; + int row, col; + struct master *master_ptr; + struct rxn_token *rxn_ptr; + char *ptr; + + if (ss_unknown == NULL) + return (OK); + cxxSS * ss_ptr_old = NULL; + col = 0; + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type != SS_MOLES) + continue; + //cxxSS *ss_ptr = use.Get_ss_assemblage_ptr()->Find(x[i]->ss_name); + cxxSS *ss_ptr = (cxxSS *) x[i]->ss_ptr; + assert(ss_ptr); + if (ss_ptr != ss_ptr_old) + { + col = x[i]->number; + ss_ptr_old = ss_ptr; + } +/* + * Calculate function value (inverse saturation index) + */ + if (x[i]->phase->rxn_x == NULL) + continue; + store_mb(&(x[i]->phase->lk), &(x[i]->f), 1.0); + for (rxn_ptr = x[i]->phase->rxn_x->token + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + store_mb(&(rxn_ptr->s->la), &(x[i]->f), -rxn_ptr->coef); + } + /* include mole fraction */ + store_mb(&(x[i]->phase->log10_fraction_x), &(x[i]->f), 1.0); + + /* include activity coeficient */ + store_mb(&(x[i]->phase->log10_lambda), &(x[i]->f), 1.0); +/* + * Put coefficients into mass action equations + */ + /* first IAP terms */ + for (rxn_ptr = x[i]->phase->rxn_x->token + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + if (rxn_ptr->s->secondary != NULL + && rxn_ptr->s->secondary->in == TRUE) + { + master_ptr = rxn_ptr->s->secondary; + } + else + { + master_ptr = rxn_ptr->s->primary; + } + if (master_ptr == NULL || master_ptr->unknown == NULL) + continue; + store_jacob0(x[i]->number, master_ptr->unknown->number, + rxn_ptr->coef); + } + + if (ss_ptr->Get_a0() != 0.0 || ss_ptr->Get_a1() != 0.0) + { +/* + * For binary solid solution + */ + /* next dnc terms */ + row = x[i]->number * (count_unknowns + 1); + if (x[i]->ss_comp_number == 0) + { + col = x[i]->number; + } + else + { + col = x[i]->number - 1; + } + store_jacob(&(x[i]->phase->dnc), &(array[row + col]), -1); + + /* next dnb terms */ + col++; + store_jacob(&(x[i]->phase->dnb), &(array[row + col]), -1); + } + else + { +/* + * For ideal solid solution + */ + row = x[i]->number * (count_unknowns + 1); + for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++) + { + if ((int) j != x[i]->ss_comp_number) + { +/* store_jacob (&(s_s_ptr->dn), &(array[row + col + j]), -1.0); */ + store_jacob(&(x[i]->phase->dn), &(array[row + col + j]), + -1.0); + } + else + { + store_jacob(&(x[i]->phase->dnb), &(array[row + col + j]), + -1.0); + } + } + } +/* + * Put coefficients into mass balance equations + */ + count_elts = 0; + paren_count = 0; + char * token = string_duplicate(x[i]->phase->formula); + ptr = token; + get_elts_in_species(&ptr, 1.0); + free_check_null(token); +/* + * Go through elements in phase + */ +#ifdef COMBINE + change_hydrogen_in_elt_list(0); +#endif + for (int j = 0; j < count_elts; j++) + { + + if (strcmp(elt_list[j].elt->name, "H") == 0 + && mass_hydrogen_unknown != NULL) + { + store_jacob0(mass_hydrogen_unknown->number, x[i]->number, + -elt_list[j].coef); + store_sum_deltas(&(delta[i]), &mass_hydrogen_unknown->delta, + elt_list[j].coef); + + } + else if (strcmp(elt_list[j].elt->name, "O") == 0 + && mass_oxygen_unknown != NULL) + { + store_jacob0(mass_oxygen_unknown->number, x[i]->number, + -elt_list[j].coef); + store_sum_deltas(&(delta[i]), &mass_oxygen_unknown->delta, + elt_list[j].coef); + + } + else + { + master_ptr = elt_list[j].elt->primary; + if (master_ptr->in == FALSE) + { + master_ptr = master_ptr->s->secondary; + } + if (master_ptr == NULL || master_ptr->in == FALSE) + { + if (state != ADVECTION && state != TRANSPORT + && state != PHAST) + { + error_string = sformatf( + "Element in phase, %s, is not in model.", + x[i]->phase->name); + warning_msg(error_string); + } + if (master_ptr != NULL) + { + master_ptr->s->la = -999.9; + } +/* + * Master species is in model + */ + } + else if (master_ptr->in == TRUE) + { + store_jacob0(master_ptr->unknown->number, x[i]->number, + -elt_list[j].coef); + store_sum_deltas(&delta[i], &master_ptr->unknown->delta, + elt_list[j].coef); +/* + * Master species in equation needs to be rewritten + */ + } + else if (master_ptr->in == REWRITE) + { + stop = FALSE; + for (int k = 0; k < count_unknowns; k++) + { + if (x[k]->type != MB) + continue; + for (int l = 0; x[k]->master[l] != NULL; l++) + { + if (x[k]->master[l] == master_ptr) + { + store_jacob0(x[k]->master[0]->unknown-> + number, x[i]->number, + -elt_list[j].coef); + store_sum_deltas(&delta[i], + &x[k]->master[0]->unknown-> + delta, elt_list[j].coef); + stop = TRUE; + break; + } + } + if (stop == TRUE) + break; + } + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_jacobian_sums(int k) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function builds lists sum_jacob1 and sum_jacob2 that describe how to sum molalities + * to form jacobian. + */ + int i, j, kk; + int count_g; + LDBLE coef; + LDBLE *source, *target; + + if (debug_prep == TRUE) + output_msg(sformatf( "\n\tJacobian summations.\n")); +/* + * Calculate jacobian coefficients for each mass balance equation + */ + for (i = 0; i < count_mb_unknowns; i++) + { +/* + * Store d(moles) for a mass balance equation + */ + /* initial solution only */ + if (mb_unknowns[i].unknown->type == SOLUTION_PHASE_BOUNDARY) + { + continue; + } + coef = mb_unknowns[i].coef; + if (debug_prep == TRUE) + output_msg(sformatf( "\n\tMass balance eq: %s\t%f\n", + mb_unknowns[i].unknown->description, (double) coef)); + store_dn(k, mb_unknowns[i].source, mb_unknowns[i].unknown->number, + coef, mb_unknowns[i].gamma_source); +/* + * Add extra terms for change in dg/dx in diffuse layer model + */ + if (s[k]->type >= H2O || dl_type_x == cxxSurface::NO_DL) + { + continue; + } + else if ((mb_unknowns[i].unknown->type == MB || + mb_unknowns[i].unknown->type == MH || + mb_unknowns[i].unknown->type == MH2O) && state >= REACTION) + { + if (mass_oxygen_unknown != NULL) + { + /* term for water, sum of all surfaces */ + source = &s[k]->tot_dh2o_moles; + target = + &(array + [mb_unknowns[i].unknown->number * (count_unknowns + 1) + + mass_oxygen_unknown->number]); + store_jacob(source, target, coef); + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d\n", + "sum[dn(i,s)/dlnwater]", (double) coef, + mb_unknowns[i].unknown->number, + mass_oxygen_unknown->number)); + } + } + + /* terms for psi, one for each surface */ + count_g = 0; + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + source = s_diff_layer[k][charge_ptr->Get_name()].Get_dx_moles_address(); + target = &(array[mb_unknowns[i].unknown->number * + (count_unknowns + 1) + x[j]->number]); + store_jacob(source, target, coef); + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d\n", + "dg/dlny", (double) coef, + mb_unknowns[i].unknown->number, x[j]->number)); + } + count_g++; + if (count_g >= (int) use.Get_surface_ptr()->Get_surface_charges().size()) + break; + } + + /* terms for related phases */ + count_g = 0; + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + /* has related phase */ + cxxSurfaceComp *comp_ptr = use.Get_surface_ptr()->Find_comp(x[j - 1]->surface_comp); + if (comp_ptr->Get_phase_name().size() == 0) + continue; + + /* now find the related phase */ + for (kk = count_unknowns - 1; kk >= 0; kk--) + { + if (x[kk]->type != PP) + continue; + //if (x[kk]->phase->name == string_hsave(comp_ptr->Get_phase_name().c_str())) + if (strcmp_nocase(x[kk]->phase->name, comp_ptr->Get_phase_name().c_str()) == 0) + break; + } + + if (kk >= 0) + { + source = s_diff_layer[k][charge_ptr->Get_name()].Get_drelated_moles_address(); + target = &(array[mb_unknowns[i].unknown->number * + (count_unknowns + 1) + x[kk]->number]); + store_jacob(source, target, coef); + if (debug_prep == TRUE) + { + output_msg(sformatf( + "\t\t%-24s%10.3f\t%d\t%d\n", "dphase", + (double) coef, + mb_unknowns[i].unknown->number, + x[kk]->number)); + } + } + count_g++; + if (count_g >= (int) use.Get_surface_ptr()->Get_surface_charges().size()) + break; + } + + } + else if (mb_unknowns[i].unknown->type == SURFACE_CB) + { + count_g = 0; + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE_CB) + continue; + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + if (mb_unknowns[i].unknown->number == x[j]->number) + { + source = s_diff_layer[k][charge_ptr->Get_name()].Get_dx_moles_address(); + target = &(array[mb_unknowns[i].unknown->number * + (count_unknowns + 1) + x[j]->number]); + store_jacob(source, target, coef); + if (debug_prep == TRUE) + { + output_msg(sformatf( + "\t\t%-24s%10.3f\t%d\t%d\n", "dg/dlny", + (double) coef, + mb_unknowns[i].unknown->number, + x[j]->number)); + } + + /* term for related phase */ + /* has related phase */ + cxxSurfaceComp *comp_ptr = use.Get_surface_ptr()->Find_comp(x[j - 1]->surface_comp); + if (comp_ptr->Get_phase_name().size() > 0) + { + /* now find the related phase */ + for (kk = count_unknowns - 1; kk >= 0; kk--) + { + if (x[kk]->type != PP) + continue; + //if (x[kk]->phase->name == string_hsave(comp_ptr->Get_phase_name().c_str())) + if (strcmp_nocase(x[kk]->phase->name, comp_ptr->Get_phase_name().c_str()) == 0) + break; + } + if (kk >= 0) + { + source = s_diff_layer[k][charge_ptr->Get_name()].Get_drelated_moles_address(); + target = &(array[mb_unknowns[i].unknown->number * + (count_unknowns + 1) + x[kk]->number]); + store_jacob(source, target, coef); + if (debug_prep == TRUE) + { + output_msg(sformatf( + "\t\t%-24s%10.3f\t%d\t%d\n", + "dphase", (double) coef, + mb_unknowns[i].unknown->number, + x[kk]->number)); + } + } + } + + if (mass_oxygen_unknown != NULL) + { + /* term for water, for same surfaces */ + source = s_diff_layer[k][charge_ptr->Get_name()].Get_dh2o_moles_address(); + target = &(array[mb_unknowns[i].unknown->number * + (count_unknowns + 1) + + mass_oxygen_unknown->number]); + store_jacob(source, target, coef); + if (debug_prep == TRUE) + { + output_msg(sformatf( + "\t\t%-24s%10.3f\t%d\t%d\n", + "dn(i,s)/dlnwater", (double) coef, + mb_unknowns[i].unknown->number, + mass_oxygen_unknown->number)); + } + } + break; + } + count_g++; + if (count_g >= (int) use.Get_surface_ptr()->Get_surface_charges().size()) + break; + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_mb_sums(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function builds lists sum_mb1 and sum_mb2 that describe how to sum molalities + * to calculate mass balance sums, including activity of water, ionic strength, + * charge balance, and alkalinity. + */ + int i; + LDBLE *target; +/* + * Make space for lists + */ + if (count_sum_mb1 + count_mb_unknowns >= max_sum_mb1) + { + space((void **) ((void *) &sum_mb1), + count_sum_mb1 + count_mb_unknowns, &max_sum_mb1, + sizeof(struct list1)); + } + if (count_sum_mb2 + count_mb_unknowns >= max_sum_mb2) + { + space((void **) ((void *) &sum_mb2), + count_sum_mb2 + count_mb_unknowns, &max_sum_mb2, + sizeof(struct list2)); + } + + if (debug_prep == TRUE) + { + output_msg(sformatf( "\n\tMass balance summations.\n\n")); + } + for (i = 0; i < count_mb_unknowns; i++) + { + target = &(mb_unknowns[i].unknown->f); + store_mb(mb_unknowns[i].source, target, mb_unknowns[i].coef); + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\n", + mb_unknowns[i].unknown->description, + (double) mb_unknowns[i].coef)); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_model(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Guts of prep. Determines species in model, rewrites equations, + * builds lists for mass balance and jacobian sums. + */ + int i, j, j0, k; + LDBLE coef_e; + + if (s_hplus == NULL || s_eminus == NULL || s_h2o == NULL) + { + error_msg("Data base is missing H+, H2O, or e- species.", CONTINUE); + input_error++; + } +/* + * Make space for lists of pointers to species in the model + */ + + max_s_x = MAX_S; + + // clear sum_species_map, which is built from s_x + sum_species_map_db.clear(); + sum_species_map.clear(); + + space((void **) ((void *) &s_x), INIT, &max_s_x, + sizeof(struct species *)); + + max_sum_mb1 = MAX_SUM_MB; + count_sum_mb1 = 0; + space((void **) ((void *) &sum_mb1), INIT, &max_sum_mb1, + sizeof(struct list1)); + + max_sum_mb2 = MAX_SUM_MB; + count_sum_mb2 = 0; + space((void **) ((void *) &sum_mb2), INIT, &max_sum_mb2, + sizeof(struct list2)); + + max_sum_jacob0 = MAX_SUM_JACOB0; + count_sum_jacob0 = 0; + space((void **) ((void *) &sum_jacob0), INIT, &max_sum_jacob0, + sizeof(struct list0)); + + max_sum_jacob1 = MAX_SUM_JACOB1; + count_sum_jacob1 = 0; + space((void **) ((void *) &sum_jacob1), INIT, &max_sum_jacob1, + sizeof(struct list1)); + + max_sum_jacob2 = MAX_SUM_JACOB2; + count_sum_jacob2 = 0; + space((void **) ((void *) &sum_jacob2), INIT, &max_sum_jacob2, + sizeof(struct list2)); + + + max_sum_delta = MAX_SUM_JACOB0; + count_sum_delta = 0; + space((void **) ((void *) &sum_delta), INIT, &max_sum_delta, + sizeof(struct list2)); + + max_species_list = 5 * MAX_S; + count_species_list = 0; + species_list = (struct species_list *) free_check_null(species_list); + space((void **) ((void *) &species_list), INIT, &max_species_list, + sizeof(struct species_list)); + +/* + * Pick species in the model, determine reaction for model, build jacobian + */ + count_s_x = 0; + compute_gfw("H2O", &gfw_water); + gfw_water *= 0.001; + for (i = 0; i < count_s; i++) + { + if (s[i]->type > H2O && s[i]->type != EX && s[i]->type != SURF) + continue; + s[i]->in = FALSE; + count_trxn = 0; + trxn_add(s[i]->rxn_s, 1.0, FALSE); /* rxn_s is set in tidy_model */ +/* + * Check if species is in model + */ + s[i]->in = inout(); + if (s[i]->in == TRUE) + { + /* for isotopes, activity of water is for 1H and 16O */ + if (s[i]->gflag == 9) + { + gfw_water = 18.0 / 1000.0; + } + if (pitzer_model == FALSE && sit_model == FALSE) + s[i]->lg = 0.0; + if (count_s_x + 1 >= max_s_x) + { + space((void **) ((void *) &s_x), count_s_x + 1, + &max_s_x, sizeof(struct species *)); + } + compute_gfw(s[i]->name, &s[i]->gfw); + s_x[count_s_x++] = s[i]; + +/* + * Write mass action equation for current model + */ + //if (write_mass_action_eqn_x(STOP) == ERROR) continue; + write_mass_action_eqn_x(STOP); + if (s[i]->type == SURF) + { + add_potential_factor(); + add_cd_music_factors(i); + } + rxn_free(s[i]->rxn_x); + s[i]->rxn_x = rxn_alloc(count_trxn + 1); + trxn_copy(s[i]->rxn_x); + for (j = 0; j < 3; j++) + { + s[i]->dz[j] = s[i]->rxn_x->dz[j]; + } + if (debug_prep == TRUE) + { + output_msg(sformatf( "\n%s\n\tMass-action equation\n", + s[i]->name)); + trxn_print(); + } +/* + * Determine mass balance equations, build sums for mass balance, build sums for jacobian + */ + count_trxn = 0; + trxn_add(s[i]->rxn_s, 1.0, FALSE); + if (s[i]->next_secondary == NULL) + { + write_mb_eqn_x(); + } + else + { + count_elts = 0; + add_elt_list(s[i]->next_secondary, 1.0); + } + if (s[i]->type == SURF) + { + add_potential_factor(); + add_cd_music_factors(i); + add_surface_charge_balance(); + add_cd_music_charge_balances(i); + } + if (debug_prep == TRUE) + { + output_msg(sformatf( "\tElement composition %s\n", + trxn.token[0].s->name)); + for (j = 0; j < count_elts; j++) + { + output_msg(sformatf( "\t\t%-20s\t%10.2f\n", + elt_list[j].elt->name, + (double) elt_list[j].coef)); + } + } + if (debug_prep == TRUE) + { + output_msg(sformatf( "\n\tMass balance equation\n", + s[i]->name)); + trxn_print(); + } + if (s[i]->type < EMINUS) + { + mb_for_species_aq(i); + } + else if (s[i]->type == EX) + { + mb_for_species_ex(i); + } + else if (s[i]->type == SURF) + { + mb_for_species_surf(i); + } +#ifdef COMBINE + build_mb_sums(); +#else + if (s[i] != s_h2o) + { + build_mb_sums(); + } +#endif + if (!pitzer_model && !sit_model) + build_jacobian_sums(i); +/* + * Build list of species for summing and printing + */ + if (s[i]->next_secondary == NULL) + { + write_mb_for_species_list(i); + } + else + { + count_elts = 0; + add_elt_list(s[i]->next_secondary, 1.0); + } + build_species_list(i); + } + } + if (dl_type_x != cxxSurface::NO_DL && (pitzer_model == TRUE || sit_model == TRUE)) + { + error_msg("-diffuse_layer option not available for Pizer or SIT model", + STOP); + } +/* + * Sum diffuse layer water into hydrogen and oxygen mass balances + */ + if (dl_type_x != cxxSurface::NO_DL && state >= REACTION) + { + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == SURFACE_CB) + { +#ifndef COMBINE + store_mb(&(x[i]->mass_water), + &(mass_hydrogen_unknwon->f), 2 / gfw_water); +#endif + if (mass_oxygen_unknown != NULL) + { + store_mb(&(x[i]->mass_water), + &(mass_oxygen_unknown->f), 1 / gfw_water); + } + } + } + } +/* + * For Pizer model add lg unknown for each aqueous species + */ + + if (pitzer_model == TRUE || sit_model == TRUE) + { + j0 = count_unknowns; + j = count_unknowns + count_s_x; + k = j0; + for (i = j0; i < j; i++) + { + if (s_x[i - j0]->type == EX) + continue; + if (s_x[i - j0]->type == SURF) + continue; + x[k]->number = k; + x[k]->type = PITZER_GAMMA; + x[k]->s = s_x[i - j0]; + x[k]->description = s_x[i - j0]->name; + k++; + count_unknowns++; + } + } +/* + * Rewrite phases to current master species + */ + for (i = 0; i < count_phases; i++) + { + count_trxn = 0; + trxn_add_phase(phases[i]->rxn_s, 1.0, FALSE); + trxn_reverse_k(); + phases[i]->in = inout(); + if (phases[i]->in == TRUE) + { +/* + * Replace e- in original equation with default redox reaction + */ + coef_e = trxn_find_coef("e-", 1); + if (equal(coef_e, 0.0, TOL) == FALSE) + { + trxn_add(pe_x[default_pe_x.c_str()], coef_e, TRUE); + } +/* + * Rewrite reaction to current master species + */ + write_mass_action_eqn_x(STOP); + trxn_reverse_k(); + rxn_free(phases[i]->rxn_x); + if (debug_prep == TRUE) + { + output_msg(sformatf( "\nPhase: %s\n", phases[i]->name)); + trxn_print(); + } + phases[i]->rxn_x = rxn_alloc(count_trxn + 1); + trxn_copy(phases[i]->rxn_x); + write_phase_sys_total(i); + } + } + build_solution_phase_boundaries(); + build_pure_phases(); + build_min_exch(); + build_min_surface(); + build_gas_phase(); + build_ss_assemblage(); +/* + * Sort species list, by master only + */ + qsort(&species_list[0], (size_t) count_species_list, + (size_t) sizeof(struct species_list), species_list_compare_master); +/* + * Save model description + */ + save_model(); + + if (input_error > 0) + { + error_msg("Stopping due to input errors.", STOP); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_pure_phases(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Includes calculation of inverse saturation index in sum_mb. + * Puts coefficients in iap and mass balance equations for each phase. + */ + bool stop; + std::string token; + char *ptr; + struct master *master_ptr; + struct rxn_token *rxn_ptr; +/* + * Build into sums the logic to calculate inverse saturation indices for + * pure phases + */ + if (pure_phase_unknown == NULL) + return (OK); + +/* + * Calculate inverse saturation index + */ + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type != PP || x[i]->phase->rxn_x == NULL) + continue; + if (pure_phase_unknown == NULL) + pure_phase_unknown = x[i]; + + store_mb(&(x[i]->phase->lk), &(x[i]->f), 1.0); + store_mb(&(x[i]->si), &(x[i]->f), 1.0); + + for (rxn_ptr = x[i]->phase->rxn_x->token + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + store_mb(&(rxn_ptr->s->la), &(x[i]->f), -rxn_ptr->coef); + } + } + for (int i = 0; i < count_unknowns; i++) + { +/* + * rxn_x is null if an element in phase is not in solution + */ + if (x[i]->type != PP || x[i]->phase->rxn_x == NULL) + continue; +/* + * Put coefficients into IAP equations + */ + for (rxn_ptr = x[i]->phase->rxn_x->token + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + if (rxn_ptr->s->secondary != NULL + && rxn_ptr->s->secondary->in == TRUE) + { + master_ptr = rxn_ptr->s->secondary; + } + else + { + master_ptr = rxn_ptr->s->primary; + } + if (master_ptr == NULL || master_ptr->unknown == NULL) + continue; + store_jacob0(x[i]->number, master_ptr->unknown->number, + rxn_ptr->coef); + } +/* + * Put coefficients into mass balance equations + */ + count_elts = 0; + paren_count = 0; + //cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr; + if (comp_ptr->Get_add_formula().size() > 0) + { + char * char_name = string_duplicate(comp_ptr->Get_add_formula().c_str()); + ptr = char_name; + get_elts_in_species(&ptr, 1.0); + free_check_null(char_name); + } + else + { + char * char_name = string_duplicate(x[i]->phase->formula); + ptr = char_name; + get_elts_in_species(&ptr, 1.0); + free_check_null(char_name); + } +/* + * Go through elements in phase + */ + +#ifdef COMBINE + change_hydrogen_in_elt_list(0); +#endif + for (int j = 0; j < count_elts; j++) + { + + if (strcmp(elt_list[j].elt->name, "H") == 0 + && mass_hydrogen_unknown != NULL) + { + store_jacob0(mass_hydrogen_unknown->number, x[i]->number, + -elt_list[j].coef); + store_sum_deltas(&(delta[i]), &mass_hydrogen_unknown->delta, + elt_list[j].coef); + + } + else if (strcmp(elt_list[j].elt->name, "O") == 0 + && mass_oxygen_unknown != NULL) + { + store_jacob0(mass_oxygen_unknown->number, x[i]->number, + -elt_list[j].coef); + store_sum_deltas(&(delta[i]), &mass_oxygen_unknown->delta, + elt_list[j].coef); + + } + else + { + master_ptr = elt_list[j].elt->primary; + if (master_ptr == NULL) + { + error_string = sformatf( + "Element undefined, %s.", + elt_list[j].elt->name); + error_msg(error_string, STOP); + } + if (master_ptr->in == FALSE) + { + master_ptr = master_ptr->s->secondary; + } + if (master_ptr == NULL || master_ptr->in == FALSE) + { + if (state != ADVECTION && state != TRANSPORT + && state != PHAST) + { + error_string = sformatf( + "Element in phase, %s, is not in model.", + x[i]->phase->name); + warning_msg(error_string); + } + if (master_ptr != NULL) + { + master_ptr->s->la = -999.9; + } +/* + * Master species is in model + */ + } + else if (master_ptr->in == TRUE) + { + store_jacob0(master_ptr->unknown->number, x[i]->number, + -elt_list[j].coef); + store_sum_deltas(&delta[i], &master_ptr->unknown->delta, + elt_list[j].coef); +/* + * Master species in equation needs to be rewritten + */ + } + else if (master_ptr->in == REWRITE) + { + stop = false; + for (int k = 0; k < count_unknowns; k++) + { + if (x[k]->type != MB) + continue; + for (int l = 0; x[k]->master[l] != NULL; l++) + { + if (x[k]->master[l] == master_ptr) + { + store_jacob0(x[k]->master[0]->unknown-> + number, x[i]->number, + -elt_list[j].coef); + store_sum_deltas(&delta[i], + &x[k]->master[0]->unknown-> + delta, elt_list[j].coef); + stop = TRUE; + break; + } + } + if (stop == TRUE) + break; + } + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_solution_phase_boundaries(void) +/* ---------------------------------------------------------------------- */ +{ + int i; + struct master *master_ptr; + struct rxn_token *rxn_ptr; +/* + * Build into sums the logic to calculate inverse saturation indices for + * solution phase boundaries + */ + if (solution_phase_boundary_unknown == NULL) + return (OK); +/* + * Calculate inverse saturation index + */ + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type != SOLUTION_PHASE_BOUNDARY) + continue; + + store_mb(&(x[i]->phase->lk), &(x[i]->f), 1.0); + store_mb(&(x[i]->si), &(x[i]->f), 1.0); + if (x[i]->phase->in != TRUE) + { + error_string = sformatf( + "Solution does not contain all elements for phase-boundary mineral, %s.", + x[i]->phase->name); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + for (rxn_ptr = x[i]->phase->rxn_x->token + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + store_mb(&(rxn_ptr->s->la), &(x[i]->f), -rxn_ptr->coef); + } + } + if (get_input_errors() > 0) + return (ERROR); +/* + * Put coefficients into array + */ + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type != SOLUTION_PHASE_BOUNDARY) + continue; + for (rxn_ptr = x[i]->phase->rxn_x->token + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + if (rxn_ptr->s->secondary != NULL + && rxn_ptr->s->secondary->in == TRUE) + { + master_ptr = rxn_ptr->s->secondary; + } + else + { + master_ptr = rxn_ptr->s->primary; + } + if (master_ptr->unknown == NULL) + continue; + store_jacob0(x[i]->number, master_ptr->unknown->number, + rxn_ptr->coef); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_species_list(int n) +/* ---------------------------------------------------------------------- */ +{ +/* + * Builds a list that includes an entry for each master species in each + * secondary reaction. Used for summing species of each element and + * printing results. + */ + int j; + struct master *master_ptr; +/* + * Check space and store reaction token name and pointer to species + */ + if (count_species_list + count_elts >= max_species_list) + { + space((void **) ((void *) &species_list), + count_species_list + count_elts, &max_species_list, + sizeof(struct species_list)); + } +/* + * Treat species made only with H+, e-, and H2O specially + */ + if (is_special(s[n]) == TRUE) + { + species_list[count_species_list].master_s = s_hplus; + species_list[count_species_list].s = s[n]; + species_list[count_species_list].coef = 0.0; + count_species_list++; + return (OK); + } +/* + * Treat exchange species specially + */ + if (s[n]->type == EX) + { + if (s[n]->primary != NULL) + return (OK); /* master species has zero molality */ + for (j = 0; j < count_elts; j++) + { + if (elt_list[j].elt->master->s->type != EX) + continue; + master_ptr = elt_list[j].elt->master; + species_list[count_species_list].master_s = + elt_list[j].elt->master->s; + species_list[count_species_list].s = s[n]; + species_list[count_species_list].coef = master_ptr->coef * + elt_list[j].coef; + count_species_list++; + } + return (OK); + } +/* + * Treat surface species specially + */ + if (s[n]->type == SURF_PSI) + return (OK); + if (s[n]->type == SURF) + { + for (j = 0; j < count_elts; j++) + { + if (elt_list[j].elt->master->s->type != SURF) + continue; + master_ptr = elt_list[j].elt->master; + species_list[count_species_list].master_s = + elt_list[j].elt->master->s; + species_list[count_species_list].s = s[n]; + species_list[count_species_list].coef = master_ptr->coef * + elt_list[j].coef; + count_species_list++; + } + return (OK); + } +/* + * Other aqueous species + */ + for (j = 0; j < count_elts; j++) + { + if (is_special(elt_list[j].elt->master->s) == TRUE) + continue; + if (elt_list[j].elt->master->s->secondary != NULL) + { + master_ptr = elt_list[j].elt->master->s->secondary; + } + else + { + master_ptr = elt_list[j].elt->master->s->primary; + } + species_list[count_species_list].master_s = master_ptr->s; + species_list[count_species_list].s = s[n]; +/* + * Find coefficient for element represented by master species + */ + species_list[count_species_list].coef = master_ptr->coef * + elt_list[j].coef; + count_species_list++; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +clear(void) +/* ---------------------------------------------------------------------- */ +{ + int i; +/* + * Resets information for setting up a new model + */ + cxxSolution *solution_ptr; +/* + * Clear species solution-dependent data + */ + solution_ptr = use.Get_solution_ptr(); + + for (i = 0; i < count_s; i++) + { + s[i]->in = FALSE; + } +/* + * Set pe structure + */ + pe_x.clear(); + default_pe_x.clear(); + if (solution_ptr->Get_initial_data()) + { + pe_x = solution_ptr->Get_initial_data()->Get_pe_reactions(); + default_pe_x = solution_ptr->Get_initial_data()->Get_default_pe(); + } + else + { + default_pe_x = "pe"; + cxxChemRxn chem_rxn; + pe_x[default_pe_x] = chem_rxn; + } + +/* + * Clear master species solution-dependent data + */ + const char * pe_str = string_hsave("pe"); + for (i = 0; i < count_master; i++) + { + master[i]->in = FALSE; + master[i]->unknown = NULL; + if (solution_ptr->Get_initial_data()) + { + master[i]->pe_rxn = solution_ptr->Get_initial_data()->Get_default_pe(); + } + else + { + master[i]->pe_rxn = pe_str; + } +/* + * copy primary reaction to secondary reaction + */ + rxn_free(master[i]->rxn_secondary); + master[i]->rxn_secondary = rxn_dup(master[i]->rxn_primary); + } + + if (state == INITIAL_SOLUTION) + { + s_h2o->secondary->in = TRUE; + s_hplus->secondary->in = TRUE; + } + else + { + s_h2o->primary->in = TRUE; + s_hplus->primary->in = TRUE; + } + s_eminus->primary->in = TRUE; +/* + * Set all unknown pointers to NULL + */ + mb_unknown = NULL; + ah2o_unknown = NULL; + mass_hydrogen_unknown = NULL; + mass_oxygen_unknown = NULL; + mu_unknown = NULL; + alkalinity_unknown = NULL; + carbon_unknown = NULL; + ph_unknown = NULL; + pe_unknown = NULL; + charge_balance_unknown = NULL; + solution_phase_boundary_unknown = NULL; + pure_phase_unknown = NULL; + exchange_unknown = NULL; + surface_unknown = NULL; + gas_unknown = NULL; + ss_unknown = NULL; +/* + * Free arrays used in model + */ + free_model_allocs(); +#ifdef SKIP + // Bug-fix + // The standard implementation of clear() sets the unknown pointer of some of the + // masters to NULL. However, the function quick_setup presumes that a master pointer + // is valid when the masters total is larger than zero. This results in a crash + // when the unknown pointer is dereferenced. The same goes for the secondary master + // species. + // + // Perhaps this should be part of the 'Clear master species solution-dependent data'-loop above?! + for ( int i = 0; i < count_master; i++ ) + { + if (master[i]->s->type == SURF_PSI) + continue; + + if ( master[i]->s == s_eminus || + master[i]->s == s_hplus || + master[i]->s == s_h2o || + master[i]->s == s_h2 || + master[i]->s == s_o2 ) + continue; + + if (master[i]->total > 0 ) + { + // Make sure masters total is set to zero when unknown pointer for master species is not set + if ( ( master[i]->s->secondary && !master[i]->s->secondary->unknown ) || !master[i]->unknown ) + { + master[i]->total = 0.0; + } + } + } +#endif + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +convert_units(cxxSolution *solution_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Converts solution concentrations to moles/kg water + * Uses totals.input conc to calculate totals.moles. + */ + LDBLE sum_solutes; + struct master *master_ptr; + std::string token; + if (!solution_ptr->Get_new_def() || !solution_ptr->Get_initial_data()) + { + input_error++; + error_msg("Missing data for convert_units", 1); + } +/* + * Convert units + */ + sum_solutes = exp(-solution_ptr->Get_ph() * LOG_10); + cxxISolution *initial_data_ptr = solution_ptr->Get_initial_data(); + std::map::iterator jit = initial_data_ptr->Get_comps().begin(); + for ( ; jit != initial_data_ptr->Get_comps().end(); jit++) + { + cxxISolutionComp &comp_ref = jit->second; + LDBLE moles; + master_ptr = master_bsearch(comp_ref.Get_description().c_str()); + if (master_ptr != NULL) + { + if (master_ptr->minor_isotope == TRUE) + continue; + } + + // initially store 0.0 for totals + solution_ptr->Get_totals()[comp_ref.Get_description()] = 0.0; + + if (strcmp(comp_ref.Get_description().c_str(), "H(1)") == 0 || + strcmp(comp_ref.Get_description().c_str(), "E") == 0) + { + continue; + } + if (comp_ref.Get_input_conc() <= 0) + continue; +/* + * Get gfw + */ + /* use given gfw if gfw > 0.0 */ + /* use formula give with "as" */ + if (comp_ref.Get_gfw() <= 0.0) + { + if (comp_ref.Get_as().size() > 0) + { + /* use given chemical formula to calculate gfw */ + if (compute_gfw(comp_ref.Get_as().c_str(), &dummy) == ERROR) + { + error_string = sformatf( "Could not compute gfw, %s.", + comp_ref.Get_as().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + else + { + comp_ref.Set_gfw(dummy); + } + if (strcmp(comp_ref.Get_description().c_str(), "Alkalinity") == 0 && + strcmp(comp_ref.Get_as().c_str(), "CaCO3") == 0) + { + comp_ref.Set_gfw(comp_ref.Get_gfw() / 2.0); + error_string = sformatf( + "Equivalent wt for alkalinity should be Ca.5(CO3).5. Using %g g/eq.", + (double) comp_ref.Get_gfw()); + warning_msg(error_string); + } + /* use gfw of master species */ + } + else + { + char * temp_desc = string_duplicate(comp_ref.Get_description().c_str()); + char *ptr = temp_desc; + copy_token(token, &ptr); + master_ptr = master_bsearch(token.c_str()); + free_check_null(temp_desc); + if (master_ptr != NULL) + { + /* use gfw for element redox state */ + comp_ref.Set_gfw(master_ptr->gfw); + } + else + { + error_string = sformatf( "Could not find gfw, %s.", + comp_ref.Get_description().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + continue; + } + } + } +/* + * Convert liters to kg solution + */ + moles = comp_ref.Get_input_conc(); + if (strstr(initial_data_ptr->Get_units().c_str(), "/l") != NULL) + { + moles *= 1.0 / (solution_ptr->Get_density()); + } +/* + * Convert milli or micro + */ + char c = comp_ref.Get_units()[0]; + if (c == 'm') + { + moles *= 1e-3; + } + else if (c == 'u') + { + moles *= 1e-6; + } +/* + * Sum grams of solute, convert from moles necessary + */ + if (strstr(comp_ref.Get_units().c_str(), "g/kgs") != NULL || + strstr(comp_ref.Get_units().c_str(), "g/l") != NULL) + { + sum_solutes += moles; + } + else if (strstr(comp_ref.Get_units().c_str(), "Mol/kgs") != NULL || + strstr(comp_ref.Get_units().c_str(), "Mol/l") != NULL || + strstr(comp_ref.Get_units().c_str(), "eq/l") != NULL) + { + sum_solutes += moles * comp_ref.Get_gfw(); + } +/* + * Convert grams to moles, if necessary + */ + if (strstr(comp_ref.Get_units().c_str(), "g/") != NULL && comp_ref.Get_gfw() != 0.0) + { + moles /= comp_ref.Get_gfw(); + } + solution_ptr->Get_totals()[comp_ref.Get_description()] = moles; + } +/* + * Convert /kgs to /kgw + */ + if (strstr(initial_data_ptr->Get_units().c_str(), "kgs") != NULL || + strstr(initial_data_ptr->Get_units().c_str(), "/l") != NULL) + { + mass_water_aq_x = 1.0 - 1e-3 * sum_solutes; + if (mass_water_aq_x <= 0) + { + error_string = sformatf( "Solute mass exceeds solution mass in conversion from /kgs to /kgw.\n" + "Mass of water is negative."); + error_msg(error_string, CONTINUE); + input_error++; + } + cxxNameDouble::iterator it; + for (it = solution_ptr->Get_totals().begin(); it != solution_ptr->Get_totals().end(); it++) + { + it->second = it->second / mass_water_aq_x; + } + } +/* + * Scale by mass of water in solution + */ + mass_water_aq_x = solution_ptr->Get_mass_water(); + cxxNameDouble::iterator it; + for (it = solution_ptr->Get_totals().begin(); it != solution_ptr->Get_totals().end(); it++) + { + it->second = it->second * mass_water_aq_x; + } + + initial_data_ptr->Set_units(moles_per_kilogram_string); + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +struct master ** Phreeqc:: +get_list_master_ptrs(char *ptr, struct master *master_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Input: ptr contains a list of one or more master species names + * Output: space is allocated and a list of master species pointers is + * returned. + */ + int j, l, count_list; + char token[MAX_LENGTH]; + struct master **master_ptr_list; + struct master *master_ptr0; +/* + * Make list of master species pointers + */ + count_list = 0; + master_ptr_list = unknown_alloc_master(); + master_ptr0 = master_ptr; + if (master_ptr0 == master_ptr->s->primary) + { +/* + * First in list is primary species + */ + for (j = 0; j < count_master; j++) + { + if (master[j] == master_ptr0) + break; + } + j++; +/* + * Element has only one valence + */ + if (j >= count_master || master[j]->elt->primary != master_ptr0) + { + master_ptr_list[count_list++] = master_ptr0; +/* + * Element has multiple valences + */ + } + else + { + if (master_ptr0->s->secondary == NULL) + { + error_string = sformatf( + "Master species for valence states of element %s are not correct.\n\tPossibly related to master species for %s.", + master_ptr0->elt->name, master[j]->elt->name); + error_msg(error_string, CONTINUE); + input_error++; + } + master_ptr_list[count_list++] = master_ptr0->s->secondary; + while (j < count_master && master[j]->elt->primary == master_ptr0) + { + if (master[j]->s->primary == NULL) + { + master_ptr_list = + (struct master **) PHRQ_realloc((void *) + master_ptr_list, + (size_t) (count_list + + + 2) * + sizeof(struct master + *)); + if (master_ptr_list == NULL) + malloc_error(); + master_ptr_list[count_list++] = master[j]; + } + j++; + } + } + } + else + { +/* + * First in list is secondary species, Include all valences from input + */ + master_ptr_list[count_list++] = master_ptr0; + while (copy_token(token, &ptr, &l) != EMPTY) + { + master_ptr = master_bsearch(token); + if (master_ptr != NULL) + { + master_ptr_list = + (struct master **) PHRQ_realloc((void *) master_ptr_list, + (size_t) (count_list + + 2) * + sizeof(struct master *)); + if (master_ptr_list == NULL) + malloc_error(); + master_ptr_list[count_list++] = master_ptr; + } + } + } + master_ptr_list[count_list] = NULL; + return (master_ptr_list); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +inout(void) +/* ---------------------------------------------------------------------- */ +{ + int i; + struct rxn_token_temp *token_ptr; +/* + * Routine goes through trxn to determine if each master species is + * in this model. + * Assumes equation is written in terms of primary and secondary species + * Checks to see if in is TRUE or REWRITE for each species + * Returns TRUE if in model + * FALSE if not + */ + for (i = 1; i < count_trxn; i++) + { + token_ptr = &(trxn.token[i]); + /* Check primary master species in */ + if (token_ptr->s->primary != NULL + && (token_ptr->s->primary->in == TRUE)) + continue; + /* Check secondary master species */ + if ((token_ptr->s->secondary != NULL) + && (token_ptr->s->secondary->in != FALSE)) + { + continue; + } + /* Must be primary master species that is out */ + return (FALSE); + } + return (TRUE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +is_special(struct species *l_spec) +/* ---------------------------------------------------------------------- */ +{ +/* + * Checks to see if a species is composed of only H, O, and e- + * Returns TRUE if true + * FALSE if not + */ + int special; + struct rxn_token *token_ptr; + + special = TRUE; + for (token_ptr = l_spec->rxn_s->token + 1; token_ptr->s != NULL; + token_ptr++) + { + if (token_ptr->s != s_hplus && + token_ptr->s != s_h2o && token_ptr->s != s_eminus) + { + special = FALSE; + break; + } + } + return (special); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +store_mb_unknowns(struct unknown *unknown_ptr, LDBLE * LDBLE_ptr, LDBLE coef, + LDBLE * gamma_ptr) +/* ---------------------------------------------------------------------- */ +/* + * Takes an unknown pointer and a coefficient and puts in + * list of mb_unknowns + */ +{ + if (equal(coef, 0.0, TOL) == TRUE) + return (OK); + if ((count_mb_unknowns + 1) >= max_mb_unknowns) + { + space((void **) ((void *) &mb_unknowns), count_mb_unknowns + 1, + &max_mb_unknowns, sizeof(struct unknown_list)); + } + mb_unknowns[count_mb_unknowns].unknown = unknown_ptr; + mb_unknowns[count_mb_unknowns].source = LDBLE_ptr; + mb_unknowns[count_mb_unknowns].gamma_source = gamma_ptr; + mb_unknowns[count_mb_unknowns].coef = coef; + count_mb_unknowns++; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +mb_for_species_aq(int n) +/* ---------------------------------------------------------------------- */ +{ +/* + * Make list of mass balance and charge balance equations in which + * to insert species n. + * + * count_mb_unknowns - number of equations and summation relations + * mb_unknowns.unknown - pointer to unknown which contains row number + * mb_unknowns.source - pointer to the LDBLE number to be multiplied + * by coef, usually moles. + * mb_unknowns.coef - coefficient of s[n] in equation or relation + */ + int i, j; + struct master *master_ptr; + struct unknown *unknown_ptr; + + count_mb_unknowns = 0; +/* + * e- does not appear in any mass balances + */ + if (s[n]->type == EMINUS) + return (OK); +/* + * Do not include diffuse layer in cb, alk, ah2o, mu + */ + if (charge_balance_unknown != NULL && s[n]->type < H2O) + { + store_mb_unknowns(charge_balance_unknown, &s[n]->moles, s[n]->z, + &s[n]->dg); + } + if (alkalinity_unknown != NULL && s[n]->type < H2O) + { + store_mb_unknowns(alkalinity_unknown, &s[n]->moles, s[n]->alk, + &s[n]->dg); + } + if (ah2o_unknown != NULL && s[n]->type < H2O) + { + store_mb_unknowns(ah2o_unknown, &s[n]->moles, 1.0, &s[n]->dg); + } + if (mu_unknown != NULL && s[n]->type < H2O) + { + store_mb_unknowns(mu_unknown, &s[n]->moles, s[n]->z * s[n]->z, + &s[n]->dg); + } +/* + * Include diffuse layer in hydrogen and oxygen mass balance + */ + if (mass_hydrogen_unknown != NULL) + { + if (dl_type_x != cxxSurface::NO_DL && state >= REACTION) + { +#ifdef COMBINE + store_mb_unknowns(mass_hydrogen_unknown, &s[n]->tot_g_moles, + s[n]->h - 2 * s[n]->o, &s[n]->dg_total_g); +#else + store_mb_unknowns(mass_hydrogen_unknown, &s[n]->tot_g_moles, + s[n]->h, &s[n]->dg_total_g); +#endif + } + else + { +#ifdef COMBINE + store_mb_unknowns(mass_hydrogen_unknown, &s[n]->moles, + s[n]->h - 2 * s[n]->o, &s[n]->dg); +#else + store_mb_unknowns(mass_hydrogen_unknown, &s[n]->moles, s[n]->h, + &s[n]->dg); +#endif + } + } + if (mass_oxygen_unknown != NULL) + { + if (dl_type_x != cxxSurface::NO_DL && state >= REACTION) + { + store_mb_unknowns(mass_oxygen_unknown, &s[n]->tot_g_moles, + s[n]->o, &s[n]->dg_total_g); + } + else + { + store_mb_unknowns(mass_oxygen_unknown, &s[n]->moles, s[n]->o, + &s[n]->dg); + } + } +/* + * Sum diffuse layer charge into (surface + DL) charge balance + */ + if (use.Get_surface_ptr() != NULL && s[n]->type < H2O && dl_type_x != cxxSurface::NO_DL) + { + j = 0; + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type == SURFACE_CB) + { + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge); + unknown_ptr = x[i]; + if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + unknown_ptr = x[i + 2]; + + store_mb_unknowns(unknown_ptr, s_diff_layer[n][charge_ptr->Get_name()].Get_g_moles_address(), + s[n]->z, s_diff_layer[n][charge_ptr->Get_name()].Get_dg_g_moles_address()); + j++; + } + } + } +/* + * Other mass balances + */ + for (i = 0; i < count_elts; i++) + { + if (elt_list[i].elt->master->s->type > AQ && + elt_list[i].elt->master->s->type < SOLID) + continue; + master_ptr = elt_list[i].elt->master; + if (master_ptr->primary == TRUE) + { + if (master_ptr->s->secondary != NULL) + { + master_ptr = master_ptr->s->secondary; + } + } + if (master_ptr->unknown == ph_unknown) + { + continue; + } + else if (master_ptr->unknown == pe_unknown) + { + continue; + } + else if (master_ptr->unknown == charge_balance_unknown) + { + continue; + } + else if (master_ptr->unknown == alkalinity_unknown) + { + continue; + } + else if (master_ptr->unknown == NULL) + { + //std::cerr << "NULL: " << master_ptr->s->name << std::endl; + continue; + } + else if (master_ptr->unknown->type == SOLUTION_PHASE_BOUNDARY) + { + continue; + } + if (dl_type_x != cxxSurface::NO_DL && state >= REACTION) + { + store_mb_unknowns(master_ptr->unknown, + &s[n]->tot_g_moles, + elt_list[i].coef * master_ptr->coef, + &s[n]->dg_total_g); + } + else + { + store_mb_unknowns(master_ptr->unknown, + &s[n]->moles, + elt_list[i].coef * master_ptr->coef, &s[n]->dg); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +mb_for_species_ex(int n) +/* ---------------------------------------------------------------------- */ +{ +/* + * Make list of mass balance and charge balance equations in which + * to insert exchange species n. + * + * count_mb_unknowns - number of equations and summation relations + * mb_unknowns.source - pointer to the LDBLE number to be multiplied + * by coef, usually moles. + * mb_unknowns.unknown - pointer to unknown which contains row number + * mb_unknowns.coef - coefficient of s[n] in equation or relation + */ + int i; + struct master *master_ptr; + count_mb_unknowns = 0; +/* + * Master species for exchange do not appear in any mass balances + */ + if (s[n]->type == EX && s[n]->primary != NULL) + return (OK); +/* + * Include diffuse layer in hydrogen and oxygen mass balance + */ + if (charge_balance_unknown != NULL) + { + store_mb_unknowns(charge_balance_unknown, &s[n]->moles, s[n]->z, + &s[n]->dg); + } + if (mass_hydrogen_unknown != NULL) + { +#ifdef COMBINE + store_mb_unknowns(mass_hydrogen_unknown, &s[n]->moles, + s[n]->h - 2 * s[n]->o, &s[n]->dg); +#else + store_mb_unknowns(mass_hydrogen_unknown, &s[n]->moles, s[n]->h, + &s[n]->dg); +#endif + } + if (mass_oxygen_unknown != NULL) + { + store_mb_unknowns(mass_oxygen_unknown, &s[n]->moles, s[n]->o, + &s[n]->dg); + } +/* + * Other mass balances + */ + for (i = 0; i < count_elts; i++) + { + if (elt_list[i].elt->master->s->type > AQ && + elt_list[i].elt->master->s->type < SOLID) + continue; + master_ptr = elt_list[i].elt->master; + if (master_ptr->primary == TRUE) + { + if (master_ptr->s->secondary != NULL) + { + master_ptr = master_ptr->s->secondary; + } + } +/* + * Special for ph_unknown, pe_unknown, and alkalinity_unknown + */ + if (master_ptr->unknown == ph_unknown) + { + continue; + } + else if (master_ptr->unknown == pe_unknown) + { + continue; + } + else if (master_ptr->unknown == alkalinity_unknown) + { + continue; + } +/* + * EX, sum exchange species only into EXCH mass balance in initial calculation + * into all mass balances in reaction calculation + */ + if (state >= REACTION || master_ptr->s->type == EX) + { + store_mb_unknowns(master_ptr->unknown, &s[n]->moles, + elt_list[i].coef * master_ptr->coef, &s[n]->dg); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +mb_for_species_surf(int n) +/* ---------------------------------------------------------------------- */ +{ +/* + * Make list of mass balance and charge balance equations in which + * to insert species n. + * + * count_mb_unknowns - number of equations and summation relations + * mb_unknowns.source - pointer to the LDBLE number to be multiplied + * by coef, usually moles. + * mb_unknowns.unknown - pointer to unknown which contains row number + * mb_unknowns.coef - coefficient of s[n] in equation or relation + */ + int i; + struct master *master_ptr; + + count_mb_unknowns = 0; +/* + * Include in charge balance, if diffuse_layer_x == FALSE + */ + if (charge_balance_unknown != NULL && dl_type_x == cxxSurface::NO_DL) + { + store_mb_unknowns(charge_balance_unknown, &s[n]->moles, s[n]->z, + &s[n]->dg); + } +/* + * Include diffuse layer in hydrogen and oxygen mass balance + */ + if (mass_hydrogen_unknown != NULL) + { +#ifdef COMBINE + store_mb_unknowns(mass_hydrogen_unknown, &s[n]->moles, + s[n]->h - 2 * s[n]->o, &s[n]->dg); +#else + store_mb_unknowns(mass_hydrogen_unknown, &s[n]->moles, s[n]->h, + &s[n]->dg); +#endif + } + if (mass_oxygen_unknown != NULL) + { + store_mb_unknowns(mass_oxygen_unknown, &s[n]->moles, s[n]->o, + &s[n]->dg); + } +/* + * Other mass balances + */ +/* + * Other mass balances + */ + for (i = 0; i < count_elts; i++) + { +/* Skip H+, e-, and H2O */ + if (elt_list[i].elt->master->s->type > AQ && + elt_list[i].elt->master->s->type < SOLID) + continue; + master_ptr = elt_list[i].elt->master; + if (master_ptr->primary == TRUE) + { + if (master_ptr->s->secondary != NULL) + { + master_ptr = master_ptr->s->secondary; + } + } +/* + * SURF_PSI, sum surface species in (surface + DL) charge balance + */ + if (master_ptr->s->type == SURF_PSI + && use.Get_surface_ptr()->Get_type() != cxxSurface::CD_MUSIC) + { + store_mb_unknowns(master_ptr->unknown, &s[n]->moles, s[n]->z, + &s[n]->dg); + continue; + } + if (master_ptr->s->type == SURF_PSI + && use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + store_mb_unknowns(master_ptr->unknown, &s[n]->moles, s[n]->dz[0], + &s[n]->dg); + continue; + } + if (master_ptr->s->type == SURF_PSI1) + { + store_mb_unknowns(master_ptr->unknown, &s[n]->moles, s[n]->dz[1], + &s[n]->dg); + continue; + } + if (master_ptr->s->type == SURF_PSI2) + { + store_mb_unknowns(master_ptr->unknown, &s[n]->moles, s[n]->dz[2], + &s[n]->dg); + /* + if (diffuse_layer_x == TRUE) { + store_mb_unknowns(master_ptr->unknown, &s[n]->moles, s[n]->z, &s[n]->dg ); + } else { + store_mb_unknowns(master_ptr->unknown, &s[n]->moles, s[n]->dz[2], &s[n]->dg ); + } + */ + continue; + } +/* + * Special for ph_unknown, pe_unknown, and alkalinity_unknown + */ + if (master_ptr->unknown == ph_unknown) + { + continue; + } + else if (master_ptr->unknown == pe_unknown) + { + continue; + } + else if (master_ptr->unknown == alkalinity_unknown) + { + continue; + } +/* + * SURF, sum surface species only into SURFACE mass balance in initial calculation + * into all mass balances in reaction calculation + */ + if (state >= REACTION || master_ptr->s->type == SURF) + { + store_mb_unknowns(master_ptr->unknown, &s[n]->moles, + elt_list[i].coef * master_ptr->coef, &s[n]->dg); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +reprep(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * If a basis species has been switched, makes new model. + * Unknowns are not changed, but mass-action equations are + * rewritten and lists for mass balance and jacobian are regenerated + */ + int i; +/* + * Initialize s, master, and unknown pointers + */ + for (i = 0; i < count_master; i++) + { + if (master[i]->in == FALSE) + continue; + rxn_free(master[i]->rxn_secondary); + master[i]->rxn_secondary = rxn_dup(master[i]->rxn_primary); + } + resetup_master(); +/* + * Set unknown pointers, unknown types, validity checks + */ + tidy_redox(); + if (get_input_errors() > 0) + { + error_msg("Program terminating due to input errors.", STOP); + } +/* + * Free arrays built in build_model + */ + s_x = (struct species **) free_check_null(s_x); + sum_mb1 = (struct list1 *) free_check_null(sum_mb1); + sum_mb2 = (struct list2 *) free_check_null(sum_mb2); + sum_jacob0 = (struct list0 *) free_check_null(sum_jacob0); + sum_jacob1 = (struct list1 *) free_check_null(sum_jacob1); + sum_jacob2 = (struct list2 *) free_check_null(sum_jacob2); + sum_delta = (struct list2 *) free_check_null(sum_delta); +/* + * Build model again + */ + build_model(); + k_temp(tc_x, patm_x); + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +resetup_master(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * For basis switch, rewrite equations for master species + * Set master_ptr->rxn_secondary, + * master_ptr->pe_rxn, + * and special cases for alkalinity, carbon, and pH. + */ + int i, j; + struct master *master_ptr, *master_ptr0; + + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type != MB) + continue; + master_ptr0 = x[i]->master[0]; + for (j = 0; (master_ptr = x[i]->master[j]) != NULL; j++) + { +/* + * Set flags + */ + if (j == 0) + { + if (master_ptr->s->primary == NULL) + { + rxn_free(master_ptr->rxn_secondary); + master_ptr->rxn_secondary = rxn_dup(master_ptr->s->rxn_s); + } + } + else + { + if (master_ptr0->s->primary == NULL) + { + rewrite_master_to_secondary(master_ptr, master_ptr0); + rxn_free(master_ptr->rxn_secondary); + master_ptr->rxn_secondary = rxn_alloc(count_trxn + 1); + trxn_copy(master_ptr->rxn_secondary); + } + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +write_mass_action_eqn_x(int stop) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reduce mass-action equation to the master species that are in the model + */ + LDBLE coef_e; + int count, repeat; + int i, count_rxn_orig; +/* + * Rewrite any secondary master species flagged REWRITE + * Replace pe if necessary + */ + count = 0; + repeat = TRUE; + while (repeat == TRUE) + { + count++; + if (count > MAX_ADD_EQUATIONS) + { + std::string name; + name = "Unknown"; + if (trxn.token[0].s != NULL) + { + name = trxn.token[0].s->name; + } + + input_error++; + error_string = sformatf( "Could not reduce equation " + "to primary and secondary species that are " + "in the model. Species: %s.", name.c_str()); + if (stop == STOP) + { + error_msg(error_string, CONTINUE); + } + else + { + warning_msg(error_string); + } + return (ERROR); + } + repeat = FALSE; + count_rxn_orig = count_trxn; + for (i = 1; i < count_rxn_orig; i++) + { + if (trxn.token[i].s->secondary == NULL) + continue; + if (trxn.token[i].s->secondary->in == REWRITE) + { + repeat = TRUE; + coef_e = + rxn_find_coef(trxn.token[i].s->secondary->rxn_secondary, + "e-"); + trxn_add(trxn.token[i].s->secondary->rxn_secondary, + trxn.token[i].coef, FALSE); + if (equal(coef_e, 0.0, TOL) == FALSE) + { + std::map < std::string, cxxChemRxn >::iterator chemRxnIt = pe_x.find(trxn.token[i].s->secondary->pe_rxn); + if ( chemRxnIt == pe_x.end() ) + { + cxxChemRxn &rxn_ref = pe_x[trxn.token[i].s->secondary->pe_rxn]; + trxn_add(rxn_ref, trxn.token[i].coef * coef_e, FALSE); + // Create temporary rxn object and add reactions together + cxxChemRxn rxn; + trxn_add(rxn, trxn.token[i].coef * coef_e, FALSE); + } + else + { + // Get reaction referred to by iterator and add reactions together + trxn_add(chemRxnIt->second, trxn.token[i].coef * coef_e, FALSE); + } + } + } + } + trxn_combine(); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_potential_factor(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Add the potential factor to surface mass-action equations. + * Factor is essentially the activity coefficient, representing + * the work required to bring charged ions to the surface + */ + int i; + std::string token; + LDBLE sum_z; + struct master *master_ptr; + struct unknown *unknown_ptr; + + if (use.Get_surface_ptr() == NULL) + { + input_error++; + error_string = sformatf( + "SURFACE not defined for surface species %s", + trxn.token[0].name); + error_msg(error_string, CONTINUE); + return(OK); + } + if (use.Get_surface_ptr()->Get_type() != cxxSurface::DDL && use.Get_surface_ptr()->Get_type() != cxxSurface::CCM) + return (OK); + sum_z = 0.0; + master_ptr = NULL; +/* + * Find sum of charge of aqueous species and surface master species + */ + for (i = 1; i < count_trxn; i++) + { + if (trxn.token[i].s->type == AQ || trxn.token[i].s == s_hplus || + trxn.token[i].s == s_eminus) + { + sum_z += trxn.token[i].s->z * trxn.token[i].coef; + } + if (trxn.token[i].s->type == SURF) + { + master_ptr = trxn.token[i].s->primary; + } + } +/* + * Find potential unknown for surface species + */ + if (master_ptr == NULL) + { + error_string = sformatf( + "Did not find a surface species in equation defining %s", + trxn.token[0].name); + error_msg(error_string, CONTINUE); + error_string = sformatf( + "One of the following must be defined with SURFACE_SPECIES:"); + error_msg(error_string, CONTINUE); + for (i = 1; i < count_trxn; i++) + { + error_string = sformatf( " %s", trxn.token[i].name); + error_msg(error_string, CONTINUE); + } + input_error++; + return (ERROR); + } + token = master_ptr->elt->name; + unknown_ptr = find_surface_charge_unknown(token, SURF_PSI); + if (unknown_ptr == NULL) + { + error_string = sformatf( + "No potential unknown found for surface species %s.", token.c_str()); + error_msg(error_string, STOP); + } + else + { + master_ptr = unknown_ptr->master[0]; /* potential for surface component */ + } +/* + * Make sure there is space + */ + if (count_trxn + 1 >= max_trxn) + { + space((void **) ((void *) &(trxn.token)), count_trxn + 1, &max_trxn, + sizeof(struct rxn_token_temp)); + } +/* + * Include psi in mass action equation + */ + if (master_ptr != NULL) + { + trxn.token[count_trxn].name = master_ptr->s->name; + trxn.token[count_trxn].s = master_ptr->s; + trxn.token[count_trxn].coef = -2.0 * sum_z; + count_trxn++; + } + else + { + output_msg(sformatf( + "How did this happen in add potential factor?\n")); + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_cd_music_factors(int n) +/* ---------------------------------------------------------------------- */ +{ +/* + * Add the potential factors for cd_music to surface mass-action equations. + * Factors are essentially the activity coefficient, representing + * the work required to bring charged ions to the three charge layers + * of the cd_music model + */ + int i; + std::string token; + struct master *master_ptr; + struct unknown *unknown_ptr; + if (use.Get_surface_ptr() == NULL) + { + input_error++; + error_string = sformatf( + "SURFACE not defined for surface species %s", + trxn.token[0].name); + error_msg(error_string, CONTINUE); + return(OK); + } + if (use.Get_surface_ptr()->Get_type() != cxxSurface::CD_MUSIC) + return (OK); + master_ptr = NULL; +/* + * Find sum of charge of aqueous species and surface master species + */ + for (i = 1; i < count_trxn; i++) + { + if (trxn.token[i].s->type == SURF) + { + master_ptr = trxn.token[i].s->primary; + } + } +/* + * Find potential unknown for surface species + */ + if (master_ptr == NULL) + { + error_string = sformatf( + "Did not find a surface species in equation defining %s", + trxn.token[0].name); + error_msg(error_string, CONTINUE); + error_string = sformatf( + "One of the following must be defined with SURFACE_SPECIES:"); + error_msg(error_string, CONTINUE); + for (i = 1; i < count_trxn; i++) + { + error_string = sformatf( " %s", trxn.token[i].name); + error_msg(error_string, CONTINUE); + } + input_error++; + return (ERROR); + } + token = master_ptr->elt->name; + /* + * Plane 0 + */ + unknown_ptr = find_surface_charge_unknown(token, SURF_PSI); + if (unknown_ptr == NULL) + { + error_string = sformatf( + "No potential unknown found for surface species %s.", token.c_str()); + error_msg(error_string, STOP); + return (ERROR); + } + master_ptr = unknown_ptr->master[0]; /* potential for surface component */ + /* + * Make sure there is space + */ + if (count_trxn + 3 >= max_trxn) + { + space((void **) ((void *) &(trxn.token)), count_trxn + 3, &max_trxn, + sizeof(struct rxn_token_temp)); + } + /* + * Include psi in mass action equation + */ + trxn.token[count_trxn].name = master_ptr->s->name; + trxn.token[count_trxn].s = master_ptr->s; + /*trxn.token[count_trxn].coef = s[n]->dz[0];*/ + trxn.token[count_trxn].coef = trxn.dz[0]; + + count_trxn++; + + /* + * Plane 1 + */ + unknown_ptr = find_surface_charge_unknown(token, SURF_PSI1); + if (unknown_ptr == NULL) + { + error_string = sformatf( + "No potential unknown found for surface species %s.", token.c_str()); + error_msg(error_string, STOP); + return (ERROR); + } + master_ptr = unknown_ptr->master[0]; /* potential for surface component */ + /* + * Include psi in mass action equation + */ + trxn.token[count_trxn].name = master_ptr->s->name; + trxn.token[count_trxn].s = master_ptr->s; + /*trxn.token[count_trxn].coef = s[n]->dz[1];*/ + trxn.token[count_trxn].coef = trxn.dz[1]; + count_trxn++; + /* + * Plane 2 + */ + unknown_ptr = find_surface_charge_unknown(token, SURF_PSI2); + if (unknown_ptr == NULL) + { + error_string = sformatf( + "No potential unknown found for surface species %s.", token.c_str()); + error_msg(error_string, STOP); + return (ERROR); + } + master_ptr = unknown_ptr->master[0]; /* potential for surface component */ + /* + * Include psi in mass action equation + */ + trxn.token[count_trxn].name = master_ptr->s->name; + trxn.token[count_trxn].s = master_ptr->s; + /*trxn.token[count_trxn].coef = s[n]->dz[2];*/ + trxn.token[count_trxn].coef = trxn.dz[2]; + count_trxn++; + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_surface_charge_balance(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Include charge balance in list for mass-balance equations + */ + int i; + char *ptr; + std::string token; + + struct master *master_ptr; + struct unknown *unknown_ptr; + if (use.Get_surface_ptr() == NULL) + { + input_error++; + error_string = sformatf( + "SURFACE not defined for surface species %s", + trxn.token[0].name); + error_msg(error_string, CONTINUE); + return(OK); + } + if (use.Get_surface_ptr()->Get_type() != cxxSurface::DDL && use.Get_surface_ptr()->Get_type() != cxxSurface::CCM) + return (OK); + master_ptr = NULL; +/* + * Find master species + */ + for (i = 0; i < count_elts; i++) + { + if (elt_list[i].elt->primary->s->type == SURF) + { + master_ptr = elt_list[i].elt->primary; + break; + } + } + if (i >= count_elts) + { + error_string = sformatf( + "No surface master species found for surface species."); + error_msg(error_string, STOP); + return(OK); + } +/* + * Find potential unknown for surface species + */ + token = master_ptr->elt->name; + unknown_ptr = find_surface_charge_unknown(token, SURF_PSI); + if (unknown_ptr == NULL) + { + error_string = sformatf( + "No potential unknown found for surface species %s.", token.c_str()); + error_msg(error_string, STOP); + return(OK); + } + master_ptr = unknown_ptr->master[0]; /* potential for surface component */ +/* + * Include charge balance in list for mass-balance equations + */ + char * temp_name = string_duplicate(master_ptr->elt->name); + ptr = temp_name; + get_secondary_in_species(&ptr, 1.0); + free_check_null(temp_name); + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_cd_music_charge_balances(int n) +/* ---------------------------------------------------------------------- */ +{ +/* + * Add the potential factor to surface mass-action equations. + * Factor is essentially the activity coefficient, representing + * the work required to bring charged ions to the surface + */ + int i; + std::string token; + + struct master *master_ptr; + struct unknown *unknown_ptr; + if (use.Get_surface_ptr() == NULL) + { + input_error++; + error_string = sformatf( + "SURFACE not defined for surface species %s", + trxn.token[0].name); + error_msg(error_string, CONTINUE); + return(OK); + } + if (use.Get_surface_ptr()->Get_type() != cxxSurface::CD_MUSIC) + return (OK); + master_ptr = NULL; +/* + * Find master species + */ + for (i = 0; i < count_elts; i++) + { + if (elt_list[i].elt->primary->s->type == SURF) + { + master_ptr = elt_list[i].elt->primary; + break; + } + } + if (i >= count_elts || master_ptr == NULL) + { + error_string = sformatf( + "No surface master species found for surface species."); + error_msg(error_string, STOP); + return ERROR; + } + /* + * Find potential unknown for plane 0 + */ + token = master_ptr->elt->name; + unknown_ptr = find_surface_charge_unknown(token, SURF_PSI); + master_ptr = unknown_ptr->master[0]; /* potential for surface component */ + /* + * Include charge balance in list for mass-balance equations + */ + { + char * temp_name = string_duplicate( master_ptr->elt->name); + char *ptr = temp_name; + get_secondary_in_species(&ptr, s[n]->dz[0]); + free_check_null(temp_name); + } + /* + * Find potential unknown for plane 1 + */ + token = master_ptr->elt->name; + unknown_ptr = find_surface_charge_unknown(token, SURF_PSI1); + master_ptr = unknown_ptr->master[0]; /* potential for surface component */ + /* + * Include charge balance in list for mass-balance equations + */ + { + char * temp_name = string_duplicate( master_ptr->elt->name); + char *ptr = temp_name; + get_secondary_in_species(&ptr, s[n]->dz[1]); + free_check_null(temp_name); + } + /* + * Find potential unknown for plane 2 + */ + token = master_ptr->elt->name; + unknown_ptr = find_surface_charge_unknown(token, SURF_PSI2); + master_ptr = unknown_ptr->master[0]; /* potential for surface component */ + /* + * Include charge balance in list for mass-balance equations + */ + { + char * temp_name = string_duplicate(master_ptr->elt->name); + char *ptr = temp_name; + get_secondary_in_species(&ptr, s[n]->dz[2]); + free_check_null(temp_name); + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +rewrite_master_to_secondary(struct master *master_ptr1, + struct master *master_ptr2) +/* ---------------------------------------------------------------------- */ +{ +/* + * Write equation for secondary master species in terms of another secondary master species + * Store result in rxn_secondary of master_ptr. + */ + LDBLE coef1, coef2; + struct master *master_ptr_p1, *master_ptr_p2; +/* + * Check that the two master species have the same primary master species + */ + master_ptr_p1 = master_ptr1->elt->primary; + master_ptr_p2 = master_ptr2->elt->primary; + if (master_ptr_p1 != master_ptr_p2 || master_ptr_p1 == NULL) + { + error_string = sformatf( + "All redox states must be for the same element. %s\t%s.", + master_ptr1->elt->name, master_ptr2->elt->name); + error_msg(error_string, CONTINUE); + input_error++; + return (ERROR); + } +/* + * Find coefficient of primary master in reaction + */ + coef1 = rxn_find_coef(master_ptr1->rxn_primary, master_ptr_p1->s->name); + coef2 = rxn_find_coef(master_ptr2->rxn_primary, master_ptr_p1->s->name); + if (equal(coef1, 0.0, TOL) == TRUE || equal(coef2, 0.0, TOL) == TRUE) + { + error_string = sformatf( + "One of these equations does not contain master species for element, %s or %s.", + master_ptr1->s->name, master_ptr2->s->name); + error_msg(error_string, CONTINUE); + input_error++; + return (ERROR); + } +/* + * Rewrite equation to secondary master species + */ + count_trxn = 0; + trxn_add(master_ptr1->rxn_primary, 1.0, FALSE); + trxn_add(master_ptr2->rxn_primary, -coef1 / coef2, TRUE); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +setup_exchange(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Fill in data for exchanger in unknowns structures + */ + struct master *master_ptr; + struct master **master_ptr_list; + + if (use.Get_exchange_ptr() == NULL) + return (OK); + for (size_t j = 0; j < use.Get_exchange_ptr()->Get_exchange_comps().size(); j++) + { + cxxExchComp & comp_ref = use.Get_exchange_ptr()->Get_exchange_comps()[j]; + //{ + // element * elt_ptr = element_store(comp_ref.Get_formula().c_str()); + // if (elt_ptr == NULL || elt_ptr->master == NULL) + // { + // error_string = sformatf( "Component not in database, %s", comp_ref.Get_formula().c_str()); + // input_error++; + // error_msg(error_string, CONTINUE); + // continue; + // } + //} + + cxxNameDouble nd(comp_ref.Get_totals()); + cxxNameDouble::iterator it = nd.begin(); + for ( ; it != nd.end(); it++) + { +/* + * Find master species + */ + element * elt_ptr = element_store(it->first.c_str()); + if (elt_ptr == NULL || elt_ptr->master == NULL) + { + error_string = sformatf( "Master species not in database " + "for %s, skipping element.", + it->first.c_str()); + input_error++; + error_msg(error_string, CONTINUE); + continue; + } + master_ptr = elt_ptr->master; + if (master_ptr->type != EX) + continue; +/* + * Check for data already given + */ + if (master_ptr->in != FALSE) + { + x[master_ptr->unknown->number]->moles += + it->second; + } + else + { +/* + * Set flags + */ + master_ptr_list = unknown_alloc_master(); + master_ptr_list[0] = master_ptr; + master_ptr->in = TRUE; +/* + * Set unknown data + */ + x[count_unknowns]->type = EXCH; + x[count_unknowns]->exch_comp = string_hsave(it->first.c_str()); + x[count_unknowns]->description = elt_ptr->name; + x[count_unknowns]->moles = it->second; + x[count_unknowns]->master = master_ptr_list; + x[count_unknowns]->master[0]->unknown = x[count_unknowns]; + count_unknowns++; + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +setup_gas_phase(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Fill in data for gas phase unknown (sum of partial pressures) + * in unknown structure + */ + if (use.Get_gas_phase_ptr() == NULL) + return (OK); + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME && ( + gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume) && numerical_fixed_volume) + { + return setup_fixed_volume_gas(); + } + +/* + * One for total moles in gas + */ + x[count_unknowns]->type = GAS_MOLES; + x[count_unknowns]->description = string_hsave("gas moles"); + x[count_unknowns]->moles = 0.0; + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + x[count_unknowns]->moles += gc_ptr->Get_moles(); + } + if (x[count_unknowns]->moles <= 0) + x[count_unknowns]->moles = MIN_TOTAL; + x[count_unknowns]->ln_moles = log(x[count_unknowns]->moles); + gas_unknown = x[count_unknowns]; + count_unknowns++; + return (OK); +} + + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +setup_ss_assemblage(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Fill in data for solid solution unknowns (sum of partial pressures) + * in unknown structure + */ + if (use.Get_ss_assemblage_ptr() == NULL) + return (OK); +/* + * One for each component in each solid solution + */ + ss_unknown = NULL; + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t j = 0; j < ss_ptrs.size(); j++) + { + for (size_t i = 0; i < ss_ptrs[j]->Get_ss_comps().size(); i++) + { + cxxSScomp *comp_ptr = &(ss_ptrs[j]->Get_ss_comps()[i]); + int l; + struct phase* phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + x[count_unknowns]->type = SS_MOLES; + x[count_unknowns]->description = string_hsave(comp_ptr->Get_name().c_str()); + x[count_unknowns]->moles = 0.0; + if (comp_ptr->Get_moles() <= 0) + { + comp_ptr->Set_moles(MIN_TOTAL_SS); + } + x[count_unknowns]->moles = comp_ptr->Get_moles(); + comp_ptr->Set_initial_moles(x[count_unknowns]->moles); + x[count_unknowns]->ln_moles = log(x[count_unknowns]->moles); + x[count_unknowns]->ss_name = string_hsave(ss_ptrs[j]->Get_name().c_str()); + x[count_unknowns]->ss_ptr = ss_ptrs[j]; + x[count_unknowns]->ss_comp_name = string_hsave(comp_ptr->Get_name().c_str()); + x[count_unknowns]->ss_comp_ptr = comp_ptr; + x[count_unknowns]->ss_comp_number = (int) i; + x[count_unknowns]->phase = phase_ptr; + x[count_unknowns]->number = count_unknowns; + x[count_unknowns]->phase->dn = comp_ptr->Get_dn(); + x[count_unknowns]->phase->dnb = comp_ptr->Get_dnb(); + x[count_unknowns]->phase->dnc = comp_ptr->Get_dnc(); + x[count_unknowns]->phase->log10_fraction_x = comp_ptr->Get_log10_fraction_x(); + x[count_unknowns]->phase->log10_lambda =comp_ptr->Get_log10_lambda(); + if (ss_unknown == NULL) + ss_unknown = x[count_unknowns]; + count_unknowns++; + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +setup_surface(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Fill in data for surface assemblage in unknown structure + */ + struct master **master_ptr_list; + int mb_unknown_number; + + if (use.Get_surface_ptr() == NULL) + return (OK); + + for (size_t i = 0; i < use.Get_surface_ptr()->Get_surface_comps().size(); i++) + { + cxxSurfaceComp *comp_ptr = &(use.Get_surface_ptr()->Get_surface_comps()[i]); + /* + * Find master species for each surface, setup unknown structure + */ + cxxNameDouble::iterator jit; + for (jit = comp_ptr->Get_totals().begin(); jit != comp_ptr->Get_totals().end(); jit++) + { + struct element *elt_ptr = element_store(jit->first.c_str()); + struct master *master_ptr = elt_ptr->master; + if (master_ptr == NULL) + { + error_string = sformatf( + "Master species not in database for %s, skipping element.", + elt_ptr->name); + warning_msg(error_string); + continue; + } + if (master_ptr->type != SURF) + continue; + /* + * Check that data not already given + */ + if (master_ptr->in != FALSE) + { + error_string = sformatf( + "Analytical data entered twice for %s.", + master_ptr->s->name); + error_msg(error_string, CONTINUE); + input_error++; + continue; + } + /* + * Set flags + */ + master_ptr_list = unknown_alloc_master(); + master_ptr_list[0] = master_ptr; + master_ptr->in = TRUE; + /* + * Setup mass balance unknown + */ + x[count_unknowns]->type = SURFACE; + x[count_unknowns]->description = string_hsave(jit->first.c_str()); + x[count_unknowns]->number = count_unknowns; + x[count_unknowns]->surface_comp = string_hsave(comp_ptr->Get_formula().c_str()); + x[count_unknowns]->master = master_ptr_list; + x[count_unknowns]->master[0]->unknown = x[count_unknowns]; + x[count_unknowns]->moles = jit->second; + if (surface_unknown == NULL) + surface_unknown = x[count_unknowns]; + x[count_unknowns]->potential_unknown = NULL; + count_unknowns++; + /*if (use.Get_surface_ptr()->edl == FALSE) continue; */ + if (use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM) + { + /* + * Setup surface-potential unknown + */ + std::string token = master_ptr->elt->name; + struct unknown *unknown_ptr = find_surface_charge_unknown(token, SURF_PSI); + if (unknown_ptr != NULL) + { + x[count_unknowns - 1]->potential_unknown = unknown_ptr; + } + else + { + /* + * Find master species + */ + replace("_CB", "_psi", token); + master_ptr = master_bsearch(token.c_str()); + master_ptr_list = unknown_alloc_master(); + master_ptr_list[0] = master_ptr; + master_ptr->in = TRUE; + /* + * Find surface charge structure + */ + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()-> + Find_charge(comp_ptr->Get_charge_name()); + if (charge_ptr == NULL) + { + input_error++; + error_msg(sformatf("Charge structure not defined for surface, %s", use.Get_surface_ptr()->Get_description().c_str()), CONTINUE); + continue; + } + x[count_unknowns]->type = SURFACE_CB; + x[count_unknowns]->surface_charge = string_hsave(charge_ptr->Get_name().c_str()); + x[count_unknowns]->related_moles = charge_ptr->Get_grams(); + x[count_unknowns]->mass_water = charge_ptr->Get_mass_water(); + replace("_psi", "_CB", token); + x[count_unknowns]->description = string_hsave(token.c_str()); + x[count_unknowns]->master = master_ptr_list; + x[count_unknowns]->master[0]->unknown = x[count_unknowns]; + x[count_unknowns]->moles = 0.0; + x[count_unknowns - 1]->potential_unknown = + x[count_unknowns]; + x[count_unknowns]->surface_comp = + x[count_unknowns - 1]->surface_comp; + count_unknowns++; + } + } + else if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC) + { + /* + * Setup 3 surface-potential unknowns + */ + mb_unknown_number = count_unknowns - 1; + std::string token(master_ptr->elt->name); + std::string mass_balance_name(token); + int plane; + for (plane = SURF_PSI; plane <= SURF_PSI2; plane++) + { + std::string cb_suffix("_CB"); + std::string psi_suffix("_psi"); + struct unknown **unknown_target; + unknown_target = NULL; + int type = SURFACE_CB; + switch (plane) + { + case SURF_PSI: + type = SURFACE_CB; + unknown_target = + &(x[mb_unknown_number]->potential_unknown); + break; + case SURF_PSI1: + cb_suffix.append("b"); + psi_suffix.append("b"); + type = SURFACE_CB1; + unknown_target = &(x[mb_unknown_number]->potential_unknown1); + break; + case SURF_PSI2: + cb_suffix.append("d"); + psi_suffix.append("d"); + type = SURFACE_CB2; + unknown_target = &(x[mb_unknown_number]->potential_unknown2); + break; + } + struct unknown *unknown_ptr = find_surface_charge_unknown(token, plane); + if (unknown_ptr != NULL) + { + *unknown_target = unknown_ptr; + } + else + { + /* + * Find master species + */ + replace(cb_suffix.c_str(), psi_suffix.c_str(), token); + master_ptr = master_bsearch(token.c_str()); + master_ptr_list = unknown_alloc_master(); + master_ptr_list[0] = master_ptr; + master_ptr->in = TRUE; + /* + * Find surface charge structure + */ + cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()-> + Find_charge(comp_ptr->Get_charge_name()); + x[count_unknowns]->type = type; + x[count_unknowns]->surface_charge = string_hsave(charge_ptr->Get_name().c_str()); + x[count_unknowns]->related_moles = charge_ptr->Get_grams(); + x[count_unknowns]->mass_water = charge_ptr->Get_mass_water(); + replace(psi_suffix.c_str(), cb_suffix.c_str(), token); + x[count_unknowns]->description = string_hsave(token.c_str()); + x[count_unknowns]->master = master_ptr_list; + /* + * Find surface charge structure + */ + if (plane == SURF_PSI) + { + /*use.Get_surface_ptr()->charge[k].psi_master = x[count_unknowns]->master[0]; */ + x[mb_unknown_number]->potential_unknown = + x[count_unknowns]; + } + else if (plane == SURF_PSI1) + { + /*use.Get_surface_ptr()->charge[k].psi_master1 = x[count_unknowns]->master[0]; */ + x[mb_unknown_number]->potential_unknown1 = + x[count_unknowns]; + } + else if (plane == SURF_PSI2) + { + /*use.Get_surface_ptr()->charge[k].psi_master2 = x[count_unknowns]->master[0]; */ + x[mb_unknown_number]->potential_unknown2 = + x[count_unknowns]; + } + x[count_unknowns]->master[0]->unknown = + x[count_unknowns]; + x[count_unknowns]->moles = 0.0; + x[count_unknowns]->surface_comp = + x[mb_unknown_number]->surface_comp; + count_unknowns++; + } + } + /* Add SURFACE unknown to a list for SURF_PSI */ + struct unknown *unknown_ptr = find_surface_charge_unknown(token, SURF_PSI); + unknown_ptr->comp_unknowns = (struct unknown **) PHRQ_realloc(unknown_ptr->comp_unknowns, + (size_t) ((unknown_ptr->count_comp_unknowns + 1) * sizeof(struct unknown *))); + if (unknown_ptr->comp_unknowns == NULL) + malloc_error(); + unknown_ptr->comp_unknowns[unknown_ptr->count_comp_unknowns++] = + x[mb_unknown_number]; + } + } + } + /* + * check related phases + */ + if (use.Get_surface_ptr()->Get_related_phases()) + { + cxxPPassemblage *pp_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, use.Get_n_surface_user()); + for (size_t i = 0; i < use.Get_surface_ptr()->Get_surface_comps().size(); i++) + { + if (use.Get_surface_ptr()->Get_surface_comps()[i].Get_phase_name().size() > 0) + { + if (pp_ptr == NULL || + (pp_ptr->Get_pp_assemblage_comps().find(use.Get_surface_ptr()->Get_surface_comps()[i].Get_phase_name()) == + pp_ptr->Get_pp_assemblage_comps().end())) + { + Rxn_new_surface.insert(use.Get_n_surface_user()); + cxxSurface *surf_ptr = Utilities::Rxn_find(Rxn_surface_map, use.Get_n_surface_user()); + surf_ptr->Set_new_def(true); + this->tidy_min_surface(); + return (FALSE); + } + } + } + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type != SURFACE_CB) + continue; + cxxSurfaceComp *comp_i_ptr = use.Get_surface_ptr()->Find_comp(x[i]->surface_comp); + for (int j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE) + continue; + if (x[j]->potential_unknown != x[i]) + continue; + cxxSurfaceComp *comp_j_ptr = use.Get_surface_ptr()->Find_comp(x[j]->surface_comp); + std::string name1, name2; + if (comp_j_ptr->Get_phase_name() != + comp_i_ptr->Get_phase_name()) + { + if (comp_i_ptr->Get_phase_name().size() == 0) + { + name1 = "None"; + } + else + { + name1 = comp_i_ptr->Get_phase_name(); + } + if (comp_j_ptr->Get_phase_name().size() == 0) + { + name2 = "None"; + } + else + { + name2 = comp_j_ptr->Get_phase_name(); + } + input_error++; + + error_string = sformatf( + "All surface sites for a single component must be related to the same phase.\n\tSite: %s is related to %s, Site: %s is related to %s", + comp_i_ptr->Get_master_element().c_str(), name1.c_str(), + comp_j_ptr->Get_master_element().c_str(), name2.c_str()); + error_msg(error_string, CONTINUE); + } + } + } + } + /* + * check related kinetics + */ + if (use.Get_surface_ptr()->Get_related_rate()) + { + cxxKinetics *kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_surface_user()); + for (size_t i = 0; i < use.Get_surface_ptr()->Get_surface_comps().size(); i++) + { + if (use.Get_surface_ptr()->Get_surface_comps()[i].Get_rate_name().size() > 0) + { + if (kinetics_ptr == NULL || + (kinetics_ptr->Find(use.Get_surface_ptr()->Get_surface_comps()[i].Get_rate_name()) == NULL)) + { + Rxn_new_surface.insert(use.Get_n_surface_user()); + this->tidy_kin_surface(); + return (FALSE); + } + } + } + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type != SURFACE_CB) + continue; + cxxSurfaceComp *comp_i_ptr = use.Get_surface_ptr()->Find_comp(x[i]->surface_comp); + for (int j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE) + continue; + if (x[j]->potential_unknown != x[i]) + continue; + cxxSurfaceComp *comp_j_ptr = use.Get_surface_ptr()->Find_comp(x[j]->surface_comp); + if (comp_j_ptr->Get_rate_name() != + comp_i_ptr->Get_rate_name()) + { + std::string name1, name2; + if (comp_i_ptr->Get_rate_name().size() == 0) + { + name1 = "None"; + } + else + { + name1 = comp_i_ptr->Get_rate_name(); + } + if (comp_j_ptr->Get_rate_name().size() == 0) + { + name2 = "None"; + } + else + { + name2 = comp_j_ptr->Get_rate_name(); + } + input_error++; + error_string = sformatf( + "All surface sites for a single component must be related to the same kinetic reaction.\n\tSite: %s is related to %s, Site: %s is related to %s", + comp_i_ptr->Get_master_element().c_str(), name1.c_str(), + comp_j_ptr->Get_master_element().c_str(), name2.c_str()); + error_msg(error_string, CONTINUE); + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +struct unknown * Phreeqc:: +find_surface_charge_unknown(std::string &str, int plane) +/* ---------------------------------------------------------------------- */ +{ +/* + * Makes name for the potential unknown and returns in str_ptr + * Returns NULL if this unknown not in unknown list else + * returns a pointer to the potential unknown + */ + std::string token; + Utilities::replace("_", " ", str); + std::string::iterator b = str.begin(); + std::string::iterator e = str.end(); + CParser::copy_token(token, b, e); + if (plane == SURF_PSI) + { + token.append("_CB"); + } + else if (plane == SURF_PSI1) + { + token.append("_CBb"); + } + else if (plane == SURF_PSI2) + { + token.append("_CBd"); + } + str = token; + for (int i = 0; i < count_unknowns; i++) + { + if (strcmp(str.c_str(), x[i]->description) == 0) + { + return (x[i]); + } + } + return (NULL); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +setup_master_rxn(struct master **master_ptr_list, const std::string &pe_rxn) +/* ---------------------------------------------------------------------- */ +{ +/* + * Rewrites rxn_secondary for all redox states in list + * First, in = TRUE; others, in = REWRITE + */ + int j; + struct master *master_ptr, *master_ptr0; +/* + * Set master_ptr->in, master_ptr->rxn + */ + master_ptr0 = master_ptr_list[0]; + for (j = 0; (master_ptr = master_ptr_list[j]) != NULL; j++) + { +/* + * Check that data not already given + */ + if (master_ptr->s == s_h2o) + { + error_string = sformatf( + "Cannot enter concentration data for O(-2),\n\tdissolved oxygen is O(0),\n\tfor mass of water, use -water identifier."); + error_msg(error_string, CONTINUE); + input_error++; + continue; + } + + if (master_ptr->in != FALSE) + { + if (master_ptr->s != s_eminus && master_ptr->s != s_hplus) + { + error_string = sformatf( + "Analytical data entered twice for %s.", + master_ptr->s->name); + error_msg(error_string, CONTINUE); + input_error++; + continue; + } + } +/* + * Set flags + */ + if (j == 0) + { + master_ptr->in = TRUE; + if (master_ptr->s->primary == NULL) + { + rxn_free(master_ptr->rxn_secondary); + master_ptr->rxn_secondary = rxn_dup(master_ptr->s->rxn_s); +/* debug + trxn_print (); + */ + } + } + else + { + master_ptr->in = REWRITE; + if (master_ptr0->s->primary == NULL) + { + rewrite_master_to_secondary(master_ptr, master_ptr0); + rxn_free(master_ptr->rxn_secondary); + master_ptr->rxn_secondary = rxn_alloc(count_trxn + 1); + trxn_copy(master_ptr->rxn_secondary); +/* debug + trxn_print (); + */ + } + } + master_ptr->pe_rxn = string_hsave(pe_rxn.c_str()); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_PR(std::vector phase_ptrs, LDBLE P, LDBLE TK, LDBLE V_m) +/* ---------------------------------------------------------------------- */ +/* Calculate fugacity and fugacity coefficient for gas pressures if critical T and P + are defined. + 1) Solve molar volume V_m or total pressure P from Peng-Robinson's EOS: + P = R * T / (V_m - b) - a * aa / (V_m^2 + 2 * b * V_m - b^2) + a = 0.457235 * (R * T_c)^2 / P_c + b = 0.077796 * R * T_c / P_c + aa = (1 + kk * (1 - T_r^0.5))^2 + kk = 0.37464 + 1.54226 * omega - 0.26992 * omega^2 + T_r = T / T_c + multicomponent gas phase: + use: b_sum = Sum(x_i * b), x_i is mole-fraction + a_aa_sum = Sum_i( Sum_j(x_i * x_j * (a_i * aa_i * a_j * aa_j)^0.5) ) + 2) Find the fugacity coefficient phi for gas i: + log(phi_i) = B_ratio * (z - 1) - log(z - B) + A / (2.8284 * B) * (B_ratio - 2 / a_aa_sum * a_aa_sum2) *\ + log((z + 2.4142 * B) / (z - 0.4142 * B)) + B_ratio = b_i / b_sum + A = a_aa_sum * P / R_TK^2 + B = b_sum * P / R_TK + a_aa_sum2 = Sum_j(x_j * (a_aa_i * a_aa_j)^0.5 + 3) correct the solubility of gas i with: + pr_si_f = log10(phi_i) - Delta_V_i * (P - 1) / (2.303 * R * TK); +*/ +{ + int i, i1, n_g = (int) phase_ptrs.size(); + LDBLE T_c, P_c; + LDBLE A, B, B_r, /*b2,*/ kk, oo, a_aa, T_r; + LDBLE m_sum, /*b_sum, a_aa_sum,*/ a_aa_sum2; + LDBLE phi; + LDBLE /*R_TK,*/ R = R_LITER_ATM; /* L atm / (K mol) */ + LDBLE r3[4], r3_12, rp, rp3, rq, rz, ri, ri1, one_3 = 0.33333333333333333; + LDBLE disct, vinit, v1, ddp, dp_dv, dp_dv2; + int it; + struct phase *phase_ptr, *phase_ptr1; + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + bool halved; + R_TK = R * TK; + m_sum = b_sum = a_aa_sum = 0.0; + for (i = 0; i < n_g; i++) + { + phase_ptr = phase_ptrs[i]; + if (n_g > 1) + { + if (phase_ptr->moles_x == 0) + continue; + m_sum += phase_ptr->moles_x; + } + if (phase_ptr->t_c == 0.0 || phase_ptr->p_c == 0.0) + error_msg("Cannot calculate a mixture of ideal and Peng_Robinson gases,\n please define Tc and Pc for the active gases in PHASES.", STOP); + //continue; + if (!phase_ptr->pr_a) + { + T_c = phase_ptr->t_c; + P_c = phase_ptr->p_c; + phase_ptr->pr_a = 0.457235 * R * R * T_c * T_c / P_c; + phase_ptr->pr_b = 0.077796 * R * T_c / P_c; + T_r = TK / T_c; + oo = phase_ptr->omega; + kk = 0.37464 + oo * (1.54226 - 0.26992 * oo); + phase_ptr->pr_alpha = pow(1 + kk * (1 - sqrt(T_r)), 2); + phase_ptr->pr_tk = TK; +// phase_ptr->pr_in = true; + } + if (phase_ptr->pr_tk != TK) + { + T_r = TK / phase_ptr->t_c; + oo = phase_ptr->omega; + kk = 0.37464 + oo * (1.54226 - 0.26992 * oo); + phase_ptr->pr_alpha = pow(1 + kk * (1 - sqrt(T_r)), 2); + phase_ptr->pr_tk = TK; +// phase_ptr->pr_in = true; + } + } + for (i = 0; i < n_g; i++) + { + phase_ptr = phase_ptrs[i]; + if (n_g == 1) + { + phase_ptr->fraction_x = 1.0; + break; + } + if (m_sum == 0) + return (OK); + phase_ptr->fraction_x = phase_ptr->moles_x / m_sum; + } + + for (i = 0; i < n_g; i++) + { + a_aa_sum2 = 0.0; + phase_ptr = phase_ptrs[i]; + //if (phase_ptr->t_c == 0.0 || phase_ptr->p_c == 0.0) + // continue; + b_sum += phase_ptr->fraction_x * phase_ptr->pr_b; + for (i1 = 0; i1 < n_g; i1++) + { + phase_ptr1 = phase_ptrs[i1]; + //if (phase_ptr1->t_c == 0.0 || phase_ptr1->p_c == 0.0) + // continue; + if (phase_ptr1->fraction_x == 0) + continue; + a_aa = sqrt(phase_ptr->pr_a * phase_ptr->pr_alpha * + phase_ptr1->pr_a * phase_ptr1->pr_alpha); + if (!strcmp(phase_ptr->name, "H2O(g)")) + { + if (!strcmp(phase_ptr1->name, "CO2(g)")) + a_aa *= 0.81; // Soreide and Whitson, 1992, FPE 77, 217 + else if (!strcmp(phase_ptr1->name, "H2S(g)")) + a_aa *= 0.81; + else if (!strcmp(phase_ptr1->name, "CH4(g)")) + a_aa *= 0.51; + else if (!strcmp(phase_ptr->name, "Mtg(g)")) + a_aa *= 0.51; + else if (!strcmp(phase_ptr1->name, "N2(g)")) + a_aa *= 0.51; + } + if (!strcmp(phase_ptr1->name, "H2O(g)")) + { + if (!strcmp(phase_ptr->name, "CO2(g)")) + a_aa *= 0.81; + else if (!strcmp(phase_ptr->name, "H2S(g)")) + a_aa *= 0.81; + else if (!strcmp(phase_ptr->name, "CH4(g)")) + a_aa *= 0.51; + else if (!strcmp(phase_ptr->name, "Mtg(g)")) + a_aa *= 0.51; + else if (!strcmp(phase_ptr->name, "N2(g)")) + a_aa *= 0.51; + } + a_aa_sum += phase_ptr->fraction_x * phase_ptr1->fraction_x * a_aa; + a_aa_sum2 += phase_ptr1->fraction_x * a_aa; + } + phase_ptr->pr_aa_sum2 = a_aa_sum2; + } + b2 = b_sum * b_sum; + + if (V_m) + { + P = R_TK / (V_m - b_sum) - a_aa_sum / (V_m * (V_m + 2 * b_sum) - b2); + if (iterations > 0 && P < 150 && V_m < 1.01) + { + // check for 3-roots... + r3[1] = b_sum - R_TK / P; + r3[2] = -3.0 * b2 + (a_aa_sum - R_TK * 2.0 * b_sum) / P; + r3[3] = b2 * b_sum + (R_TK * b2 - b_sum * a_aa_sum) / P; + // the discriminant of the cubic eqn... + disct = 18. * r3[1] * r3[2] * r3[3] - + 4. * pow(r3[1], 3) * r3[3] + + r3[1] * r3[1] * r3[2] * r3[2] - + 4. * pow(r3[2], 3) - + 27. * r3[3] * r3[3]; + //if (iterations > 50) + // it = 0; // debug + if (disct > 0) + { + // 3-roots, find the largest P... + it = 0; + halved = false; + ddp = 1e-9; + v1 = vinit = 0.729; + dp_dv = f_Vm(v1, this); + while (fabs(dp_dv) > 1e-11 && it < 40) + { + it +=1; + dp_dv2 = f_Vm(v1 - ddp, this); + v1 -= (dp_dv * ddp / (dp_dv - dp_dv2)); + if (!halved && (v1 > vinit || v1 < 0.03)) + { + if (vinit > 0.329) + vinit -= 0.1; + else + vinit -=0.05; + if (vinit < 0.03) + { + vinit = halve(f_Vm, 0.03, 1.0, 1e-3); + if (f_Vm(vinit - 2e-3, this) < 0) + vinit = halve(f_Vm, vinit + 2e-3, 1.0, 1e-3); + halved = true; + } + v1 = vinit; + } + dp_dv = f_Vm(v1, this); + if (fabs(dp_dv) < 1e-11) + { + if (f_Vm(v1 - 1e-4, this) < 0) + { + v1 = halve(f_Vm, v1 + 1e-4, 1.0, 1e-3); + dp_dv = f_Vm(v1, this); + } + } + } + if (it == 40) + { +// accept a (possible) whobble in the curve... +// error_msg("No convergence when calculating P in Peng-Robinson.", STOP); + } + if (V_m < v1 && it < 40) + P = R_TK / (v1 - b_sum) - a_aa_sum / (v1 * (v1 + 2 * b_sum) - b2); + } + } + if (P <= 0) // iterations = -1 + P = 1; + } else + { + if (P < 1e-10) + P = 1e-10; + r3[1] = b_sum - R_TK / P; + r3_12 = r3[1] * r3[1]; + r3[2] = -3.0 * b2 + (a_aa_sum - R_TK * 2.0 * b_sum) / P; + r3[3] = b2 * b_sum + (R_TK * b2 - b_sum * a_aa_sum) / P; + // solve t^3 + rp*t + rq = 0. + // molar volume V_m = t - r3[1] / 3... + rp = r3[2] - r3_12 / 3; + rp3 = rp * rp * rp; + rq = (2.0 * r3_12 * r3[1] - 9.0 * r3[1] * r3[2]) / 27 + r3[3]; + rz = rq * rq / 4 + rp3 / 27; + if (rz >= 0) // Cardono's method... + { + ri = sqrt(rz); + if (ri + rq / 2 <= 0) + { + V_m = pow(ri - rq / 2, one_3) + pow(- ri - rq / 2, one_3) - r3[1] / 3; + } + else + { + ri = - pow(ri + rq / 2, one_3); + V_m = ri - rp / (3.0 * ri) - r3[1] / 3; + } + } + else // use complex plane... + { + ri = sqrt(- rp3 / 27); // rp < 0 + ri1 = acos(- rq / 2 / ri); + V_m = 2.0 * pow(ri, one_3) * cos(ri1 / 3) - r3[1] / 3; + } + } + // calculate the fugacity coefficients... + for (i = 0; i < n_g; i++) + { + phase_ptr = phase_ptrs[i]; + if (phase_ptr->fraction_x == 0.0) + { + phase_ptr->pr_p = 0; + phase_ptr->pr_phi = 1; + phase_ptr->pr_si_f = 0.0; + continue; + } + phase_ptr->pr_p = phase_ptr->fraction_x * P; + rz = P * V_m / R_TK; + A = a_aa_sum * P / (R_TK * R_TK); + B = b_sum * P / R_TK; + B_r = phase_ptr->pr_b / b_sum; + if (rz > B) + { + phi = B_r * (rz - 1) - log(rz - B) + A / (2.828427 * B) * (B_r - 2.0 * phase_ptr->pr_aa_sum2 / a_aa_sum) * + log((rz + 2.41421356 * B) / (rz - 0.41421356 * B)); + phi = (phi > 4.44 ? 4.44 : (phi < -3 ? -3 : phi)); + //if (phi > 4.44) + // phi = 4.44; + } + else + phi = -3.0; // fugacity coefficient > 0.05 + if (/*!strcmp(phase_ptr->name, "H2O(g)") && */phi < -3) + { + // avoid such phi... + phi = -3; + } + phase_ptr->pr_phi = exp(phi); + phase_ptr->pr_si_f = phi / LOG_10; + // for initial equilibrations, adapt log_k of the gas phase... + if (state < REACTION) + { + rho_0 = calc_rho_0(TK - 273.15, P); + calc_dielectrics(TK - 273.15, P); + phase_ptr->lk = calc_lk_phase(phase_ptr, TK, P); + } + phase_ptr->pr_in = true; + } + if (gas_phase_ptr && iterations > 2) + { + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME) + { + gas_phase_ptr->Set_total_p(P); + } + gas_phase_ptr->Set_v_m(V_m); + return (OK); + } + return (V_m); +} + +LDBLE Phreeqc:: +f_Vm(LDBLE v1, void *cookie) +/* ---------------------------------------------------------------------- */ +{ + LDBLE ff; + Phreeqc * pThis; + pThis = (Phreeqc *) cookie; + + ff = v1 * (v1 + 2 * pThis->b_sum) - pThis->b2; + LDBLE dp_dv = -pThis->R_TK / pow(v1 - pThis->b_sum, 2) + + pThis->a_aa_sum * 2 * (v1 + pThis->b_sum) / (ff * ff); + return dp_dv; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +setup_pure_phases(void) +/* ---------------------------------------------------------------------- */ +{ + //LDBLE si_org; +/* + * Fills in data for pure_phase assemglage in unknown structure + */ + + if (use.Get_pp_assemblage_ptr() == NULL) + return (OK); + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); +/* + * Setup unknowns + */ + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + cxxPPassemblageComp * comp_ptr = &(it->second); + int j; + struct phase * phase_ptr = phase_bsearch(it->first.c_str(), &j, FALSE); + assert(phase_ptr); + x[count_unknowns]->type = PP; + x[count_unknowns]->description = string_hsave(comp_ptr->Get_name().c_str()); + x[count_unknowns]->pp_assemblage_comp_name = x[count_unknowns]->description; + x[count_unknowns]->pp_assemblage_comp_ptr = comp_ptr; + x[count_unknowns]->moles = comp_ptr->Get_moles(); + x[count_unknowns]->phase = phase_ptr; + x[count_unknowns]->si = comp_ptr->Get_si(); + //si_org = comp_ptr->Get_si_org(); + /* si_org is used for Peng-Robinson gas, with the fugacity + coefficient added later in adjust_pure_phases, + when rxn_x has been defined for each phase in the model */ + x[count_unknowns]->delta = comp_ptr->Get_delta(); + x[count_unknowns]->dissolve_only = comp_ptr->Get_dissolve_only(); + if (pure_phase_unknown == NULL) + pure_phase_unknown = x[count_unknowns]; + count_unknowns++; + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +adjust_setup_pure_phases(void) +/* ---------------------------------------------------------------------- */ +{ + int i; + struct phase *phase_ptr; + LDBLE si_org, p, t; +/* + * Fills in data for pure_phase assemglage in unknown structure + */ + + if (use.Get_pp_assemblage_ptr() == NULL) + return (OK); +/* + * Adjust si for gases + */ + for (i = 0; i < count_unknowns; i++) + { + std::vector phase_ptrs; + if (x[i]->type == PP) + { + phase_ptr = x[i]->phase; + phase_ptrs.push_back(phase_ptr); + //cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp * ) x[i]->pp_assemblage_comp_ptr; + si_org = comp_ptr->Get_si_org(); + if (phase_ptr->p_c > 0 && phase_ptr->t_c > 0) + { + if (si_org > 3.5) + si_org = 3.5; + p = exp(si_org * LOG_10); + patm_x = p; + t = use.Get_solution_ptr()->Get_tc() + 273.15; + if (!phase_ptr->pr_in || p != phase_ptr->pr_p || t != phase_ptr->pr_tk) + { + calc_PR(phase_ptrs, p, t, 0); + } + x[i]->si = si_org + phase_ptr->pr_si_f; + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +setup_solution(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Fills in data in unknown structure for the solution + */ + struct master *master_ptr; + cxxSolution *solution_ptr; + char *ptr; + std::string token; + struct master_isotope *master_isotope_ptr; + struct phase *phase_ptr; + + solution_ptr = use.Get_solution_ptr(); + count_unknowns = 0; + + /* + * Treat minor isotopes as special in initial solution calculation + */ + if (solution_ptr->Get_initial_data()) + { + std::map::iterator comp_it = solution_ptr->Get_initial_data()->Get_comps().begin(); + for ( ; comp_it != solution_ptr->Get_initial_data()->Get_comps().end(); comp_it++) + { + master_ptr = master_bsearch(comp_it->first.c_str()); + if ((master_ptr != NULL) + && (master_ptr->minor_isotope == TRUE) + && (initial_solution_isotopes == FALSE)) + { + master_isotope_ptr = master_isotope_search(comp_it->first.c_str()); + if (master_isotope_ptr != NULL) + { + master_isotope_ptr->ratio = comp_it->second.Get_input_conc(); + } + } + } + } + cxxNameDouble::iterator it = solution_ptr->Get_totals().begin(); + for ( ; it != solution_ptr->Get_totals().end(); it++) + { + cxxISolutionComp *comp_ptr = NULL; + if (solution_ptr->Get_initial_data()) + { + std::map::iterator comp_it; + comp_it = solution_ptr->Get_initial_data()->Get_comps().find(it->first.c_str()); + comp_ptr = &(comp_it->second); + } + char * temp_desc = string_duplicate(it->first.c_str()); + ptr = temp_desc; + copy_token(token, &ptr); + master_ptr = master_bsearch(token.c_str()); +/* + * Check that total not <= zero + */ + if (it->second <= 0.0) + { + if (strcmp(token.c_str(), "H(1)") != 0 && strcmp(token.c_str(), "E") != 0) + { + free_check_null(temp_desc); + continue; + } + } +/* + * Find master species + */ + master_ptr = master_bsearch(token.c_str()); + if (master_ptr == NULL) + { + error_string = sformatf( + "Master species not in database for %s, skipping element.", + it->first.c_str()); + warning_msg(error_string); + free_check_null(temp_desc); + continue; + } + if (master_ptr->type != AQ) + { + /* solution_ptr->totals[i].skip = TRUE; */ + error_string = sformatf( + "Only aqueous concentrations are allowed in solution data, ignoring %s.", + it->first.c_str()); + warning_msg(error_string); + free_check_null(temp_desc); + continue; + } +/* + * Store list of master species pointers, set master[i].in and master[i].rxn for list + */ + x[count_unknowns]->master = get_list_master_ptrs(ptr, master_ptr); + if (comp_ptr) + { + setup_master_rxn(x[count_unknowns]->master, comp_ptr->Get_pe_reaction()); + } + else + { + setup_master_rxn(x[count_unknowns]->master, "pe"); + } + +/* + * Set default unknown data + */ + x[count_unknowns]->type = MB; + x[count_unknowns]->description = string_hsave(it->first.c_str()); + for (int j = 0; x[count_unknowns]->master[j] != NULL; j++) + { + x[count_unknowns]->master[j]->unknown = x[count_unknowns]; + } + x[count_unknowns]->moles = it->second; +/* + * Set pointers + */ + free_check_null(temp_desc); + temp_desc = string_duplicate(it->first.c_str()); + ptr = temp_desc; + copy_token(token, &ptr); + Utilities::str_tolower(token); + if (strstr(token.c_str(), "alk") != NULL) + { + if (alkalinity_unknown == NULL) + { + x[count_unknowns]->type = ALK; + alkalinity_unknown = x[count_unknowns]; + } + else + { + error_msg("Alkalinity entered more than once.", CONTINUE); + input_error++; + } + } + else if (strcmp(token.c_str(), "c") == 0 || strcmp(token.c_str(), "c(4)") == 0) + { + if (carbon_unknown == NULL) + { + carbon_unknown = x[count_unknowns]; + } + else + { + error_msg("Carbon entered more than once.", CONTINUE); + input_error++; + } + } + else if (strcmp(token.c_str(), "h(1)") == 0) + { + if (ph_unknown == NULL) + { + ph_unknown = x[count_unknowns]; + } + else + { + error_msg("pH entered more than once.", CONTINUE); + input_error++; + } + } + else if (strcmp(token.c_str(), "e") == 0) + { + if (pe_unknown == NULL) + { + pe_unknown = x[count_unknowns]; + } + else + { + error_msg("pe entered more than once.", CONTINUE); + input_error++; + } + } + free_check_null(temp_desc); +/* + * Charge balance unknown + */ + if (comp_ptr && comp_ptr->Get_equation_name().size() > 0) + { + char * temp_eq_name = string_duplicate(comp_ptr->Get_equation_name().c_str()); + ptr = temp_eq_name; + copy_token(token, &ptr); + Utilities::str_tolower(token); + if (strstr(token.c_str(), "charge") != NULL) + { + if (charge_balance_unknown == NULL) + { + charge_balance_unknown = x[count_unknowns]; + x[count_unknowns]->type = CB; + if (charge_balance_unknown == ph_unknown) + { + x[count_unknowns]->moles = solution_ptr->Get_cb(); + } + } + else + { + error_msg("Charge balance specified for more" + " than one species.", CONTINUE); + input_error++; + } + } + else + { +/* + * Solution phase boundaries + */ + int l; + phase_ptr = phase_bsearch(comp_ptr->Get_equation_name().c_str(), &l, FALSE); + if (phase_ptr == NULL) + { + error_string = sformatf( "Expected a mineral name, %s.", + comp_ptr->Get_equation_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + x[count_unknowns]->type = SOLUTION_PHASE_BOUNDARY; + x[count_unknowns]->phase = phase_ptr; + x[count_unknowns]->si = comp_ptr->Get_phase_si(); + /* For Peng-Robinson gas, the fugacity + coefficient is added later in adjust_setup_solution, + when rxn_x has been defined for each phase in the model */ + if (solution_phase_boundary_unknown == NULL) + { + solution_phase_boundary_unknown = x[count_unknowns]; + } + } + free_check_null(temp_eq_name); + } + count_unknowns++; + } +/* + * Set mb_unknown + */ + if (count_unknowns > 0) + mb_unknown = x[0]; +/* + * Special for alkalinity + */ + if (alkalinity_unknown != NULL) + { + if (carbon_unknown != NULL) + { +/* + * pH adjusted to obtain given alkalinity + */ + if (ph_unknown == NULL) + { + output_msg(sformatf("\npH will be adjusted to obtain desired alkalinity.\n\n")); + ph_unknown = alkalinity_unknown; + master_ptr = master_bsearch("H(1)"); + alkalinity_unknown->master[0] = master_ptr; + master_ptr->in = TRUE; + master_ptr->unknown = ph_unknown; + ph_unknown->master[0] = master_ptr; + ph_unknown->description = string_hsave("H(1)"); + } + else + { + error_msg("pH adjustment is needed for alkalinity but" + " charge balance or a phase boundary was also specified.", + CONTINUE); + input_error++; + } +/* + * Carbonate ion adjusted to obtain given alkalintiy + */ + } + else + { + if (alkalinity_unknown->master[0]->s->secondary != NULL) + { + alkalinity_unknown->master[0]->s->secondary->in = TRUE; + alkalinity_unknown->master[0]->s->secondary->unknown = + alkalinity_unknown; + } + else + { + error_msg + ("Error in definition of Alkalinity in SOLUTION_MASTER_SPECIES and SOLUTION_SPECIES.\n\tAlkalinity master species should be same as master species for C(4).", + CONTINUE); + input_error++; + } + } + } + //if (pitzer_model == FALSE && sit_model == FALSE) + { + /* + * Ionic strength + */ + mu_unknown = x[count_unknowns]; + x[count_unknowns]->description = string_hsave("Mu"); + x[count_unknowns]->type = MU; + x[count_unknowns]->number = count_unknowns; + x[count_unknowns]->moles = 0.0; + count_unknowns++; + } + /* + * Activity of water + */ + ah2o_unknown = x[count_unknowns]; + ah2o_unknown->description = string_hsave("A(H2O)"); + ah2o_unknown->type = AH2O; + ah2o_unknown->number = count_unknowns; + ah2o_unknown->master = unknown_alloc_master(); + ah2o_unknown->master[0] = master_bsearch("O"); + ah2o_unknown->master[0]->unknown = ah2o_unknown; + ah2o_unknown->moles = 0.0; + count_unknowns++; + + if (state >= REACTION) + { +/* + + * Reaction: pH for charge balance + */ + ph_unknown = x[count_unknowns]; + ph_unknown->description = string_hsave("pH"); + ph_unknown->type = CB; + ph_unknown->moles = solution_ptr->Get_cb(); + ph_unknown->number = count_unknowns; + ph_unknown->master = unknown_alloc_master(); + ph_unknown->master[0] = s_hplus->primary; + ph_unknown->master[0]->unknown = ph_unknown; + charge_balance_unknown = ph_unknown; + count_unknowns++; +/* + * Reaction: pe for total hydrogen + */ + pe_unknown = x[count_unknowns]; + mass_hydrogen_unknown = x[count_unknowns]; + mass_hydrogen_unknown->description = string_hsave("Hydrogen"); + mass_hydrogen_unknown->type = MH; +#ifdef COMBINE + mass_hydrogen_unknown->moles = + solution_ptr->Get_total_h() - 2 * solution_ptr->Get_total_o(); +#else + mass_hydrogen_unknown->moles = solution_ptr->total_h; +#endif + mass_hydrogen_unknown->number = count_unknowns; + mass_hydrogen_unknown->master = unknown_alloc_master(); + mass_hydrogen_unknown->master[0] = s_eminus->primary; + mass_hydrogen_unknown->master[0]->unknown = mass_hydrogen_unknown; + count_unknowns++; +/* + * Reaction H2O for total oxygen + */ + mass_oxygen_unknown = x[count_unknowns]; + mass_oxygen_unknown->description = string_hsave("Oxygen"); + mass_oxygen_unknown->type = MH2O; + mass_oxygen_unknown->moles = solution_ptr->Get_total_o(); + mass_oxygen_unknown->number = count_unknowns; + mass_oxygen_unknown->master = unknown_alloc_master(); + mass_oxygen_unknown->master[0] = s_h2o->primary; + count_unknowns++; + } +/* + * Validity tests + */ + if ((ph_unknown != NULL) && + (ph_unknown == charge_balance_unknown) + && (alkalinity_unknown != NULL)) + { + error_msg("pH adustment cannot attain charge balance" + " when alkalinity is fixed.", CONTINUE); + input_error++; + } + if ((alkalinity_unknown != NULL) && + (alkalinity_unknown->type == CB || + alkalinity_unknown->type == SOLUTION_PHASE_BOUNDARY)) + { + error_msg("Alkalinity cannot be used with charge balance" + " or solution phase boundary constraints.", CONTINUE); + input_error++; + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +adjust_setup_solution(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Fills in data in unknown structure for the solution + */ + int i; + struct phase *phase_ptr; + LDBLE p, t; + + for (i = 0; i < count_unknowns; i++) + { + std::vector phase_ptrs; + if (x[i]->type == SOLUTION_PHASE_BOUNDARY) + { + x[count_unknowns]->type = SOLUTION_PHASE_BOUNDARY; + phase_ptr = x[i]->phase; + phase_ptrs.push_back(phase_ptr); + if (phase_ptr->p_c > 0 && phase_ptr->t_c > 0) + { + if (x[i]->si > 3.5) + x[i]->si = 3.5; + p = exp(x[i]->si * LOG_10); + patm_x = p; + t = use.Get_solution_ptr()->Get_tc() + 273.15; + if (!phase_ptr->pr_in || p != phase_ptr->pr_p || t != phase_ptr->pr_tk) + { + calc_PR(phase_ptrs, p, t, 0); + } + x[i]->si += phase_ptr->pr_si_f; + } + } + } + return (OK); + +} +/* ---------------------------------------------------------------------- */ +struct master ** Phreeqc:: +unknown_alloc_master(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Allocates space for a list of 2 master pointers + */ + struct master **master_ptr; + + master_ptr = (struct master **) PHRQ_malloc(2 * sizeof(struct master *)); + if (master_ptr == NULL) + { + malloc_error(); + } + else + { + master_ptr[0] = NULL; + master_ptr[1] = NULL; + } + return (master_ptr); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +setup_unknowns(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Counts unknowns and allocates space for unknown structures + */ + int i; + cxxSolution *solution_ptr; + + solution_ptr = use.Get_solution_ptr(); +/* + * Calculate maximum number of unknowns + */ + max_unknowns = 0; +/* + * Count mass balance in solution + */ + if (solution_ptr->Get_initial_data()) + { + max_unknowns += (int) solution_ptr->Get_initial_data()->Get_comps().size(); + } + else + { + max_unknowns += (int) solution_ptr->Get_totals().size(); + } +/* + * Add 5 for ionic strength, activity of water, charge balance, total H, total O + */ + max_unknowns += 5; +/* + * Count pure phases + */ + if (use.Get_pp_assemblage_ptr() != NULL) + { + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + max_unknowns += (int) pp_assemblage_ptr->Get_pp_assemblage_comps().size(); + } +/* + * Count exchange + */ + if (use.Get_exchange_ptr() != NULL) + { + cxxExchange *exchange_ptr = use.Get_exchange_ptr(); + for (size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++) + { + cxxExchComp & comp_ref = exchange_ptr->Get_exchange_comps()[j]; + cxxNameDouble nd(comp_ref.Get_totals()); + cxxNameDouble::iterator it = nd.begin(); + for ( ; it != nd.end(); it++) + { + element * elt_ptr = element_store(it->first.c_str()); + if (elt_ptr == NULL || elt_ptr->master == NULL) + { + error_string = sformatf( + "Master species missing for element %s", + it->first.c_str()); + error_msg(error_string, STOP); + } + if (elt_ptr->master->type == EX) + { + max_unknowns++; + } + } + } + } +/* + * Count surfaces + */ + if (use.Get_surface_ptr() != NULL) + { + if (use.Get_surface_ptr()->Get_type() != cxxSurface::CD_MUSIC) + { + max_unknowns += (int) use.Get_surface_ptr()->Get_surface_comps().size() + + (int) use.Get_surface_ptr()->Get_surface_charges().size(); + } + else + { + max_unknowns += (int) (use.Get_surface_ptr()->Get_surface_comps().size() + + 4 * (int) use.Get_surface_ptr()->Get_surface_charges().size()); + } + } +/* + * Count gas components + */ + if (use.Get_gas_phase_ptr() != NULL) + { + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME && + (gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume) && numerical_fixed_volume) + { + max_unknowns += (int) gas_phase_ptr->Get_gas_comps().size(); + } + else + { + max_unknowns++; + } + } +/* + * Count solid solutions + */ + if (use.Get_ss_assemblage_ptr() != NULL) + { + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t i = 0; i < ss_ptrs.size(); i++) + { + max_unknowns += (int) ss_ptrs[i]->Get_ss_comps().size(); + } + } +/* + * Pitzer/Sit + */ + max_unknowns++; + if (pitzer_model == TRUE || sit_model == TRUE) + { + max_unknowns += count_s; + } + +/* + * Allocate space for pointer array and structures + */ + + space((void **) ((void *) &x), INIT, &max_unknowns, + sizeof(struct unknown *)); + for (i = 0; i < max_unknowns; i++) + { + x[i] = (struct unknown *) unknown_alloc(); + x[i]->number = i; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +store_dn(int k, LDBLE * source, int row, LDBLE coef_in, LDBLE * gamma_source) +/* ---------------------------------------------------------------------- */ +{ +/* + * Stores the terms for d moles of species k in solution into row, multiplied + * by coef_in + */ + int col; + LDBLE coef; + struct rxn_token *rxn_ptr; + struct master *master_ptr; + + if (equal(coef_in, 0.0, TOL) == TRUE) + { + return (OK); + } +/* Gamma term for d molality of species */ +/* Note dg includes molality as a factor */ + + row = row * (count_unknowns + 1); + if (s[k]->type != SURF && s[k] != s_h2o) + { + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d\n", + "Activity coefficient", (double) (-1.0 * coef_in), + row / (count_unknowns + 1), mu_unknown->number)); + } + /* mu term */ + if (gamma_source != NULL) + { + store_jacob(gamma_source, &array[row + mu_unknown->number], + -1.0 * coef_in); + } + } +/* + * Mass of water factor + */ + if (mass_oxygen_unknown != NULL && s[k]->type != EX && s[k]->type != SURF) + { + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d\n", + mass_oxygen_unknown->master[0]->s->name, + (double) coef_in, row / (count_unknowns + 1), + mass_oxygen_unknown->number)); + } + store_jacob(source, &(array[row + mass_oxygen_unknown->number]), + coef_in); + } + if (s[k] == s_h2o) + return (OK); + for (rxn_ptr = s[k]->rxn_x->token + 1; rxn_ptr->s != NULL; rxn_ptr++) + { + if (rxn_ptr->s->secondary != NULL + && rxn_ptr->s->secondary->in == TRUE) + { + master_ptr = rxn_ptr->s->secondary; + } + else + { + master_ptr = rxn_ptr->s->primary; + } + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%s\n", master_ptr->s->name)); + } + if (master_ptr == NULL ||master_ptr->unknown == NULL) + continue; + col = master_ptr->unknown->number; + coef = coef_in * rxn_ptr->coef; + store_jacob(source, &(array[row + col]), coef); + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d\n", + master_ptr->s->name, (double) coef, + row / (count_unknowns + 1), col)); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +store_jacob(LDBLE * source, LDBLE * target, LDBLE coef) +/* ---------------------------------------------------------------------- */ +{ +/* + * Adds a new item to either sum_jacob1 or sum_jacob2 + * If coef is 1.0, adds to sum_jacob1, which does not require a multiply + * Otherwise, adds to sum_jacob2, which allows multiply by coef + */ + if (equal(coef, 1.0, TOL) == TRUE) + { + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\tjacob1 %d\n", count_sum_jacob1)); + } + sum_jacob1[count_sum_jacob1].source = source; + sum_jacob1[count_sum_jacob1++].target = target; + /* Check space */ + if (count_sum_jacob1 >= max_sum_jacob1) + { + space((void **) ((void *) &sum_jacob1), count_sum_jacob1, + &max_sum_jacob1, sizeof(struct list1)); + } + } + else + { + if (debug_prep == TRUE) + { + output_msg(sformatf( "\t\tjacob2 %d\n", count_sum_jacob2)); + } + sum_jacob2[count_sum_jacob2].source = source; + sum_jacob2[count_sum_jacob2].target = target; + sum_jacob2[count_sum_jacob2++].coef = coef; + /* Check space */ + if (count_sum_jacob2 >= max_sum_jacob2) + { + space((void **) ((void *) &sum_jacob2), count_sum_jacob2, + &max_sum_jacob2, sizeof(struct list2)); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +store_jacob0(int row, int column, LDBLE coef) +/* ---------------------------------------------------------------------- */ +{ +/* + * Stores in list a constant coef which will be added into jacobian array + */ + sum_jacob0[count_sum_jacob0].target = + &(array[row * (count_unknowns + 1) + column]); + sum_jacob0[count_sum_jacob0++].coef = coef; + /* Check space */ + if (count_sum_jacob0 >= max_sum_jacob0) + { + space((void **) ((void *) &sum_jacob0), count_sum_jacob0, + &max_sum_jacob0, sizeof(struct list0)); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +store_mb(LDBLE * source, LDBLE * target, LDBLE coef) +/* ---------------------------------------------------------------------- */ +{ +/* + * Adds item to list sum_mb1 or sum_mb2 + * If coef is 1.0, adds to sum_mb1, which does not require a multiply + * else, adds to sum_mb2, which will multiply by coef + */ + if (equal(coef, 1.0, TOL) == TRUE) + { + sum_mb1[count_sum_mb1].source = source; + sum_mb1[count_sum_mb1++].target = target; + if (count_sum_mb1 >= max_sum_mb1) + { + space((void **) ((void *) &sum_mb1), + count_sum_mb1 + count_trxn + 4, &max_sum_mb1, + sizeof(struct list1)); + } + } + else + { + sum_mb2[count_sum_mb2].source = source; + sum_mb2[count_sum_mb2].coef = coef; + sum_mb2[count_sum_mb2++].target = target; + if (count_sum_mb2 >= max_sum_mb2) + { + space((void **) ((void *) &sum_mb2), count_sum_mb2, + &max_sum_mb2, sizeof(struct list2)); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +store_sum_deltas(LDBLE * source, LDBLE * target, LDBLE coef) +/* ---------------------------------------------------------------------- */ +{ +/* + * List sum_delta is summed to determine the change in the mass of + * each element due to mass transfers of minerals, changes show up + * in x[i]->delta. These may be multiplied by a factor under some + * situations where the entire calculated step is not taken + */ + sum_delta[count_sum_delta].source = source; + sum_delta[count_sum_delta].target = target; + sum_delta[count_sum_delta++].coef = coef; + /* Check space */ + if (count_sum_delta >= max_sum_delta) + { + space((void **) ((void *) &sum_delta), count_sum_delta, + &max_sum_delta, sizeof(struct list2)); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +switch_bases(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Check if activity of first master species is predominant among activities of + * secondary master species included in mass balance. + */ + int i, j; + int first; + int return_value; + LDBLE la, la1; + struct master *master_ptr; + + return_value = FALSE; + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type != MB) + continue; + if (x[i]->type == PITZER_GAMMA) + break; + first = 0; + la = x[i]->master[0]->s->la; + for (j = 1; x[i]->master[j] != NULL; j++) + { + la1 = x[i]->master[j]->s->lm + x[i]->master[j]->s->lg; + if (first == 0 && la1 > la + 10.) + { + la = la1; + first = j; + } + else if (first != 0 && la1 > la) + { + la = la1; + first = j; + } + } + if (first != 0) + { + master_ptr = x[i]->master[0]; + x[i]->master[0] = x[i]->master[first]; + x[i]->master[0]->in = TRUE; + x[i]->master[first] = master_ptr; + x[i]->master[first]->in = REWRITE; +/* + fprintf(stderr, "Switching bases to %s.\tIteration %d\n", + x[i]->master[0]->s->name, iterations, la, x[i]->master[0]->s->la); + */ + x[i]->master[0]->s->la = la; + x[i]->la = la; + log_msg(sformatf( "Switching bases to %s.\tIteration %d\n", + x[i]->master[0]->s->name, iterations)); + return_value = TRUE; + } + } + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_redox(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Write pe redox reactions (rxn in struct pe_data) in terms of master species + * defined in analytical data + * + */ + std::string token, tok1, tok2; + struct master *master_ptr1, *master_ptr2; +/* + * Keep valences of oxygen and hydrogen in model, if not already in + */ + for (int i = 0; i < count_master; i++) + { + if (master[i]->primary == TRUE && + (master[i]->s == s_hplus || master[i]->s == s_h2o)) + { + int j = i + 1; + while (j < count_master && master[j]->elt->primary == master[i]) + { + if (master[j]->in == FALSE && master[j]->s != master[i]->s) + { + master[j]->in = REWRITE; + master[j]->pe_rxn = master[i]->pe_rxn; + } + j++; + } + } + } +/* + * Writes equations for e- for each redox couple used in solution n + */ + std::map::iterator it; + for (it = pe_x.begin(); it != pe_x.end(); it++) + { + if (strcmp_nocase(it->first.c_str(), "pe") == 0) + { + cxxChemRxn temp_rxn(s_eminus->rxn); + it->second = temp_rxn; + } + else + { + token = it->first; + replace("/", " ", token); + std::string::iterator b = token.begin(); + std::string::iterator e = token.end(); + +/* + * Get redox states and elements from redox couple + */ + CParser::copy_token(tok1, b, e); + CParser::copy_token(tok2, b, e); +/* + * Find master species + */ + master_ptr1 = master_bsearch(tok1.c_str()); + master_ptr2 = master_bsearch(tok2.c_str()); + if (master_ptr1 != NULL && master_ptr2 != NULL) + { + rewrite_master_to_secondary(master_ptr1, master_ptr2); +/* + * Rewrite equation to e- + */ + trxn_swap("e-"); + } + else + { + error_string = sformatf( + "Cannot find master species for redox couple, %s.", + it->first.c_str()); + error_msg(error_string, STOP); + } + if (inout() == FALSE) + { + error_string = sformatf( + "Analytical data missing for redox couple, %s\n\t Using pe instead.", + it->first.c_str()); + warning_msg(error_string); + cxxChemRxn temp_rxn(s_eminus->rxn); + it->second = temp_rxn; + } + else + { + struct reaction *rxn = rxn_alloc(count_trxn + 1); + trxn_copy(rxn); + cxxChemRxn temp_rxn(rxn); + it->second = temp_rxn; + rxn_free(rxn); + } + } + } +/* + * Rewrite equations to master species that are "in" the model + */ + for (it = pe_x.begin(); it != pe_x.end(); it++) + { + count_trxn = 0; + trxn_add(it->second, 1.0, FALSE); + if (write_mass_action_eqn_x(CONTINUE) == FALSE) + { + error_string = sformatf( "Could not rewrite redox " + "couple equation for %s\n\t Possibly missing data for one " + "of the redox states.", it->first.c_str()); + warning_msg(error_string); + error_string = sformatf( "Using pe instead of %s.", + it->first.c_str()); + warning_msg(error_string); + cxxChemRxn temp_rxn(s_eminus->rxn); + it->second = temp_rxn; + } + else + { + struct reaction *rxn = rxn_alloc(count_trxn + 1); + trxn_copy(rxn); + cxxChemRxn temp_rxn(rxn); + it->second = temp_rxn; + rxn_free(rxn); + } + } + + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +write_mb_eqn_x(void) +/* ---------------------------------------------------------------------- */ +{ + int count, repeat; + int i, count_rxn_orig; + int j, k; + struct master *master_ptr; +/* + * Rewrite any secondary master species flagged REWRITE + * Don`t add in any pe reactions + */ + count = 0; + repeat = TRUE; + while (repeat == TRUE) + { + count++; + if (count > MAX_ADD_EQUATIONS) + { + std::string name; + name = "Unknown"; + if (trxn.token[0].s != NULL) + { + name = trxn.token[0].s->name; + } + error_string = sformatf( "Could not reduce equation " + "to primary and secondary species that are " + "in the model. Species: %s.", name.c_str()); + error_msg(error_string, CONTINUE); + return (ERROR); + } + repeat = FALSE; + count_rxn_orig = count_trxn; + for (i = 1; i < count_rxn_orig; i++) + { + if (trxn.token[i].s->secondary == NULL) + continue; + if (trxn.token[i].s->secondary->in == REWRITE) + { + repeat = TRUE; + trxn_add(trxn.token[i].s->secondary->rxn_secondary, + trxn.token[i].coef, FALSE); + } + } + trxn_combine(); + } +/* + * + */ + count_elts = 0; + paren_count = 0; + for (i = 1; i < count_trxn; i++) + { + j = count_elts; + char * temp_name = string_duplicate(trxn.token[i].s->name); + char * ptr = temp_name; + get_elts_in_species(&ptr, trxn.token[i].coef); + free_check_null(temp_name); + for (k = j; k < count_elts; k++) + { + if (trxn.token[i].s->secondary != NULL) + { + master_ptr = trxn.token[i].s->secondary->elt->primary; + } + else + { + master_ptr = trxn.token[i].s->primary; + } + if (elt_list[k].elt == master_ptr->elt) + { + elt_list[k].coef = 0.0; + break; + } + } + if (trxn.token[i].s->secondary == NULL) + { + char * temp_name = string_duplicate(trxn.token[i].s->primary->elt->name); + char *ptr = temp_name; + get_secondary_in_species(&ptr, trxn.token[i].coef); + free_check_null(temp_name); + } + else + { + char * temp_name = string_duplicate(trxn.token[i].s->secondary->elt->name); + ptr = temp_name; + get_secondary_in_species(&ptr, trxn.token[i].coef); + free_check_null(temp_name); + } + } + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +write_mb_for_species_list(int n) +/* ---------------------------------------------------------------------- */ +{ +/* + * Sets up data to add to species_list + * Original secondary redox states are retained + */ + int i; +/* + * Start with secondary reaction + */ + count_trxn = 0; + trxn_add(s[n]->rxn_s, 1.0, FALSE); +/* + * Copy to elt_list + */ + count_elts = 0; + paren_count = 0; + for (i = 1; i < count_trxn; i++) + { + if (trxn.token[i].s->secondary == NULL) + { + char * temp_name = string_duplicate(trxn.token[i].s->primary->elt->name); + char * ptr = temp_name; + get_secondary_in_species(&ptr, trxn.token[i].coef); + free_check_null(temp_name); + } + else + { + char * temp_name = string_duplicate(trxn.token[i].s->secondary->elt->name); + char * ptr = temp_name; + if (get_secondary_in_species(&ptr, trxn.token[i].coef) == ERROR) + { + input_error++; + error_string = sformatf( "Error parsing %s.", trxn.token[i].s->secondary->elt->name); + error_msg(error_string, CONTINUE); + } + free_check_null(temp_name); + } + } + for (i = 0; i < count_elts; i++) + { + if (strcmp(elt_list[i].elt->name, "O(-2)") == 0) + { + if (count_elts >= max_elts) + { + space((void **) ((void *) &elt_list), count_elts, &max_elts, + sizeof(struct elt_list)); + } + elt_list[count_elts].elt = element_h_one; + elt_list[count_elts].coef = elt_list[i].coef * 2; + count_elts++; + } + } + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + } + s[n]->next_sys_total = + (struct elt_list *) free_check_null(s[n]->next_sys_total); + s[n]->next_sys_total = elt_list_save(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +write_phase_sys_total(int n) +/* ---------------------------------------------------------------------- */ +{ +/* + * Sets up data to add to species_list + * Original secondary redox states are retained + */ + int i; +/* + * Start with secondary reaction + */ + count_trxn = 0; + trxn_add_phase(phases[n]->rxn_s, 1.0, FALSE); +/* + * Copy to elt_list + */ + count_elts = 0; + paren_count = 0; + for (i = 1; i < count_trxn; i++) + { + if (trxn.token[i].s->secondary == NULL) + { + char * temp_name = string_duplicate(trxn.token[i].s->primary->elt->name); + char *ptr = temp_name; + get_secondary_in_species(&ptr, trxn.token[i].coef); + free_check_null(temp_name); + } + else + { + char * temp_name = string_duplicate(trxn.token[i].s->secondary->elt->name); + char *ptr = temp_name; + get_secondary_in_species(&ptr, trxn.token[i].coef); + free_check_null(temp_name); + } + } + for (i = 0; i < count_elts; i++) + { + if (strcmp(elt_list[i].elt->name, "O(-2)") == 0) + { + if (count_elts >= max_elts) + { + space((void **) ((void *) &elt_list), count_elts, &max_elts, + sizeof(struct elt_list)); + } + elt_list[count_elts].elt = element_h_one; + elt_list[count_elts].coef = elt_list[i].coef * 2; + count_elts++; + } + } + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + } + phases[n]->next_sys_total = + (struct elt_list *) free_check_null(phases[n]->next_sys_total); + phases[n]->next_sys_total = elt_list_save(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_delta_v(reaction *r_ptr, bool phase) +/* ---------------------------------------------------------------------- */ +{ +/* calculate delta_v from molar volumes */ +//dlp + LDBLE d_v = 0.0; + + if (phase) + { + /* for phases: reactants have coef's < 0, products have coef's > 0, v.v. for species */ + for (size_t i = 1; r_ptr->token[i].s /*|| r_ptr->token[i].s*/ ; i++) + { + //if (!r_ptr->token[i].s) + // continue; + //if (!strcmp(r_ptr->token[i].s->name, "H+")) + // continue; + //if (!strcmp(r_ptr->token[i].s->name, "e-")) + // continue; + //else if (r_ptr->token[i].s->logk[vm_tc]) + d_v += r_ptr->token[i].coef * r_ptr->token[i].s->logk[vm_tc]; + } + } + else + { + for (size_t i = 0; r_ptr->token[i].name /*|| r_ptr->token[i].s*/ ; i++) + { + if (!r_ptr->token[i].s) + continue; + //if (!strcmp(r_ptr->token[i].s->name, "H+")) + // continue; + //if (!strcmp(r_ptr->token[i].s->name, "e-")) + // continue; + //else if (r_ptr->token[i].s->logk[vm_tc]) + d_v -= r_ptr->token[i].coef * r_ptr->token[i].s->logk[vm_tc]; + } + } + return d_v; +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_lk_phase(phase *p_ptr, LDBLE TK, LDBLE pa) +/* ---------------------------------------------------------------------- */ +{ +/* + * calculate log_k for a single phase, correct for pressure + * see calc_vm (below) for details. + */ + + reaction *r_ptr = (p_ptr->rxn_x ? p_ptr->rxn_x :\ + (p_ptr->rxn_s ? p_ptr->rxn_s : NULL)); + if (!r_ptr) + return 0.0; + if (!r_ptr->logk[vm0]) // in case Vm of the phase is 0... + return k_calc(r_ptr->logk, TK, pa * PASCAL_PER_ATM); + + LDBLE tc = TK - 273.15; + LDBLE pb_s = 2600. + pa * 1.01325, TK_s = tc + 45.15, sqrt_mu = sqrt(mu_x); + LDBLE d_v = 0.0; + species * s_ptr; + + for (size_t i = 0; r_ptr->token[i].name; i++) + { + if (!r_ptr->token[i].s) + continue; + s_ptr = r_ptr->token[i].s; + //if (!strcmp(s_ptr->name, "H+")) + if (s_ptr == s_hplus) + continue; + //if (!strcmp(s_ptr->name, "e-")) + if (s_ptr == s_eminus) + continue; + //if (!strcmp(s_ptr->name, "H2O")) + if (s_ptr == s_h2o) + { + d_v += r_ptr->token[i].coef * 18.016 / calc_rho_0(tc, pa); + continue; + } + else if (s_ptr->logk[vma1]) + { + /* supcrt volume at I = 0... */ + d_v += r_ptr->token[i].coef * + (s_ptr->logk[vma1] + s_ptr->logk[vma2] / pb_s + + (s_ptr->logk[vma3] + s_ptr->logk[vma4] / pb_s) / TK_s - + s_ptr->logk[wref] * QBrn); + //if (dgdP && s_ptr->z) + //{ + // LDBLE re = s_ptr->z * s_ptr->z / (s_ptr->logk[wref] / 1.66027e5 + s_ptr->z / 3.082); + // LDBLE Z3 = fabs(pow(s_ptr->z, 3)) / re / re - s_ptr->z / 9.498724; + // d_v += r_ptr->token[i].coef * ZBrn * 1.66027e5 * Z3 * dgdP; + //} + if (s_ptr->z) + { + /* the ionic strength term * I^0.5... */ + if (s_ptr->logk[b_Av] < 1e-5) + d_v += s_ptr->z * s_ptr->z * 0.5 * DH_Av * sqrt_mu; + else + { + /* limit the Debye-Hueckel slope by b... */ + d_v += s_ptr->z * s_ptr->z * 0.5 * DH_Av * + sqrt_mu / (1 + s_ptr->logk[b_Av] * DH_B * sqrt_mu); + } + /* plus the volume terms * I... */ + if (s_ptr->logk[vmi1] != 0.0 || s_ptr->logk[vmi2] != 0.0 || s_ptr->logk[vmi3] != 0.0) + { + LDBLE bi = s_ptr->logk[vmi1] + s_ptr->logk[vmi2] / TK_s + s_ptr->logk[vmi3] * TK_s; + if (s_ptr->logk[vmi4] == 1.0) + d_v += bi * mu_x; + else + d_v += bi * pow(mu_x, s_ptr->logk[vmi4]); + } + } + } + else if (s_x[i]->millero[0]) + { + /* Millero volume at I = 0... */ + d_v += s_ptr->millero[0] + tc * (s_ptr->millero[1] + tc * s_ptr->millero[2]); + if (s_ptr->z) + { + /* the ionic strength terms... */ + d_v += s_ptr->z * s_ptr->z * 0.5 * DH_Av * sqrt_mu + + (s_ptr->millero[3] + tc * (s_ptr->millero[4] + tc * s_ptr->millero[5])) * mu_x; + } + } + else + continue; + } + d_v -= p_ptr->logk[vm0]; + r_ptr->logk[delta_v] = d_v; + if (r_ptr->token[0].name && !strcmp(r_ptr->token[0].name, "H2O(g)")) + r_ptr->logk[delta_v] = 0.0; + + return k_calc(r_ptr->logk, TK, pa * PASCAL_PER_ATM); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_vm(LDBLE tc, LDBLE pa) +/* ---------------------------------------------------------------------- */ +{ +/* + * Calculate molar volumes for aqueous species with a Redlich type eqn: + Vm = Vm0(tc) + (Av / 2) * z^2 * I^0.5 + coef(tc) * I^(b4). + * Vm0(tc) is calc'd using supcrt parms, or from millero[0] + millero[1] * tc + millero[2] * tc^2 + * for Av * z^2 * I^0.5, see Redlich and Meyer, Chem. Rev. 64, 221. + Av is in (cm3/mol)(mol/kg)^-0.5, = DH_Av. + If b_Av != 0, the extended DH formula is used: I^0.5 /(1 + b_Av * DH_B * I^0.5). + DH_Av and DH_B are from calc_dielectrics(tc, pa). + * coef(tc) = logk[vmi1] + logk[vmi2] / (TK - 228) + logk[vmi3] * (TK - 228). + * b4 = logk[vmi4], or + * coef(tc) = millero[3] + millero[4] * tc + millero[5] * tc^2 + */ + LDBLE pb_s = 2600. + pa * 1.01325, TK_s = tc + 45.15, sqrt_mu = sqrt(mu_x); + for (int i = 0; i < count_s_x; i++) + { + //if (!strcmp(s_x[i]->name, "H2O")) + if (s_x[i] == s_h2o) + { + s_x[i]->logk[vm_tc] = 18.016 / rho_0; + continue; + } + if (s_x[i]->logk[vma1]) + { + /* supcrt volume at I = 0... */ + s_x[i]->rxn_x->logk[vm_tc] = s_x[i]->logk[vma1] + s_x[i]->logk[vma2] / pb_s + + (s_x[i]->logk[vma3] + s_x[i]->logk[vma4] / pb_s) / TK_s - + s_x[i]->logk[wref] * QBrn; + /* A (small) correction by Shock et al., 1992, for 155 < tc < 255, P_sat < P < 1e3. + The vma1..a4 and wref numbers are refitted for major cations and anions on xpts, + probably invalidates the correction. */ + //if (dgdP && s_x[i]->z) + //{ + // LDBLE re = s_x[i]->z * s_x[i]->z / (s_x[i]->logk[wref] / 1.66027e5 + s_x[i]->z / 3.082); + // LDBLE Z3 = fabs(pow(s_x[i]->z, 3)) / re / re - s_x[i]->z / 9.498724; + // s_x[i]->rxn_x->logk[vm_tc] += ZBrn * 1.66027e5 * Z3 * dgdP; + //} + if (s_x[i]->z) + { + /* the ionic strength term * I^0.5... */ + if (s_x[i]->logk[b_Av] < 1e-5) + s_x[i]->rxn_x->logk[vm_tc] += s_x[i]->z * s_x[i]->z * 0.5 * DH_Av * sqrt_mu; + else + { + /* limit the Debye-Hueckel slope by b... */ + /* pitzer... */ + //s_x[i]->rxn_x->logk[vm_tc] += s_x[i]->z * s_x[i]->z * 0.5 * DH_Av * + // log(1 + s_x[i]->logk[b_Av] * sqrt(mu_x)) / s_x[i]->logk[b_Av]; + /* extended DH... */ + s_x[i]->rxn_x->logk[vm_tc] += s_x[i]->z * s_x[i]->z * 0.5 * DH_Av * + sqrt_mu / (1 + s_x[i]->logk[b_Av] * DH_B * sqrt_mu); + } + /* plus the volume terms * I... */ + if (s_x[i]->logk[vmi1] != 0.0 || s_x[i]->logk[vmi2] != 0.0 || s_x[i]->logk[vmi3] != 0.0) + { + LDBLE bi = s_x[i]->logk[vmi1] + s_x[i]->logk[vmi2] / TK_s + s_x[i]->logk[vmi3] * TK_s; + if (s_x[i]->logk[vmi4] == 1.0) + s_x[i]->rxn_x->logk[vm_tc] += bi * mu_x; + else + s_x[i]->rxn_x->logk[vm_tc] += bi * pow(mu_x, s_x[i]->logk[vmi4]); + } + } + } + else if (s_x[i]->millero[0]) + { + /* Millero volume at I = 0... */ + s_x[i]->rxn_x->logk[vm_tc] = s_x[i]->millero[0] + tc * (s_x[i]->millero[1] + tc * s_x[i]->millero[2]); + if (s_x[i]->z) + { + /* the ionic strength terms... */ + s_x[i]->rxn_x->logk[vm_tc] += s_x[i]->z * s_x[i]->z * 0.5 * DH_Av * sqrt_mu + + (s_x[i]->millero[3] + tc * (s_x[i]->millero[4] + tc * s_x[i]->millero[5])) * mu_x; + } + } + else + continue; + + /* for calculating delta_v of the reaction... */ + s_x[i]->logk[vm_tc] = s_x[i]->rxn_x->logk[vm_tc]; + } + return OK; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +k_temp(LDBLE tc, LDBLE pa) /* pa - pressure in atm */ +/* ---------------------------------------------------------------------- */ +{ +/* + * Calculates log k's for all species and pure_phases + */ + + if (tc == current_tc && pa == current_pa && ((fabs(mu_x - current_mu) < 1e-3 * mu_x) || !mu_terms_in_logk)) + return OK; + + int i; + LDBLE tempk = tc + 273.15; +/* + * Calculate log k for all aqueous species + */ + /* calculate relative molar volumes for tc... */ + rho_0 = calc_rho_0(tc, pa); + + pa = patm_x; + calc_dielectrics(tc, pa); + + calc_vm(tc, pa); + + mu_terms_in_logk = false; + for (i = 0; i < count_s_x; i++) + { + //if (s_x[i]->rxn_x->logk[vm_tc]) + /* calculate delta_v for the reaction... */ + s_x[i]->rxn_x->logk[delta_v] = calc_delta_v(s_x[i]->rxn_x, false); + if (tc == current_tc && s_x[i]->rxn_x->logk[delta_v] == 0) + continue; + mu_terms_in_logk = true; + s_x[i]->lk = k_calc(s_x[i]->rxn_x->logk, tempk, pa * PASCAL_PER_ATM); + } +/* + * Calculate log k for all pure phases + */ + for (i = 0; i < count_phases; i++) + { + if (phases[i]->in == TRUE) + { + + phases[i]->rxn_x->logk[delta_v] = calc_delta_v(phases[i]->rxn_x, true) - + phases[i]->logk[vm0]; + if (phases[i]->rxn_x->logk[delta_v]) + mu_terms_in_logk = true; + phases[i]->lk = k_calc(phases[i]->rxn_x->logk, tempk, pa * PASCAL_PER_ATM); + + } + } +/* + * Calculate miscibility gaps for solid solutions + */ + if (use.Get_ss_assemblage_ptr() != NULL) + { + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t i = 0; i < ss_ptrs.size(); i++) + { + if (fabs(tempk - ss_ptrs[i]->Get_tk()) > 0.01) + { + ss_prep(tempk, ss_ptrs[i], FALSE); + } + } + } + + current_tc = tc; + current_pa = pa; + current_mu = mu_x; + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +k_calc(LDBLE * l_logk, LDBLE tempk, LDBLE presPa) +/* ---------------------------------------------------------------------- */ +{ + /* + * Calculates log k at specified temperature and pressure + * Returns calculated log k. + * + * delta_v is in cm3/mol. + */ + + /* Molar energy */ + LDBLE me = tempk * R_KJ_DEG_MOL; + + /* Pressure difference */ + LDBLE delta_p = presPa - REF_PRES_PASCAL; + + /* Calculate new log k value for this temperature and pressure */ + LDBLE lk = l_logk[logK_T0] + - l_logk[delta_h] * (298.15 - tempk) / (LOG_10 * me * 298.15) + + l_logk[T_A1] + + l_logk[T_A2] * tempk + + l_logk[T_A3] / tempk + + l_logk[T_A4] * log10(tempk) + + l_logk[T_A5] / (tempk * tempk) + + l_logk[T_A6] * tempk * tempk; + if (delta_p > 0) + /* cm3 * J /mol = 1e-9 m3 * kJ /mol */ + lk -= l_logk[delta_v] * 1E-9 * delta_p / (LOG_10 * me); + return lk; +} + + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +save_model(void) +/* ---------------------------------------------------------------------- */ +{ + int i; +/* + * save temperature + */ + last_model.temperature = tc_x; +/* + * save pressure + */ + last_model.pressure = patm_x; +/* + * mark master species + */ + for (i = 0; i < count_master; i++) + { + master[i]->last_model = FALSE; + if (master[i]->total > 0) + { + if (master[i]->primary == TRUE) + { + master[i]->last_model = TRUE; + } + else + { + /* mark primary master */ + master[i]->s->secondary->elt->primary->last_model = TRUE; + } + } + } +/* + * save list of phase pointers for gas phase + */ + last_model.gas_phase = + (struct phase **) free_check_null(last_model.gas_phase); + if (use.Get_gas_phase_ptr() != NULL) + { + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + last_model.count_gas_phase = (int) gas_phase_ptr->Get_gas_comps().size(); + last_model.gas_phase = + (struct phase **) PHRQ_malloc((size_t) last_model.count_gas_phase * + sizeof(struct phase *)); + if (last_model.gas_phase == NULL) + malloc_error(); + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + int k; + struct phase *phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str() , &k, FALSE); + assert(phase_ptr); + last_model.gas_phase[i] = phase_ptr; + } + } + else + { + last_model.count_gas_phase = 0; + last_model.gas_phase = NULL; + } +/* + * save list of names of solid solutions + */ + last_model.ss_assemblage = + (const char **) free_check_null(last_model.ss_assemblage); + if (use.Get_ss_assemblage_ptr() != NULL) + { + size_t count_ss = use.Get_ss_assemblage_ptr()->Get_SSs().size(); + last_model.count_ss_assemblage = (int) count_ss; + last_model.ss_assemblage = + (const char **) PHRQ_malloc(count_ss * sizeof(char *)); + if (last_model.ss_assemblage == NULL) + malloc_error(); + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t j = 0; j < ss_ptrs.size(); j++) + { + last_model.ss_assemblage[j] = string_hsave(ss_ptrs[j]->Get_name().c_str()); + } + } + else + { + last_model.count_ss_assemblage = 0; + last_model.ss_assemblage = NULL; + } +/* + * save list of phase pointers for pp_assemblage + */ + last_model.pp_assemblage = + (struct phase **) free_check_null(last_model.pp_assemblage); + last_model.add_formula = + (const char **) free_check_null(last_model.add_formula); + last_model.si = (LDBLE *) free_check_null(last_model.si); + if (use.Get_pp_assemblage_ptr() != NULL) + { + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + last_model.count_pp_assemblage = (int) pp_assemblage_ptr->Get_pp_assemblage_comps().size(); + last_model.pp_assemblage = + (struct phase **) PHRQ_malloc((size_t) last_model.count_pp_assemblage * + sizeof(struct phase *)); + if (last_model.pp_assemblage == NULL) + malloc_error(); + last_model.add_formula = + (const char **) PHRQ_malloc((size_t)last_model.count_pp_assemblage * sizeof(char *)); + if (last_model.add_formula == NULL) + malloc_error(); + last_model.si = + (LDBLE *) PHRQ_malloc((size_t) last_model.count_pp_assemblage * sizeof(LDBLE)); + if (last_model.si == NULL) + malloc_error(); + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + i = 0; + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + int j; + struct phase * phase_ptr = phase_bsearch(it->first.c_str(), &j, false); + assert(phase_ptr); + last_model.pp_assemblage[i] = phase_ptr; + last_model.add_formula[i] = string_hsave(it->second.Get_add_formula().c_str()); + last_model.si[i] = it->second.Get_si(); + i++; + } + } + else + { + last_model.count_pp_assemblage = 0; + last_model.pp_assemblage = NULL; + last_model.add_formula = NULL; + last_model.si = NULL; + } +/* + * save data for surface + */ + last_model.surface_comp = + (const char **) free_check_null(last_model.surface_comp); + last_model.surface_charge = + (const char **) free_check_null(last_model.surface_charge); + if (use.Get_surface_ptr() != NULL) + { + /* comps */ + last_model.count_surface_comp = (int) use.Get_surface_ptr()->Get_surface_comps().size(); + last_model.surface_comp = + (const char **) PHRQ_malloc(use.Get_surface_ptr()->Get_surface_comps().size() * + sizeof(char *)); + if (last_model.surface_comp == NULL) + malloc_error(); + for (i = 0; i < (int) use.Get_surface_ptr()->Get_surface_comps().size(); i++) + { + last_model.surface_comp[i] = string_hsave(use.Get_surface_ptr()->Get_surface_comps()[i].Get_formula().c_str()); + } + /* charge */ + last_model.count_surface_charge = (int) use.Get_surface_ptr()->Get_surface_charges().size(); + last_model.surface_charge = + (const char **) PHRQ_malloc( use.Get_surface_ptr()->Get_surface_charges().size() * + sizeof(char *)); + if (last_model.surface_charge == NULL) + malloc_error(); + for (i = 0; i < (int) use.Get_surface_ptr()->Get_surface_charges().size(); i++) + { + last_model.surface_charge[i] = string_hsave(use.Get_surface_ptr()->Get_surface_charges()[i].Get_name().c_str()); + } + last_model.dl_type = use.Get_surface_ptr()->Get_dl_type(); + /*last_model.edl = use.Get_surface_ptr()->edl; */ + last_model.surface_type = use.Get_surface_ptr()->Get_type(); + } + else + { + last_model.dl_type = cxxSurface::NO_DL; + /*last_model.edl = -1; */ + last_model.surface_type = cxxSurface::UNKNOWN_DL; + last_model.count_surface_comp = 0; + last_model.surface_comp = NULL; + last_model.count_surface_charge = 0; + last_model.surface_charge = NULL; + } + + current_tc = NAN; + current_pa = NAN; + current_mu = NAN; + mu_terms_in_logk = true; + + last_model.numerical_fixed_volume = numerical_fixed_volume; + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_same_model(void) +/* ---------------------------------------------------------------------- */ +{ + int i; +/* + * Force new model to be built in prep + */ + if (last_model.force_prep == TRUE) + { + last_model.force_prep = FALSE; + return (FALSE); + } +/* + * Check master species + */ + for (i = 0; i < count_master; i++) + { +/* + output_msg(sformatf("%s\t%e\t%d\n", master[i]->elt->name, + master[i]->total, master[i]->last_model); + */ + if (master[i]->s == s_hplus || master[i]->s == s_h2o) + continue; + if (master[i]->total > MIN_TOTAL && master[i]->last_model == TRUE) + { + if (master[i]->s->secondary != NULL) + { + if (master[i]->s->secondary->unknown != NULL) + continue; + } + else + { + if (master[i]->unknown != NULL) + continue; + } + } + if (master[i]->total <= MIN_TOTAL && master[i]->last_model == FALSE) + continue; + return (FALSE); + } +/* + * Check gas_phase + */ + if (use.Get_gas_phase_ptr() != NULL) + { + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + if (last_model.gas_phase == NULL) + return (FALSE); + if (last_model.numerical_fixed_volume != numerical_fixed_volume) + return (FALSE); + if (last_model.count_gas_phase != (int) gas_phase_ptr->Get_gas_comps().size()) + return (FALSE); + for (i = 0; i < (int) gas_phase_ptr->Get_gas_comps().size(); i++) + { + cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + int k; + struct phase *phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str() , &k, FALSE); + assert(phase_ptr); + if (last_model.gas_phase[i] != phase_ptr) + { + return (FALSE); + } + } + } + else + { + if (last_model.gas_phase != NULL) + return (FALSE); + } +/* + * Check solid solutions + */ + if (use.Get_ss_assemblage_ptr() != NULL) + { + if (last_model.count_ss_assemblage != (int) use.Get_ss_assemblage_ptr()->Get_SSs().size()) + return (FALSE); + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (size_t i = 0; i < ss_ptrs.size(); i++) + { + if (last_model.ss_assemblage[i] != string_hsave(ss_ptrs[i]->Get_name().c_str())) + { + return (FALSE); + } + } + } + else + { + if (last_model.ss_assemblage != NULL) + return (FALSE); + } +/* + * Check pure_phases + */ + if (use.Get_pp_assemblage_ptr() != NULL) + { + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + if (last_model.count_pp_assemblage != (int) pp_assemblage_ptr->Get_pp_assemblage_comps().size()) + return (FALSE); + + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + i = 0; + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + int j; + struct phase * phase_ptr = phase_bsearch(it->first.c_str(), &j, FALSE); + assert(phase_ptr); + if (last_model.pp_assemblage[i] != phase_ptr) + { + return (FALSE); + } + if (last_model.add_formula[i] != + string_hsave(it->second.Get_add_formula().c_str())) + { + return (FALSE); + } + i++; + /* A. Crapsi + if (last_model.si[i] != use.Get_pp_assemblage_ptr()->pure_phases[i].si) + { + return (FALSE); + } + */ + } + } + else + { + if (last_model.pp_assemblage != NULL) + return (FALSE); + } +/* + * Check surface + */ + if (use.Get_surface_ptr() != NULL) + { + if (last_model.count_surface_comp != (int) use.Get_surface_ptr()->Get_surface_comps().size()) + return (FALSE); + if (last_model.count_surface_charge != (int) use.Get_surface_ptr()->Get_surface_charges().size()) + return (FALSE); + if (last_model.dl_type != use.Get_surface_ptr()->Get_dl_type()) + return (FALSE); + /*if (last_model.edl != use.Get_surface_ptr()->edl) return(FALSE); */ + if (last_model.surface_type != use.Get_surface_ptr()->Get_type()) + return (FALSE); + /* + if (last_model.only_counter_ions != use.Get_surface_ptr()->only_counter_ions) return(FALSE); + */ + for (i = 0; i < (int) use.Get_surface_ptr()->Get_surface_comps().size(); i++) + { + if (last_model.surface_comp[i] != + string_hsave(use.Get_surface_ptr()->Get_surface_comps()[i].Get_formula().c_str())) + return (FALSE); + if (use.Get_surface_ptr()->Get_surface_comps()[i].Get_phase_name().size() > 0) + { + cxxPPassemblage *pp_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, use.Get_n_surface_user()); + if (pp_ptr == NULL || (pp_ptr->Get_pp_assemblage_comps().find(use.Get_surface_ptr()->Get_surface_comps()[i].Get_phase_name()) == + pp_ptr->Get_pp_assemblage_comps().end())) + { + Rxn_new_surface.insert(use.Get_n_surface_user()); + cxxSurface *surf_ptr = Utilities::Rxn_find(Rxn_surface_map, use.Get_n_surface_user()); + surf_ptr->Set_new_def(true); + this->tidy_min_surface(); + return (FALSE); + } + } + if (use.Get_surface_ptr()->Get_surface_comps()[i].Get_rate_name().size() > 0) + { + cxxKinetics *kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_surface_user()); + if (kinetics_ptr == NULL || + (kinetics_ptr->Find(use.Get_surface_ptr()->Get_surface_comps()[i].Get_rate_name()) == NULL)) + { + Rxn_new_surface.insert(use.Get_n_surface_user()); + cxxSurface *surf_ptr = Utilities::Rxn_find(Rxn_surface_map, use.Get_n_surface_user()); + surf_ptr->Set_new_def(true); + this->tidy_kin_surface(); + return (FALSE); + } + } + } + for (i = 0; i < (int) use.Get_surface_ptr()->Get_surface_charges().size(); i++) + { + if (last_model.surface_charge[i] != + string_hsave(use.Get_surface_ptr()->Get_surface_charges()[i].Get_name().c_str())) + return (FALSE); + } + } + else + { + if (last_model.surface_comp != NULL) + return (FALSE); + } +/* + * Model is the same + */ + return (TRUE); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_min_exch(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Defines proportionality factor between mineral and exchanger to + * jacob0 + */ + int j, k, jj; + int row; + struct master *master_ptr; + struct unknown *unknown_ptr; + LDBLE coef; + + if (use.Get_exchange_ptr() == NULL) + return (OK); + cxxExchange *ex_ptr = use.Get_exchange_ptr(); + int n_user = ex_ptr->Get_n_user(); + cxxExchange * exchange_ptr = Utilities::Rxn_find(Rxn_exchange_map, n_user); + if (exchange_ptr == NULL) + { + input_error++; + error_string = sformatf( "Exchange %d not found.", + use.Get_n_exchange_user()); + error_msg(error_string, CONTINUE); + return ERROR; + } + n_user = exchange_ptr->Get_n_user(); + if (!exchange_ptr->Get_related_phases()) + return (OK); + for (size_t i = 0; i < exchange_ptr->Get_exchange_comps().size(); i++) + { + cxxExchComp & comp_ref = exchange_ptr->Get_exchange_comps()[i]; + if (comp_ref.Get_phase_name().size() == 0) + continue; + // Find exchange master + cxxNameDouble nd(comp_ref.Get_totals()); + cxxNameDouble::iterator it = nd.begin(); + struct master *exchange_master = NULL; + for ( ; it != nd.end(); it++) + { + element * elt_ptr = element_store(it->first.c_str()); + assert (elt_ptr); + if (elt_ptr->master->type == EX) + { + exchange_master = elt_ptr->master; + } + } + if (exchange_master == NULL) + { + input_error++; + error_string = sformatf( + "Did not find master exchange species for %s", + comp_ref.Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + /* find unknown number */ + for (j = count_unknowns - 1; j >= 0; j--) + { + if (x[j]->type != EXCH) + continue; + if (x[j]->master[0] == exchange_master) + break; + } + for (k = count_unknowns - 1; k >= 0; k--) + { + if (x[k]->type != PP) + continue; + //if (x[k]->phase->name == string_hsave(comp_ref.Get_phase_name().c_str())) + if (strcmp_nocase(x[k]->phase->name, comp_ref.Get_phase_name().c_str()) == 0) + break; + } + if (j == -1) + { + input_error++; + error_string = sformatf( + "Did not find unknown for master exchange species %s", + exchange_master->s->name); + error_msg(error_string, CONTINUE); + } + if (j == -1 || k == -1) + continue; +/* + * Build jacobian + */ + + /* charge balance */ + store_jacob0(charge_balance_unknown->number, x[k]->number, + comp_ref.Get_formula_z() * comp_ref.Get_phase_proportion()); + store_sum_deltas(&delta[k], &charge_balance_unknown->delta, + -comp_ref.Get_formula_z() * comp_ref.Get_phase_proportion()); + + + /* mole balance balance */ + count_elts = 0; + paren_count = 0; + { + char * formula = string_duplicate(comp_ref.Get_formula().c_str()); + char * ptr = formula; + get_elts_in_species(&ptr, 1.0); + free_check_null(formula); + } +#ifdef COMBINE + change_hydrogen_in_elt_list(0); +#endif + for (jj = 0; jj < count_elts; jj++) + { + master_ptr = elt_list[jj].elt->primary; + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Did not find unknown for %s, exchange related to mineral %s", + elt_list[jj].elt->primary->elt->name, comp_ref.Get_phase_name().c_str()); + error_msg(error_string, STOP); + } + if (master_ptr->in == FALSE) + { + master_ptr = master_ptr->s->secondary; + } + if (master_ptr->s->type == EX) + { + if (equal + (x[j]->moles, + x[k]->moles * elt_list[jj].coef * + comp_ref.Get_phase_proportion(), + 5.0 * convergence_tolerance) == FALSE) + { + error_string = sformatf( + "Resetting number of sites in exchanger %s (=%e) to be consistent with moles of phase %s (=%e).\n%s", + master_ptr->s->name, (double) x[j]->moles, + comp_ref.Get_phase_name().c_str(), + (double) (x[k]->moles * elt_list[jj].coef * + comp_ref.Get_phase_proportion()), + "\tHas equilibrium_phase assemblage been redefined?\n"); + warning_msg(error_string); + x[j]->moles = + x[k]->moles * elt_list[jj].coef * + comp_ref.Get_phase_proportion(); + } + } + coef = elt_list[jj].coef; + if (master_ptr->s == s_hplus) + { + row = mass_hydrogen_unknown->number; + unknown_ptr = mass_hydrogen_unknown; + } + else if (master_ptr->s == s_h2o) + { + row = mass_oxygen_unknown->number; + unknown_ptr = mass_oxygen_unknown; + } + else + { + row = master_ptr->unknown->number; + unknown_ptr = master_ptr->unknown; + } + store_jacob0(row, x[k]->number, + coef * comp_ref.Get_phase_proportion()); + store_sum_deltas(&delta[k], &unknown_ptr->delta, + -coef * comp_ref.Get_phase_proportion()); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_min_surface(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Defines proportionality factor between mineral and surface to + * jacob0 + */ + if (use.Get_surface_ptr() == NULL) + return (OK); + cxxSurface *surface_ptr = use.Get_surface_ptr(); + if (!surface_ptr->Get_related_phases()) + return (OK); + for (size_t i = 0; i < surface_ptr->Get_surface_comps().size(); i++) + { + cxxSurfaceComp *comp_ptr = &(surface_ptr->Get_surface_comps()[i]); + if (comp_ptr->Get_phase_name().size() == 0) + continue; + struct element *elt_ptr = element_store(comp_ptr->Get_master_element().c_str()); + /* find unknown number */ + int j; + for (j = count_unknowns - 1; j >= 0; j--) + { + if (x[j]->type != SURFACE) + continue; + if (x[j]->master[0] == elt_ptr->master) + break; + } + int k; + for (k = count_unknowns - 1; k >= 0; k--) + { + if (x[k]->type != PP) + continue; + //if (x[k]->phase->name == string_hsave(comp_ptr->Get_phase_name().c_str())) + if (strcmp_nocase(x[k]->phase->name, comp_ptr->Get_phase_name().c_str()) == 0) + break; + } + if (j == -1) + { + input_error++; + error_string = sformatf( + "Did not find unknown for master surface species %s", + elt_ptr->master->s->name); + error_msg(error_string, CONTINUE); + } + if (j == -1 || k == -1) + continue; + + /* update grams == moles in this case */ + if (j < count_unknowns - 1 && x[j + 1]->type == SURFACE_CB) + { + store_sum_deltas(&delta[k], &(x[j + 1]->related_moles), -1.0); + } + + /* charge balance */ + store_jacob0(charge_balance_unknown->number, x[k]->number, + comp_ptr->Get_formula_z() * comp_ptr->Get_phase_proportion()); + store_sum_deltas(&delta[k], &charge_balance_unknown->delta, + -comp_ptr->Get_formula_z() * comp_ptr->Get_phase_proportion()); + count_elts = 0; + paren_count = 0; + + // + { + /* Add specified formula for all types of surfaces */ + char * formula = string_duplicate(comp_ptr->Get_formula().c_str()); + char *ptr1 = formula; + get_elts_in_species(&ptr1, 1.0); + free_check_null(formula); + } +#ifdef COMBINE + change_hydrogen_in_elt_list(0); +#endif + for (int jj = 0; jj < count_elts; jj++) + { + struct master * master_ptr = elt_list[jj].elt->primary; + if (master_ptr->in == FALSE) + { + master_ptr = master_ptr->s->secondary; + } + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Did not find unknown for %s, surface related to mineral %s", + elt_list[jj].elt->primary->elt->name, comp_ptr->Get_phase_name().c_str()); + error_msg(error_string, STOP); + } + if (master_ptr->s->type == SURF) + { + if (equal + (x[j]->moles, + x[k]->moles * elt_list[jj].coef * + comp_ptr->Get_phase_proportion(), + 5.0 * convergence_tolerance) == FALSE) + { + error_string = sformatf( + "Resetting number of sites in surface %s (=%e) to be consistent with moles of phase %s (=%e).\n%s", + master_ptr->s->name, (double) x[j]->moles, + comp_ptr->Get_phase_name().c_str(), + (double) (x[k]->moles * elt_list[jj].coef * + comp_ptr->Get_phase_proportion()), + "\tHas equilibrium_phase assemblage been redefined?\n"); + warning_msg(error_string); + x[j]->moles = + x[k]->moles * elt_list[jj].coef * + comp_ptr->Get_phase_proportion(); + } + } + LDBLE coef = elt_list[jj].coef; + int row; + struct unknown *unknown_ptr; + if (master_ptr->s == s_hplus) + { + row = mass_hydrogen_unknown->number; + unknown_ptr = mass_hydrogen_unknown; + } + else if (master_ptr->s == s_h2o) + { + row = mass_oxygen_unknown->number; + unknown_ptr = mass_oxygen_unknown; + } + else + { + row = master_ptr->unknown->number; + unknown_ptr = master_ptr->unknown; + } + store_jacob0(row, x[k]->number, + coef * comp_ptr->Get_phase_proportion()); + store_sum_deltas(&delta[k], &unknown_ptr->delta, + -coef * comp_ptr->Get_phase_proportion()); + } + + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +setup_related_surface(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Fill in data for surface assemblage in unknown structure + */ + if (use.Get_surface_ptr() == NULL) + return (OK); + if (!use.Get_surface_ptr()->Get_related_phases()) + return (OK); + + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type == SURFACE) + { + cxxSurfaceComp *comp_ptr = use.Get_surface_ptr()->Find_comp(x[i]->surface_comp); + if (comp_ptr->Get_phase_name().size() > 0) + { + int k; + for (k = count_unknowns - 1; k >= 0; k--) + { + if (x[k]->type != PP) + continue; + //if (x[k]->phase->name == string_hsave(comp_ptr->Get_phase_name().c_str())) + if (strcmp_nocase(x[k]->phase->name, comp_ptr->Get_phase_name().c_str()) == 0) + break; + } + if (k == -1) + continue; + + x[i]->phase_unknown = x[k]; +/* !!!!! */ + x[i]->moles = x[k]->moles * comp_ptr->Get_phase_proportion(); + + } + } + else if (x[i]->type == SURFACE_CB) + { + cxxSurfaceComp *comp_ptr = use.Get_surface_ptr()->Find_comp(x[i-1]->surface_comp); + if (comp_ptr->Get_phase_name().size() > 0) + { + cxxSurfaceComp *comp_i_ptr = use.Get_surface_ptr()->Find_comp(x[i]->surface_comp); + int k; + for (k = count_unknowns - 1; k >= 0; k--) + { + if (x[k]->type != PP) + continue; + //if (x[k]->phase->name == string_hsave(comp_i_ptr->Get_phase_name().c_str())) + if (strcmp_nocase(x[k]->phase->name, comp_i_ptr->Get_phase_name().c_str()) == 0) + break; + } + if (k == -1) + continue; + + x[i]->phase_unknown = x[k]; + /* !!!! Added for security, not checked... */ + x[i]->related_moles = x[k]->moles * comp_i_ptr->Get_phase_proportion(); + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +change_hydrogen_in_elt_list(LDBLE charge) +/* ---------------------------------------------------------------------- */ +{ + int j; + int found_h, found_o; + LDBLE coef_h, coef_o, coef; + found_h = -1; + found_o = -1; + coef_h = 0.0; + coef_o = 0.0; + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + for (j = 0; j < count_elts; j++) + { + if (strcmp(elt_list[j].elt->name, "H") == 0) + { + found_h = j; + coef_h = elt_list[j].coef; + } + else if (strcmp(elt_list[j].elt->name, "O") == 0) + { + found_o = j; + coef_o = elt_list[j].coef; + } + } + coef = coef_h - 2 * coef_o - charge; + if (found_h < 0 && found_o < 0) + return (OK); + if (found_h >= 0 && found_o < 0) + return (OK); + if (found_h < 0 && found_o >= 0) + { + elt_list[count_elts].elt = s_hplus->primary->elt; + elt_list[count_elts].coef = coef; + count_elts++; + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + return (OK); + } + elt_list[found_h].coef = coef; + return (OK); +} diff --git a/phreeqcpp/print.cpp b/phreeqcpp/print.cpp new file mode 100644 index 00000000..902b1707 --- /dev/null +++ b/phreeqcpp/print.cpp @@ -0,0 +1,3660 @@ +#include "Utils.h" +#include "Phreeqc.h" +#include "phqalloc.h" +#include +#include +#include "Temperature.h" +#include "cxxMix.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "Reaction.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "cxxKinetics.h" +#include "Solution.h" +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +array_print(LDBLE * array_l, int row_count, int column_count, + int l_max_column_count) +/* ---------------------------------------------------------------------- */ +{ + int i, j, k; + + for (i = 0; i < row_count; i++) + { + k = 0; + output_msg(sformatf("%d\n", i)); + for (j = 0; j < column_count; j++) + { + if (k > 7) + { + output_msg(sformatf("\n")); + k = 0; + } + output_msg(sformatf("%11.2e", + (double) array_l[i * l_max_column_count + j])); + k++; + } + if (k != 0) + { + output_msg(sformatf("\n")); + } + output_msg(sformatf("\n")); + } + output_msg(sformatf("\n")); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_pr_in_false(void) +/* ---------------------------------------------------------------------- */ +{ + // set pr_in to false for subsequent steps... + if (use.Get_pp_assemblage_in()) + { + for (int i = 0; i < count_unknowns; i++) + { + if (x[i]->type == PP) + x[i]->phase->pr_in = false; + } + } + if (use.Get_gas_phase_ptr()) + { + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + int k; + struct phase *phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str(), &k, FALSE); + if (phase_ptr) + phase_ptr->pr_in = false; + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_all(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Print each block of results in sequence to file output + * also print selected output to punch_file + * Each routine is controlled by a variable in structure print. + * print.all == FALSE will turn off all prints. + */ + if (pr.all == FALSE) + { + set_pr_in_false(); + return (OK); + } +/* + * Makes sorted list including all species for each valence state + */ + if (pr.surface == TRUE || pr.exchange == TRUE || pr.species == TRUE) + { + species_list_sort(); + } +/* + * Print results + */ + s_h2o->lm = s_h2o->la; + print_using(); + print_mix(); + print_reaction(); + print_kinetics(); + print_user_print(); + print_gas_phase(); + print_pp_assemblage(); + print_ss_assemblage(); + print_surface(); + print_exchange(); + print_initial_solution_isotopes(); + print_isotope_ratios(); + print_isotope_alphas(); + print_totals(); + print_eh(); + print_species(); + print_alkalinity(); + print_saturation_indices(); + if (!pr.saturation_indices) + set_pr_in_false(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_all(void) +/* ---------------------------------------------------------------------- */ +{ +//#ifndef PHREEQ98 /* if not PHREEQ98 use the standard declaration */ +// if (pr.hdf == FALSE && (punch.in == FALSE || pr.punch == FALSE) && user_graph->commands == NULL) +// return (OK); +///* +// * Punch results +// */ +// if (state == TRANSPORT || state == PHAST || state == ADVECTION) +// { +// use.Get_kinetics_ptr() = kinetics_bsearch(use.Get_n_kinetics_user(), &i); +// } +// else if (use.Get_kinetics_in() != FALSE) +// { +// use.Get_kinetics_ptr() = kinetics_bsearch(-2, &i); +// } +//#else /* if PHREEQ98 execute punch_user_graph first, that is, before punch.in and pr.punch is checked */ + if (state == TRANSPORT || state == PHAST || state == ADVECTION) + { + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_kinetics_user())); + } + else if (use.Get_kinetics_in() != FALSE) + { + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, -2)); + } +#if defined PHREEQ98 + if (pr.user_graph == TRUE) + { + punch_user_graph(); + } +#elif defined MULTICHART + if (pr.user_graph == TRUE) + { + chart_handler.Punch_user_graph(this); + } +#endif + //if (pr.hdf == FALSE && (punch.in == FALSE || pr.punch == FALSE)) + if (pr.hdf == FALSE && (SelectedOutput_map.size() == 0 || pr.punch == FALSE)) + return (OK); + std::map < int, SelectedOutput >::iterator so_it = SelectedOutput_map.begin(); + for ( ; so_it != SelectedOutput_map.end(); so_it++) + { + current_selected_output = &(so_it->second); + if (pr.punch == FALSE || + current_selected_output == NULL || + !current_selected_output->Get_active() /* || + current_selected_output->Get_punch_ostream() == NULL*/) + continue; + phrq_io->Set_punch_ostream(current_selected_output->Get_punch_ostream()); + + // UserPunch + std::map < int, UserPunch >::iterator up_it = UserPunch_map.find(current_selected_output->Get_n_user()); + current_user_punch = up_it == UserPunch_map.end() ? NULL : &(up_it->second); + + punch_identifiers(); + punch_totals(); + punch_molalities(); + punch_activities(); + punch_pp_assemblage(); + punch_saturation_indices(); + punch_gas_phase(); + punch_kinetics(); + punch_ss_assemblage(); + punch_isotopes(); + punch_calculate_values(); + punch_user_punch(); + /* + * new line for punch_file + */ + punch_msg("\n"); + + /* + * signal end of row + */ + fpunchf_end_row("\n"); + punch_flush(); + } + current_selected_output = NULL; + current_user_punch = NULL; + phrq_io->Set_punch_ostream(NULL); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_diffuse_layer(cxxSurfaceCharge *charge_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints total moles of each element in diffuse layer + * Remove comment to print total moles of each species + */ + LDBLE mass_water_surface, r, sum_surfs; + LDBLE molality, moles_excess, moles_surface, d; + + if (use.Get_surface_ptr() == NULL) + return (OK); +/* + * Find position of component in surface charge data + */ + int j; + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE_CB) + continue; + cxxSurfaceCharge * charge_ptr_search = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + if (charge_ptr->Get_name() == charge_ptr_search->Get_name()) + { + break; + } + } + if (j >= count_unknowns) + { + error_string = sformatf( + "In print_diffuse_layer: component not found, %s.", + charge_ptr->Get_name().c_str()); + error_msg(error_string, STOP); + } +/* + * Loop through all surface components, calculate each H2O surface (diffuse layer), + * H2O aq, and H2O bulk (diffuse layers plus aqueous). + */ + + if (mass_water_surfaces_x != 0) + { + d = 100 * charge_ptr->Get_mass_water() / mass_water_surfaces_x; + } + else + { + d = 0.0; + } + output_msg(sformatf( + "\tWater in diffuse layer: %8.3e kg, %4.1f%% of total DDL-water.\n", + (double) charge_ptr->Get_mass_water(), (double) d)); + if (use.Get_surface_ptr()->Get_debye_lengths() > 0 && d > 0) + { + sum_surfs = 0.0; + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE_CB) + continue; + cxxSurfaceCharge * charge_ptr_search = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + sum_surfs += + charge_ptr_search->Get_specific_area() * + charge_ptr_search->Get_grams(); + } + r = 0.002 * mass_water_bulk_x / sum_surfs; + output_msg(sformatf( + "\tRadius of total pore: %8.3e m; of free pore: %8.3e m.\n", + (double) r, (double) (r - use.Get_surface_ptr()->Get_thickness()))); + } + + if (debug_diffuse_layer == TRUE) + { + output_msg(sformatf( + "\n\t\tDistribution of species in diffuse layer\n\n")); + output_msg(sformatf( + "\n\tSpecies \t Moles \tMoles excess\t g\n")); + } + if ((mass_water_surface = charge_ptr->Get_mass_water())) + { + count_elts = 0; + paren_count = 0; + for (j = 0; j < count_s_x; j++) + { + if (s_x[j]->type > HPLUS) + continue; + molality = under(s_x[j]->lm); + moles_excess = mass_water_aq_x * molality * (charge_ptr->Get_g_map()[s_x[j]->z].Get_g() * + s_x[j]->erm_ddl + + mass_water_surface / + mass_water_aq_x * (s_x[j]->erm_ddl - 1)); + moles_surface = mass_water_surface * molality + moles_excess; + if (debug_diffuse_layer == TRUE) + { + output_msg(sformatf("\t%-12s\t%12.3e\t%12.3e\t%12.3e\n", + s_x[j]->name, moles_surface, moles_excess, + charge_ptr->Get_g_map()[s_x[j]->z].Get_g())); + } + /* + * Accumulate elements in diffuse layer + */ + add_elt_list(s_x[j]->next_elt, moles_surface); + } + /* + strcpy(token, s_h2o->name); + ptr = &(token[0]); + get_elts_in_species (&ptr, mass_water_surface / gfw_water); + */ + if (count_elts > 0) + { + qsort(elt_list, (size_t)count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + } + /* + * Print totals + */ + if (use.Get_surface_ptr()->Get_dl_type() != cxxSurface::DONNAN_DL) + { + output_msg(sformatf( + "\n\tTotal moles in diffuse layer (excluding water)\n\n")); + } + else + { + LDBLE exp_g = charge_ptr->Get_g_map()[1].Get_g() * mass_water_aq_x / mass_water_surface + 1; + LDBLE psi_DL = -log(exp_g) * R_KJ_DEG_MOL * tk_x / F_KJ_V_EQ; + output_msg(sformatf( + "\n\tTotal moles in diffuse layer (excluding water), Donnan calculation.")); + output_msg(sformatf( + "\n\tDonnan Layer potential, psi_DL = %10.3e V.\n\tBoltzmann factor, exp(-psi_DL * F / RT) = %9.3e (= c_DL / c_free if z is +1).\n\n", + psi_DL, exp_g)); + } + output_msg(sformatf("\tElement \t Moles\n")); + for (j = 0; j < count_elts; j++) + { + output_msg(sformatf("\t%-14s\t%12.4e\n", + elt_list[j].elt->name, (double)elt_list[j].coef)); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_eh(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints eh calculated from redox couples + * Only calculates eh if two redox states of an element have mass balance + * equations. + */ + int i, j, k, first; + LDBLE pe, eh; + struct master *master_ptr0, *master_ptr1; + char token[MAX_LENGTH]; + + if (pr.eh == FALSE || pr.all == FALSE) + return (OK); + + tk_x = tc_x + 273.15; + + first = TRUE; + for (i = 0; i < count_master; i++) + { + if (master[i]->in != TRUE) + continue; + if (master[i]->primary == TRUE) + continue; +/* + * Secondary master species has mass balance equation + */ + master_ptr0 = master[i]->elt->primary; + for (k = i + 1; k < count_master; k++) + { + if (master[k]->in != TRUE) + continue; + master_ptr1 = master[k]->elt->primary; + if (master_ptr1 != master_ptr0) + break; +/* + * Another secondary master species of same element has mass balance equation + * Rewrite equations to calculate pe + */ + rewrite_master_to_secondary(master[k], master[i]); + trxn_swap("e-"); +/* debug + trxn_print(); + */ +/* + * Calculate pe, eh + */ + pe = -k_calc(trxn.logk, tk_x, patm_x * PASCAL_PER_ATM); + for (j = 1; j < count_trxn; j++) + { + pe -= trxn.token[j].s->la * trxn.token[j].coef; + } + eh = ((LOG_10 * R_KJ_DEG_MOL * tk_x) / F_KJ_V_EQ) * pe; +/* + * Print heading + */ + if (first == TRUE) + { + print_centered("Redox couples"); + output_msg(sformatf("\t%-15s%12s%12s\n\n", + "Redox couple", "pe", "Eh (volts)")); + first = FALSE; + } +/* + * Print result + */ + strcpy(token, master[i]->elt->name); + strcat(token, "/"); + strcat(token, master[k]->elt->name); + output_msg(sformatf("\t%-15s%12.4f%12.4f\n", token, + (double) pe, (double) eh)); + } + } + if (first == FALSE) + output_msg(sformatf("\n")); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_exchange(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Print moles of each exchange species + */ + int i; + cxxExchange * exchange_ptr; + const char *name, *name1; + struct master *master_ptr; + LDBLE dum, dum2; +/* + * Print exchange data + */ + exchange_ptr = use.Get_exchange_ptr(); + if (exchange_ptr == NULL || pr.exchange == FALSE || pr.all == FALSE) + return (OK); + + if (state >= REACTION) + { + print_centered("Exchange composition"); + } +/* + * Print list of species + */ + + s_h2o->lm = s_h2o->la; + name = s_hplus->secondary->elt->name; + for (i = 0; i < count_species_list; i++) + { +/* + * Get name of master species + */ + if (species_list[i].s->type != EX) + continue; + if (species_list[i].master_s->secondary != NULL) + { + master_ptr = species_list[i].master_s->secondary; + name1 = species_list[i].master_s->secondary->elt->name; + } + else + { + master_ptr = species_list[i].master_s->primary; + name1 = species_list[i].master_s->primary->elt->name; + } +/* + * Check if new master species, print total molality + */ + if (name1 != name) + { + name = name1; + output_msg(sformatf("%-14s%12.3e mol", name, + (double) master_ptr->unknown->moles)); + cxxExchange *exchange_ptr = (cxxExchange *) (use.Get_exchange_ptr()); + if (master_ptr->unknown->exch_comp == NULL) + { + error_string = sformatf("Exchange unknown has no exchange component for exchanger %s." + "\nIs the same name used for a SURFACE and an EXCHANGER?", + master_ptr->unknown->description); + error_msg(error_string, STOP); + } + const cxxExchComp *exchange_comp_ptr = exchange_ptr->Find_comp(master_ptr->unknown->exch_comp); + assert(exchange_comp_ptr); + if (exchange_comp_ptr->Get_phase_name().size() > 0) + { + output_msg(sformatf("\t[%g (mol %s)/(mol %s)]", + (double) exchange_comp_ptr->Get_phase_proportion(), + exchange_comp_ptr->Get_formula().c_str(), + exchange_comp_ptr->Get_phase_name().c_str())); + } + else if (exchange_comp_ptr->Get_rate_name().size() > 0) + { + output_msg(sformatf( + "\t[%g (mol %s)/(mol kinetic reactant %s)]", + (double) exchange_comp_ptr->Get_phase_proportion(), + exchange_comp_ptr->Get_formula().c_str(), + exchange_comp_ptr->Get_rate_name().c_str())); + } + output_msg(sformatf("\n\n")); + /* Heading for species */ + output_msg(sformatf("\t%-15s%12s%12s%12s%10s\n", " ", " ", + "Equiv- ", "Equivalent", "Log ")); + output_msg(sformatf("\t%-15s%12s%12s%12s%10s\n\n", + "Species", "Moles ", "alents ", "Fraction", "Gamma")); + } +/* + * Print species data + */ +/* !!!!! */ + if (master_ptr->total > 1.0e-10) + { + if (species_list[i].s->equiv != 0.0) + { + dum = fabs(species_list[i].s->equiv) / master_ptr->total; + } + else + { + if (species_list[i].master_s->z == 0) + { + dum = 1 / master_ptr->total; + } + else + { + dum = 1; + } + } + if (species_list[i].master_s->z != 0.0) + { + dum2 = fabs(species_list[i].master_s->z); + } + else + { + dum2 = 1; + } + output_msg(sformatf("\t%-15s%12.3e%12.3e%12.3e%10.3f\n", + species_list[i].s->name, + (double) species_list[i].s->moles, + (double) (species_list[i].s->moles * dum2 * + species_list[i].s->equiv), + (double) (species_list[i].s->moles * + dum /* / dum2 */ ), + (double) (species_list[i].s->lg - log10(dum)))); + } + } + output_msg(sformatf("\n")); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_gas_phase(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints gas phase composition if present + */ + LDBLE lp, moles, initial_moles, delta_moles; + struct rxn_token *rxn_ptr; + char info[MAX_LENGTH]; + bool PR = false; + + if (pr.gas_phase == FALSE || pr.all == FALSE) + return (OK); + if (use.Get_gas_phase_ptr() == NULL) + return (OK); + + cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr->Get_v_m() >= 0.01) + PR = true; + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + if (gas_unknown == NULL) + return (OK); + if (gas_unknown->moles < 1e-12) + { + sprintf(info, "Fixed-pressure gas phase %d dissolved completely", + use.Get_n_gas_phase_user()); + print_centered(info); + return (OK); + } + gas_phase_ptr->Set_total_moles(gas_unknown->moles); + gas_phase_ptr->Set_volume(gas_phase_ptr->Get_total_moles() * R_LITER_ATM * tk_x / + gas_phase_ptr->Get_total_p()); + if (PR) + gas_phase_ptr->Set_volume(gas_phase_ptr->Get_v_m() * gas_unknown->moles); + } +/* + * Print heading + */ + print_centered("Gas phase"); + output_msg(sformatf("Total pressure: %5.2f atmospheres", + (double) gas_phase_ptr->Get_total_p())); + if (gas_phase_ptr->Get_total_p() >= 1500) + output_msg(" WARNING: Program limit.\n"); + else if (PR) + output_msg(" (Peng-Robinson calculation)\n"); + else + output_msg(" \n"); + output_msg(sformatf(" Gas volume: %10.2e liters\n", + (double) gas_phase_ptr->Get_volume())); + if(gas_phase_ptr->Get_total_moles() > 0) + { + if (PR) + { + output_msg(sformatf(" Molar volume: %10.2e liters/mole", + (double) (gas_phase_ptr->Get_v_m()))); + } + else + { + output_msg(sformatf(" Molar volume: %10.2e liters/mole", + (double) (gas_phase_ptr->Get_volume() / gas_phase_ptr->Get_total_moles()))); + } + } + if (/*!numerical_fixed_volume && */((PR && gas_phase_ptr->Get_v_m() <= 0.016))) + output_msg(" WARNING: Program limit for Peng-Robinson.\n"); + else + output_msg("\n"); + if (PR) + output_msg(sformatf( " P * Vm / RT: %8.5f (Compressibility Factor Z) \n", + (double) (gas_phase_ptr->Get_total_p() * gas_phase_ptr->Get_v_m() / (R_LITER_ATM * tk_x)))); + + + output_msg(sformatf("\n%68s\n%78s\n", "Moles in gas", + "----------------------------------")); + if (PR) + output_msg(sformatf( "%-11s%12s%12s%7s%12s%12s%12s\n\n", "Component", + "log P", "P", "phi", "Initial", "Final", "Delta")); + else + output_msg(sformatf("%-18s%12s%12s%12s%12s%12s\n\n", "Component", + "log P", "P", "Initial", "Final", "Delta")); + + for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++) + { +/* + * Calculate partial pressure + */ + cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[j]); + int k; + struct phase *phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str(), &k, FALSE); + if (phase_ptr->in == TRUE) + { + lp = -phase_ptr->lk; + for (rxn_ptr = + phase_ptr->rxn_x->token + 1; + rxn_ptr->s != NULL; rxn_ptr++) + { + lp += rxn_ptr->s->la * rxn_ptr->coef; + } + lp -= phase_ptr->pr_si_f; + moles = phase_ptr->moles_x; + } + else + { + lp = -99.99; + moles = 0; + phase_ptr->p_soln_x = 0; + } +/* + * Print gas composition + */ + if (state != TRANSPORT && state != PHAST) + { + initial_moles = gc_ptr->Get_moles(); + delta_moles = phase_ptr->moles_x - gc_ptr->Get_moles(); + } + else + { + initial_moles = gc_ptr->Get_initial_moles(); + delta_moles = gc_ptr->Get_initial_moles() - + gc_ptr->Get_moles(); + } + if (moles <= MIN_TOTAL) + moles = 0.0; + if (fabs(delta_moles) <= MIN_TOTAL) + delta_moles = 0.0; + if (PR) + { + output_msg(sformatf("%-11s%12.2f%12.3e%7.3f%12.3e%12.3e%12.3e\n", + phase_ptr->name, + (double) lp, + (double) phase_ptr->p_soln_x, + (double) phase_ptr->pr_phi, + (double) initial_moles, + (double) moles, + (double) delta_moles)); + } + else + output_msg(sformatf("%-18s%12.2f%12.3e%12.3e%12.3e%12.3e\n", + phase_ptr->name, + (double) lp, + (double) phase_ptr->p_soln_x, + (double) initial_moles, + (double) moles, + (double) delta_moles)); + if (!strcmp(phase_ptr->name, "H2O(g)") && phase_ptr->p_soln_x == 90) + output_msg(" WARNING: The pressure of H2O(g) is above the program limit: use the polynomial for log_k.\n"); + + } + output_msg("\n"); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_ss_assemblage(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Prints solid solution composition if present + */ + int i, j; + LDBLE delta_moles; + LDBLE nb, nc, xb, xb1, xb2, xb1moles, xb2moles; + + if (pr.ss_assemblage == FALSE || pr.all == FALSE) + return (OK); + if (use.Get_ss_assemblage_ptr() == NULL) + return (OK); + /* + * Print heading + */ + print_centered("Solid solutions"); + output_msg(sformatf("\n")); + output_msg(sformatf("%-15s %22s %11s %11s %11s\n\n", + "Solid solution", "Component", "Moles", "Delta moles", + "Mole fract")); + /* + * Print solid solutions + */ + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (j = 0; j < (int) ss_ptrs.size(); j++) + { + cxxSS * ss_ptr = ss_ptrs[j]; + if (ss_ptr->Get_ss_in()) + { + /* solid solution name, moles */ + output_msg(sformatf("%-15s %22s %11.2e\n", + ss_ptr->Get_name().c_str(), " ", + (double) ss_ptr->Get_total_moles())); + /* component name, moles, delta moles, mole fraction */ + + for (i = 0; i < (int) ss_ptr->Get_ss_comps().size(); i++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[i]); + if (state != TRANSPORT && state != PHAST) + { + delta_moles = + comp_ptr->Get_moles() - + comp_ptr->Get_initial_moles() - + comp_ptr->Get_delta(); + } + else + { + delta_moles = + comp_ptr->Get_moles() - + comp_ptr->Get_init_moles(); + } + output_msg(sformatf( + "%15s %22s %11.2e %11.2e %11.2e\n", " ", + comp_ptr->Get_name().c_str(), + (double) comp_ptr->Get_moles(), (double) delta_moles, + (double) (comp_ptr->Get_moles() / + ss_ptr->Get_total_moles()))); + } + if (ss_ptr->Get_miscibility()) + { + cxxSScomp *comp0_ptr = &(ss_ptr->Get_ss_comps()[0]); + cxxSScomp *comp1_ptr = &(ss_ptr->Get_ss_comps()[1]); + nc = comp0_ptr->Get_moles(); + nb = comp1_ptr->Get_moles(); + xb = nb / (nb + nc); + xb1 = ss_ptr->Get_xb1(); + xb2 = ss_ptr->Get_xb2(); + + if (xb > xb1 && xb < xb2) + { + xb2moles = (xb1 - 1) / xb1 * nb + nc; + xb2moles = xb2moles / ((xb1 - 1) / xb1 * xb2 + (1 - xb2)); + xb1moles = (nb - xb2moles * xb2) / xb1; + output_msg(sformatf( + "\n%14s Solid solution is in miscibility gap\n", + " ")); + output_msg(sformatf( + "%14s End members in pct of %s\n\n", " ", + comp1_ptr->Get_name().c_str())); + output_msg(sformatf("%22s %11g pct %11.2e\n", + " ", (double) xb1, (double) xb1moles)); + output_msg(sformatf("%22s %11g pct %11.2e\n", + " ", (double) xb2, (double) xb2moles)); + } + } + } + else + { + /* solid solution name, moles */ + output_msg(sformatf("%-15s %22s %11.2e\n", + ss_ptr->Get_name().c_str(), " ", + (double) 0.0)); + /* component name, moles, delta moles, mole fraction */ + for (i = 0; i < (int) ss_ptr->Get_ss_comps().size(); i++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[i]); + if (state != TRANSPORT && state != PHAST) + { + delta_moles = + comp_ptr->Get_moles() - + comp_ptr->Get_initial_moles() - + comp_ptr->Get_delta(); + } + else + { + delta_moles = + comp_ptr->Get_moles() - + comp_ptr->Get_init_moles(); + } + output_msg(sformatf( + "%15s %22s %11.2e %11.2e %11.2e\n", " ", + comp_ptr->Get_name().c_str(), + (double) 0, (double) delta_moles, (double) 0)); + } + } + } + output_msg(sformatf("\n")); + return (OK); +} +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +print_reaction(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * prints irreversible reaction as defined and as + * relative moles of each element and total amount + * of reaction + */ + cxxReaction *reaction_ptr; + if (pr.use == FALSE || pr.all == FALSE) + return (OK); + if (state < REACTION || use.Get_reaction_in() == FALSE) + return (OK); + if (state == TRANSPORT && transport_step == 0) + return (OK); + reaction_ptr = use.Get_reaction_ptr(); +/* + * Print amount of reaction + */ + output_msg(sformatf("Reaction %d.\t%s\n\n", use.Get_n_reaction_user(), + reaction_ptr->Get_description().c_str())); + output_msg(sformatf( + "\t%11.3e moles of the following reaction have been added:\n\n", + (double) step_x)); +/* + * Print reaction + */ + output_msg(sformatf("\t%-15s%10s\n", " ", "Relative")); + output_msg(sformatf("\t%-15s%10s\n\n", "Reactant", "moles")); + cxxNameDouble::const_iterator cit = reaction_ptr->Get_reactantList().begin(); + for ( ; cit != reaction_ptr->Get_reactantList().end(); cit++) + { + output_msg(sformatf("\t%-15s%13.5f\n", + cit->first.c_str(), (double) cit->second)); + } + output_msg(sformatf("\n")); +/* + * Debug + */ + + output_msg(sformatf("\t%-15s%10s\n", " ", "Relative")); + output_msg(sformatf("\t%-15s%10s\n", "Element", "moles")); + cit = reaction_ptr->Get_elementList().begin(); + for ( ; cit != reaction_ptr->Get_elementList().end(); cit++) + { + struct element * elt_ptr = element_store(cit->first.c_str()); + assert(elt_ptr); + output_msg(sformatf("\t%-15s%13.5f\n", + elt_ptr->name, + (double) cit->second)); + } + output_msg(sformatf("\n")); + return (OK); +} +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +print_kinetics(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * prints kinetic reaction, + * should be called only on final kinetic step + */ + LDBLE sim_time; + cxxKinetics *kinetics_ptr; + if (pr.kinetics == FALSE || pr.all == FALSE) + return (OK); + if (state < REACTION) + return (OK); + kinetics_ptr = NULL; + if (use.Get_kinetics_in() == TRUE) + { + if (state == TRANSPORT || state == PHAST || state == ADVECTION) + { + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_kinetics_user()); + } + else + { + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, -2); + } + } + if (kinetics_ptr == NULL) + return (OK); +/* + * determine time step + */ + if (state == TRANSPORT || state == PHAST) + { + kin_time_x = timest; + } + else if (state == ADVECTION) + { + kin_time_x = advection_kin_time; + } + sim_time = 0.; + if (run_info.Get_run_cells()) + { + sim_time = rate_sim_time; + } + else + { + if (incremental_reactions == TRUE) + { + if (!kinetics_ptr->Get_equalIncrements()) + { + for (int i = 0; i < reaction_step; i++) + { + if (i < (int) kinetics_ptr->Get_steps().size()) + { + sim_time += kinetics_ptr->Get_steps()[i]; + } + else + { + sim_time += kinetics_ptr->Get_steps().back(); + } + } + } + else + { + if (reaction_step > kinetics_ptr->Get_count()) + { + sim_time = kinetics_ptr->Get_steps().front(); + } + else + { + sim_time = + reaction_step * kinetics_ptr->Get_steps().front() / + ((LDBLE) (kinetics_ptr->Get_count())); + } + } + } + } +/* + * Print amount of reaction + */ + if (phast == FALSE) + { + output_msg(sformatf("Kinetics %d.\t%s\n\n", + use.Get_n_kinetics_user(), kinetics_ptr->Get_description().c_str())); + } + else + { + output_msg(sformatf("Kinetics.\n\n")); + } +/* + * Print reaction + */ + if (state == TRANSPORT) + { + output_msg(sformatf("\tTime: %g seconds\n", + (double) (initial_total_time + transport_step * timest))); + output_msg(sformatf("\tTime step: %g seconds\n\n", + (double) kin_time_x)); + } + else if (state == ADVECTION) + { + output_msg(sformatf("\tTime: %g seconds\n", + (double) (initial_total_time + + advection_step * advection_kin_time))); + output_msg(sformatf("\tTime step: %g seconds\n\n", + (double) kin_time_x)); + } + else if (state == PHAST) + { + output_msg(sformatf("\tTime: %g seconds\n", + (double) rate_sim_time_end)); + output_msg(sformatf("\tTime step: %g seconds\n\n", + (double) kin_time_x)); + } + else if (state == REACTION) + { + if (incremental_reactions == FALSE) + { + output_msg(sformatf("\tTime step: %g seconds\n\n", + (double) kin_time_x)); + } + else + { + output_msg(sformatf( + "\tTime step: %g seconds (Incremented time: %g seconds)\n\n", + (double) kin_time_x, (double) sim_time)); + } + } + output_msg(sformatf("\t%-15s%12s%12s %-15s%12s\n\n", + "Rate name", "Delta Moles", "Total Moles", "Reactant", + "Coefficient")); + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp *kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + if (state != TRANSPORT && state != PHAST) + { + output_msg(sformatf("\t%-15s%12.3e%12.3e", + kinetics_comp_ptr->Get_rate_name().c_str(), + (double) -kinetics_comp_ptr->Get_moles(), + (double) kinetics_comp_ptr->Get_m())); + } + else + { + output_msg(sformatf("\t%-15s%12.3e%12.3e", + kinetics_comp_ptr->Get_rate_name().c_str(), + (double) (kinetics_comp_ptr->Get_m() - + kinetics_comp_ptr->Get_initial_moles()), + (double) kinetics_comp_ptr->Get_m())); + } + cxxNameDouble::iterator it = kinetics_comp_ptr->Get_namecoef().begin(); + for ( ; it != kinetics_comp_ptr->Get_namecoef().end(); it++) + { + std::string name = it->first; + LDBLE coef = it->second; + if (it == kinetics_comp_ptr->Get_namecoef().begin()) + { + output_msg(sformatf(" %-15s%12g\n", + name.c_str(), + (double) coef)); + } + else + { + output_msg(sformatf("\t%39s %-15s%12g\n", " ", + name.c_str(), + (double) coef)); + } + } + } + output_msg(sformatf("\n")); + return (OK); +} +#ifdef SKIP +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_master_reactions(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Debugging print routine to test primary and secondary reactions + */ + int i; + struct rxn_token *next_token; + + for (i = 0; i < count_master; i++) + { + output_msg(sformatf("%s\t%s\n\tPrimary reaction\n", + master[i]->elt->name, master[i]->s->name)); + next_token = master[i]->rxn_primary->token; + for (; next_token->s != NULL; next_token++) + { + output_msg(sformatf("\t\t%s\t%f\n", next_token->s->name, + (double) next_token->coef)); + } + output_msg(sformatf("\n\tSecondary reaction:\n")); + if (master[i]->rxn_secondary != NULL) + { + next_token = master[i]->rxn_secondary->token; + for (; next_token->s != NULL; next_token++) + { + output_msg(sformatf("\t\t%s\t%f\n", + next_token->s->name, (double) next_token->coef)); + } + } + output_msg(sformatf("\n\tRedox reaction:\n")); + if (*(master[i]->pe_rxn) != NULL) + { + next_token = (*(master[i]->pe_rxn))->token; + for (; next_token->s != NULL; next_token++) + { + output_msg(sformatf("\t\t%s\t%f\n", + next_token->s->name, (double) next_token->coef)); + } + } + output_msg(sformatf("\n")); + } + return (OK); +} +#endif +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +print_mix(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * prints definition of mixing, solution number and multiplier + */ + cxxMix * mix_ptr; + cxxSolution *solution_ptr; + + if (pr.use == FALSE || pr.all == FALSE) + return (OK); + if (use.Get_mix_in() == FALSE || state < REACTION) + return (OK); + if (state == TRANSPORT) + { + mix_ptr = Utilities::Rxn_find(Rxn_mix_map, use.Get_n_mix_user()); + } + else + { + mix_ptr = Utilities::Rxn_find(Rxn_mix_map, use.Get_n_mix_user_orig()); + } + if (mix_ptr == NULL) + { + mix_ptr = use.Get_mix_ptr(); + } +/* + * Print mixture data + */ + if (mix_ptr == NULL) + { + return (OK); + + } + if (state == TRANSPORT) + { + output_msg(sformatf("Mixture %d.\t%s\n\n", use.Get_n_mix_user(), + mix_ptr->Get_description().c_str())); + } + else + { + output_msg(sformatf("Mixture %d.\t%s\n\n", mix_ptr->Get_n_user(), + mix_ptr->Get_description().c_str())); + } + std::map::const_iterator cit; + for (cit = mix_ptr->Get_mixComps().begin(); cit != mix_ptr->Get_mixComps().end(); cit++) + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, cit->first); + if (solution_ptr == NULL) + { + input_error++; + return (ERROR); + } + output_msg(sformatf("\t%11.3e Solution %d\t%-55s\n", + (double) cit->second, + cit->first, solution_ptr->Get_description().c_str())); + } + output_msg(sformatf("\n")); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_reaction(struct reaction *rxn_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Debugging print of individual chemical reactions for + * species or phases + */ + int j; + struct rxn_token *next_token; + + if (pr.use == FALSE || pr.all == FALSE) + return (OK); + + output_msg(sformatf("%s\t\n", rxn_ptr->token[0].s->name)); + output_msg(sformatf("\n\tlog k:\n")); + for (j = 0; j < MAX_LOG_K_INDICES; j++) + { + output_msg(sformatf("\t%f", (double) rxn_ptr->logk[j])); + } + output_msg(sformatf("\n\nReaction:\n")); + for (next_token = rxn_ptr->token; next_token->s != NULL; next_token++) + { + output_msg(sformatf("\t\t%s\t%f\n", next_token->s->name, + (double) next_token->coef)); + } + output_msg(sformatf("\n")); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_saturation_indices(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints saturation indices of all applicable pure_phases + */ + int i; + LDBLE si, iap; + LDBLE lk; + LDBLE la_eminus; + struct rxn_token *rxn_ptr; + struct reaction *reaction_ptr; + bool gas = true; + + if (pr.saturation_indices == FALSE || pr.all == FALSE) + return (OK); + if (state == INITIAL_SOLUTION) + { + iap = 0; + for (size_t tok = 1; tok < pe_x[default_pe_x].Get_tokens().size(); tok++) + { + iap += pe_x[default_pe_x].Get_tokens()[tok].coef * pe_x[default_pe_x].Get_tokens()[tok].s->la; + /* fprintf(output,"\t%s\t%f\t%f\n", rxn_ptr->s->name, rxn_ptr->coef, rxn_ptr->s->la ); */ + } + lk = k_calc(pe_x[default_pe_x].Get_logk(), tk_x, patm_x * PASCAL_PER_ATM); + la_eminus = lk + iap; + /* fprintf(output,"\t%s\t%f\n", "pe", si ); */ + } + else + { + la_eminus = s_eminus->la; + } +/* If a fixed pressure gas-phase disappeared, no PR for the SI's of gases... */ + if (use.Get_gas_phase_ptr() != NULL) + { + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + if (gas_unknown == NULL || gas_unknown->moles < 1e-12) + gas = false; + } + } + +/* + * Print heading + */ + print_centered("Saturation indices"); + output_msg(sformatf(" %-15s%9s%8s%9s%3d%4s%3d%4s\n\n", "Phase", "SI**", + "log IAP", "log K(", int(tk_x), " K, ", int(floor(patm_x + 0.5)), " atm)")); + + for (i = 0; i < count_phases; i++) + { + if (phases[i]->in == FALSE || phases[i]->type != SOLID) + continue; + /* check for solids and gases in equation */ + if (phases[i]->replaced) + reaction_ptr = phases[i]->rxn_s; + else + reaction_ptr = phases[i]->rxn; +/* + * Print saturation index + */ + reaction_ptr->logk[delta_v] = calc_delta_v(reaction_ptr, true) - + phases[i]->logk[vm0]; + if (reaction_ptr->logk[delta_v]) + mu_terms_in_logk = true; + lk = k_calc(reaction_ptr->logk, tk_x, patm_x * PASCAL_PER_ATM); + iap = 0.0; + for (rxn_ptr = reaction_ptr->token + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + if (rxn_ptr->s != s_eminus) + { + iap += (rxn_ptr->s->lm + rxn_ptr->s->lg) * rxn_ptr->coef; + } + else + { + iap += la_eminus * rxn_ptr->coef; + } + } + si = -lk + iap; + + output_msg(sformatf(" %-15s%7.2f %8.2f%8.2f %s", + phases[i]->name, (double) si, (double) iap, (double) lk, + phases[i]->formula)); + if (gas && phases[i]->pr_in && phases[i]->pr_p) + { + if (phases[i]->moles_x || state == INITIAL_SOLUTION) + { + output_msg(sformatf("\t%s%5.1f%s%5.3f", + " Pressure ", (double) phases[i]->pr_p, " atm, phi ", (double) phases[i]->pr_phi)); + } else + { + for (int j = 0; j < count_unknowns; j++) + { + if (x[j]->type != PP) + continue; + if (!strcmp(x[j]->phase->name, phases[i]->name)) + { + if (x[j]->moles) + output_msg(sformatf("\t%s%5.1f%s%5.3f", + " Pressure ", (double) phases[i]->pr_p, " atm, phi ", (double) phases[i]->pr_phi)); + break; + } + } + } + } + phases[i]->pr_in = false; + output_msg("\n"); + } + output_msg(sformatf("\n%s\n%s", + "**For a gas, SI = log10(fugacity). Fugacity = pressure * phi / 1 atm.", + " For ideal gases, phi = 1.")); + output_msg("\n\n"); + + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_pp_assemblage(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints saturation indices and masses of pure_phases in pp_assemblage + */ + int j, k; + LDBLE si, iap, lk; + char token[MAX_LENGTH]; + struct rxn_token *rxn_ptr; + struct phase *phase_ptr; + + if (pr.pp_assemblage == FALSE || pr.all == FALSE) + return (OK); + if (pure_phase_unknown == NULL) + return (OK); +/* + * Print heading + */ + print_centered("Phase assemblage"); + output_msg(sformatf("%73s\n", "Moles in assemblage")); + output_msg(sformatf("%-14s%8s%2s%7s %11s", "Phase", "SI", " ", "log IAP", + "log K(T, P)")); + output_msg(sformatf(" %8s%12s%12s", " Initial", " Final", + " Delta")); + output_msg("\n\n"); + + for (j = 0; j < count_unknowns; j++) + { + if (x[j]->type != PP) + continue; + //cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[j]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp * ) x[j]->pp_assemblage_comp_ptr; +/* + * Print saturation index + */ + iap = 0.0; + phase_ptr = x[j]->phase; + if (x[j]->phase->rxn_x == NULL || phase_ptr->in == FALSE) + { + output_msg(sformatf("%-18s%23s", x[j]->phase->name, + "Element not present.")); + } + else + { + phase_ptr = x[j]->phase; + phase_ptr->rxn->logk[delta_v] = calc_delta_v(phase_ptr->rxn, true) - + phase_ptr->logk[vm0]; + if (phase_ptr->rxn->logk[delta_v]) + mu_terms_in_logk = true; + lk = k_calc(phase_ptr->rxn->logk, tk_x, patm_x * PASCAL_PER_ATM); + for (rxn_ptr = phase_ptr->rxn->token + 1; rxn_ptr->s != NULL; + rxn_ptr++) + { + if (rxn_ptr->s != s_eminus) + { + iap += (rxn_ptr->s->lm + rxn_ptr->s->lg) * rxn_ptr->coef; + } + else + { + iap += s_eminus->la * rxn_ptr->coef; + } + } + si = -lk + iap; + /* + for (rxn_ptr = x[j]->phase->rxn_x->token + 1; rxn_ptr->s != NULL; rxn_ptr++) { + iap += rxn_ptr->s->la * rxn_ptr->coef; + } + si = -x[j]->phase->lk + iap; + output_msg(OUTPUT_MESSAGE,"\t%-15s%7.2f%8.2f%8.2f", x[j]->phase->name, (double) si, (double) iap, (double) x[j]->phase->lk); + */ + output_msg(sformatf("%-14s%8.2f %7.2f %8.2f", + x[j]->phase->name, (double) si, (double) iap, (double) lk)); + } +/* + * Print pure phase assemblage data + */ + if (x[j]->moles < 0.0) + x[j]->moles = 0.0; + if (state != TRANSPORT && state != PHAST) + { + sprintf(token, " %11.3e %11.3e %11.3e", + (double) (comp_ptr->Get_moles() + + comp_ptr->Get_delta()), (double) x[j]->moles, + (double) (x[j]->moles - comp_ptr->Get_moles() - + comp_ptr->Get_delta())); + } + else + { + sprintf(token, " %11.3e %11.3e %11.3e", + (double) comp_ptr->Get_initial_moles(), + (double) x[j]->moles, + (double) (x[j]->moles - comp_ptr->Get_initial_moles())); + } + if (x[j]->moles <= 0.0) + { + for (k = 0; k < 11; k++) + { + token[13 + k] = ' '; + } + } + if (comp_ptr->Get_add_formula().size() == 0) + { + output_msg(sformatf("%37s\n", token)); + } + else + { + output_msg(sformatf("\n %-18s%-15s%36s\n", + comp_ptr->Get_add_formula().c_str(), " is reactant", token)); + } + } + output_msg("\n"); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_species(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints description of solution, uses array species_list for + * order of aqueous species. + */ + int i; + const char *name, *name1; + struct master *master_ptr; + LDBLE min; + LDBLE lm; + + if (pr.species == FALSE || pr.all == FALSE) + return (OK); + min = -1000; + print_centered("Distribution of species"); +/* + * Heading for species + */ + if (pitzer_model == TRUE) + { + if (ICON == TRUE) + { + output_msg(sformatf("%60s%10s\n", "MacInnes", "MacInnes")); + output_msg(sformatf("%40s%10s%10s%10s%10s\n", + "MacInnes", "Log", "Log", "Log", "mole V")); + } + else + { + output_msg(sformatf("%60s%10s\n", "Unscaled", "Unscaled")); + output_msg(sformatf("%40s%10s%10s%10s%10s\n", + "Unscaled", "Log", "Log", "Log", "mole V")); + } + } + else + { + output_msg(sformatf("%50s%10s%10s%10s\n", "Log", "Log", "Log", "mole V")); + } + output_msg(sformatf(" %-13s%12s%12s%10s%10s%10s%10s\n\n", "Species", +#ifdef NO_UTF8_ENCODING + "Molality", "Activity", "Molality", "Activity", "Gamma", "cm3/mol")); +#else + "Molality", "Activity", "Molality", "Activity", "Gamma", "cm³/mol")); +#endif +/* + * Print list of species + */ + s_h2o->lm = s_h2o->la; + name = s_hplus->secondary->elt->name; + for (i = 0; i < count_species_list; i++) + { +/* + * Get name of master species + */ + if (species_list[i].s->type == EX) + continue; + if (species_list[i].s->type == SURF) + continue; + if (species_list[i].master_s->secondary != NULL) + { + master_ptr = species_list[i].master_s->secondary; + name1 = species_list[i].master_s->secondary->elt->name; + } + else + { + master_ptr = species_list[i].master_s->primary; + name1 = species_list[i].master_s->primary->elt->name; + } +/* + * Check if new master species, print total molality + */ + if (name1 != name) + { + name = name1; + output_msg(sformatf("%-11s%12.3e\n", name, + (double) (master_ptr->total / mass_water_aq_x))); + min = censor * master_ptr->total / mass_water_aq_x; + if (min > 0) + { + min = log10(min); + } + else + { + min = -1000.; + } + } +/* + * Print species data + */ + if (species_list[i].s->lm > min) + { + if (species_list[i].s == s_h2o) + { + lm = log10(s_h2o->moles / mass_water_aq_x); + } + else + { + lm = species_list[i].s->lm; + } + output_msg(sformatf( + " %-13s%12.3e%12.3e%10.3f%10.3f%10.3f", + species_list[i].s->name, + (double) ((species_list[i].s->moles) / + mass_water_aq_x), + (double) under(species_list[i].s->lm + + species_list[i].s->lg), (double) lm, + (double) (species_list[i].s->lm + + species_list[i].s->lg), + (double) species_list[i].s->lg)); + //if (species_list[i].s->logk[vm_tc] || !strcmp(species_list[i].s->name, "H+")) + if (species_list[i].s->logk[vm_tc] || species_list[i].s == s_hplus) + output_msg(sformatf("%10.2f\n", + (double) species_list[i].s->logk[vm_tc])); + else + output_msg(sformatf(" (0) \n")); + } + } + output_msg(sformatf("\n")); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_surface(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints description of surface, including charge and potential, + * grams and specific area, moles of each species on surface sites, + * and description of diffuse layer if applicable. + */ + cxxSurface *surface_ptr; + std::string name, token; + struct master *master_ptr; + LDBLE molfrac, charge; +/* + * Print surface speciation + */ + surface_ptr = use.Get_surface_ptr(); + if (surface_ptr == NULL || pr.surface == FALSE || pr.all == FALSE) + return (OK); + if (surface_ptr->Get_type() == cxxSurface::CD_MUSIC) + return (print_surface_cd_music()); + + if (state >= REACTION) + { + print_centered("Surface composition"); + } +/* + * Print list of species + */ + + s_h2o->lm = s_h2o->la; + if (use.Get_surface_ptr()->Get_type() == cxxSurface::DDL) + { + output_msg(sformatf("%-14s\n", "Diffuse Double Layer Surface-Complexation Model\n")); + } + else if (use.Get_surface_ptr()->Get_type() == cxxSurface::CCM) + { + output_msg(sformatf("%-14s\n", "Constant Capacitance Surface-Complexation Model\n")); + } + for (int j = 0; j < count_unknowns; j++) + { + /*if (use.Get_surface_ptr()->edl == TRUE) { */ + if (use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM) + { + if (x[j]->type != SURFACE_CB) + continue; + name = x[j]->master[0]->elt->name; + Utilities::replace("_psi", "", name); + } + else + { + if (x[j]->type != SURFACE) + continue; + token = x[j]->master[0]->elt->name; + Utilities::replace("_", " ", token); + std::string::iterator b = token.begin(); + std::string::iterator e = token.end(); + CParser::copy_token(name, b, e); + } + output_msg(sformatf("%-14s\n", name.c_str())); +/* + * Description of surface + */ + if (dl_type_x != cxxSurface::NO_DL) + { + output_msg(sformatf( + "\t%11.3e Surface + diffuse layer charge, eq\n", + (double) x[j]->f)); + } + /*if (use.Get_surface_ptr()->edl == TRUE && diffuse_layer_x == FALSE) { */ + if ((use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM) && dl_type_x == cxxSurface::NO_DL) + { + charge = x[j]->f; + } + else + { + charge = calc_surface_charge(name.c_str()); + } + output_msg(sformatf("\t%11.3e Surface charge, eq\n", + (double) charge)); + if (x[j]->type == SURFACE_CB) + { + cxxSurfaceCharge * charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); + if ((charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()) > 0) + { +#ifdef NO_UTF8_ENCODING + output_msg(sformatf("\t%11.3e sigma, C/m2\n", +#else + output_msg(sformatf("\t%11.3e sigma, C/m²\n", +#endif + (double) (charge * F_C_MOL / + (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams())))); + } + else + { +#ifdef NO_UTF8_ENCODING + output_msg(sformatf("\tundefined sigma, C/m2\n")); +#else + output_msg(sformatf("\tundefined sigma, C/m²\n")); +#endif + } + if (use.Get_surface_ptr()->Get_type() == cxxSurface::CCM) + { + output_msg(sformatf("\t%11.3e capacitance, F/m^2\n", + (double) (charge_ptr->Get_capacitance0()))); + } + output_msg(sformatf("\t%11.3e psi, V\n", + (double) (x[j]->master[0]->s->la * 2 * R_KJ_DEG_MOL * + tk_x * LOG_10 / F_KJ_V_EQ))); + output_msg(sformatf("\t%11.3e -F*psi/RT\n", + (double) (x[j]->master[0]->s->la * (-2) * LOG_10))); + output_msg(sformatf("\t%11.3e exp(-F*psi/RT)\n", + exp(x[j]->master[0]->s->la * (-2) * LOG_10))); + cxxSurfaceComp * comp_ptr = surface_ptr->Find_comp(x[j]->surface_comp); + if (comp_ptr->Get_phase_name().size() > 0) + { + output_msg(sformatf( +#ifdef NO_UTF8_ENCODING + "\t%11.3e specific area, m2/mol %s\n", +#else + "\t%11.3e specific area, m²/mol %s\n", +#endif + (double) charge_ptr->Get_specific_area(), + comp_ptr->Get_phase_name().c_str())); + output_msg(sformatf( +#ifdef NO_UTF8_ENCODING + "\t%11.3e m2 for %11.3e moles of %s\n\n", +#else + "\t%11.3e m² for %11.3e moles of %s\n\n", +#endif + (double) (charge_ptr->Get_grams() * + charge_ptr->Get_specific_area()), + (double) charge_ptr->Get_grams(), + comp_ptr->Get_phase_name().c_str())); + } + else if (comp_ptr->Get_rate_name().size() > 0) + { + output_msg(sformatf( +#ifdef NO_UTF8_ENCODING + "\t%11.3e specific area, m2/mol %s\n", +#else + "\t%11.3e specific area, m²/mol %s\n", +#endif + (double) charge_ptr->Get_specific_area(), + comp_ptr->Get_rate_name().c_str())); + output_msg(sformatf( +#ifdef NO_UTF8_ENCODING + "\t%11.3e m2 for %11.3e moles of %s\n\n", +#else + "\t%11.3e m² for %11.3e moles of %s\n\n", +#endif + (double) (charge_ptr->Get_grams() * + charge_ptr->Get_specific_area()), + (double) charge_ptr->Get_grams(), + comp_ptr->Get_rate_name().c_str())); + } + else + { + output_msg(sformatf( +#ifdef NO_UTF8_ENCODING + "\t%11.3e specific area, m2/g\n", +#else + "\t%11.3e specific area, m²/g\n", +#endif + (double) charge_ptr->Get_specific_area())); +#ifdef NO_UTF8_ENCODING + output_msg(sformatf("\t%11.3e m2 for %11.3e g\n\n", +#else + output_msg(sformatf("\t%11.3e m² for %11.3e g\n\n", +#endif + (double) (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()), + (double) charge_ptr->Get_grams())); + } + if (dl_type_x != cxxSurface::NO_DL) + print_diffuse_layer(charge_ptr); + output_msg(sformatf("\n")); +/* + * Heading for species + */ + for (int k = j - 1; k < count_unknowns; k++) + { + if (x[k]->type != SURFACE) + continue; + if (x[j] != x[k]->potential_unknown) + continue; + master_ptr = x[k]->master[0]; + output_msg(sformatf("%-14s\n", + x[k]->master[0]->elt->name)); + output_msg(sformatf("\t%11.3e moles", + (double) x[k]->moles)); + cxxSurfaceComp * comp_k_ptr = surface_ptr->Find_comp(x[k]->surface_comp); + if (comp_k_ptr->Get_phase_name().size() > 0) + { + output_msg(sformatf("\t[%g mol/(mol %s)]\n", + (double) comp_k_ptr->Get_phase_proportion(), + comp_k_ptr->Get_phase_name().c_str())); + } + else if (comp_k_ptr->Get_rate_name().size() > 0) + { + output_msg(sformatf( + "\t[%g mol/(mol kinetic reactant %s)]\n", + (double) comp_k_ptr->Get_phase_proportion(), + comp_k_ptr->Get_rate_name().c_str())); + } + else + { + output_msg(sformatf("\n")); + } + output_msg(sformatf("\t%-15s%12s%12s%12s%12s\n", " ", + " ", "Mole", " ", "Log")); + output_msg(sformatf("\t%-15s%12s%12s%12s%12s\n\n", + "Species", "Moles", "Fraction", "Molality", + "Molality")); + for (int i = 0; i < count_species_list; i++) + { + if (species_list[i].master_s != master_ptr->s) + continue; +/* + * Print species data + */ + if (x[k]->moles >= MIN_RELATED_SURFACE) + { + molfrac = + (LDBLE) (species_list[i].s->moles) / x[k]->moles * + species_list[i].s->equiv; + } + else + { + molfrac = 0.0; + } + output_msg(sformatf( + "\t%-15s%12.3e%12.3f%12.3e%12.3f\n", + species_list[i].s->name, + (double) species_list[i].s->moles, + (double) molfrac, + (double) (species_list[i].s->moles / + mass_water_aq_x), + log10(species_list[i].s->moles / + mass_water_aq_x))); + } + output_msg(sformatf("\n")); + } + } + else + { + int k = j; + master_ptr = x[k]->master[0]; + output_msg(sformatf("%-14s\n", x[k]->master[0]->elt->name)); + output_msg(sformatf("\t%11.3e moles\n", + (double) x[k]->moles)); + output_msg(sformatf("\t%-15s%12s%12s%12s%12s\n", " ", " ", + "Mole", " ", "Log")); + output_msg(sformatf("\t%-15s%12s%12s%12s%12s\n\n", + "Species", "Moles", "Fraction", "Molality", + "Molality")); + for (int i = 0; i < count_species_list; i++) + { + if (species_list[i].master_s != master_ptr->s) + continue; +/* + * Print species data + */ + if (x[k]->moles >= MIN_RELATED_SURFACE) + { + molfrac = + (double) (species_list[i].s->moles) / x[k]->moles * + species_list[i].s->equiv; + } + else + { + molfrac = 0.0; + } + output_msg(sformatf( + "\t%-15s%12.3e%12.3f%12.3e%12.3f\n", + species_list[i].s->name, + (double) species_list[i].s->moles, + (double) molfrac, + (double) (species_list[i].s->moles / + mass_water_aq_x), + log10(species_list[i].s->moles / mass_water_aq_x))); + } + output_msg(sformatf("\n")); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_surface_cd_music(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints description of cd music surfaces, including charge and potential, + * grams and specific area, moles of each species on surface sites, + * and description of diffuse layer if applicable. + */ + cxxSurface *surface_ptr; + std::string name; + struct master *master_ptr, *master_ptr0, *master_ptr1, *master_ptr2; + struct unknown *unknown_ptr0, *unknown_ptr1, *unknown_ptr2; + LDBLE molfrac, charge0, charge1, charge2, sum; +/* + * Print surface speciation + */ + surface_ptr = use.Get_surface_ptr(); + if (surface_ptr == NULL || pr.surface == FALSE || pr.all == FALSE) + return (OK); + + if (state >= REACTION) + { + print_centered("Surface composition"); + } +/* + * Print list of species + */ + + s_h2o->lm = s_h2o->la; + for (int j = 0; j < count_unknowns; j++) + { + if (x[j]->type != SURFACE_CB) + continue; + name = x[j]->master[0]->elt->name; + Utilities::replace("_psi", "", name); + output_msg(sformatf("%-14s\n", name.c_str())); + cxxSurfaceCharge * charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge); +/* + * Description of surface + */ + if (dl_type_x != cxxSurface::NO_DL) + { + output_msg(sformatf( + "\t%11.3e Surface + diffuse layer charge, eq\n\n", + (double) (x[j + 2]->f + (charge_ptr->Get_sigma0() + charge_ptr->Get_sigma1()) * (charge_ptr->Get_specific_area() * charge_ptr->Get_grams()) / F_C_MOL))); + } + master_ptr0 = + surface_get_psi_master(charge_ptr->Get_name().c_str(), SURF_PSI); + master_ptr1 = + surface_get_psi_master(charge_ptr->Get_name().c_str(), SURF_PSI1); + master_ptr2 = + surface_get_psi_master(charge_ptr->Get_name().c_str(), SURF_PSI2); + unknown_ptr0 = x[master_ptr0->unknown->number]; + unknown_ptr1 = x[master_ptr1->unknown->number]; + unknown_ptr2 = x[master_ptr2->unknown->number]; + + charge0 = unknown_ptr0->f; + charge1 = unknown_ptr1->f; + if (dl_type_x != cxxSurface::NO_DL) + { + charge2 = + charge_ptr->Get_sigma2() * + (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()) / F_C_MOL; + } + else + { + charge2 = unknown_ptr2->f; + } + sum = 0; + for (int k = 0; k < x[j]->count_comp_unknowns; k++) + { + sum += + x[j]->comp_unknowns[k]->moles * + x[j]->comp_unknowns[k]->master[0]->s->z; + } + output_msg(sformatf("\t%11.3e Surface charge, plane 0, eq\n", + (double) (charge0 + sum))); + output_msg(sformatf("\t%11.3e Surface charge, plane 1, eq\n", + (double) charge1)); + output_msg(sformatf("\t%11.3e Surface charge, plane 2, eq\n", + (double) charge2)); + output_msg(sformatf( + "\t%11.3e Sum of surface charge, all planes, eq\n\n", + (double) (charge0 + sum + charge1 + charge2))); + if (x[j]->type == SURFACE_CB) + { + if ((charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()) > 0) + { + output_msg(sformatf( +#ifdef NO_UTF8_ENCODING + "\t%11.3e sigma, plane 0, C/m2\n", +#else + "\t%11.3e sigma, plane 0, C/m²\n", +#endif + (double) charge_ptr->Get_sigma0())); + output_msg(sformatf( +#ifdef NO_UTF8_ENCODING + "\t%11.3e sigma, plane 1, C/m2\n", +#else + "\t%11.3e sigma, plane 1, C/m²\n", +#endif + (double) charge_ptr->Get_sigma1())); + output_msg(sformatf( +#ifdef NO_UTF8_ENCODING + "\t%11.3e sigma, plane 2, C/m2\n", +#else + "\t%11.3e sigma, plane 2, C/m²\n", +#endif + (double) charge_ptr->Get_sigma2())); + output_msg(sformatf( +#ifdef NO_UTF8_ENCODING + "\t%11.3e sigma, diffuse layer, C/m2\n\n", +#else + "\t%11.3e sigma, diffuse layer, C/m²\n\n", +#endif + (double) charge_ptr->Get_sigmaddl())); + } + else + { +#ifdef NO_UTF8_ENCODING + output_msg(sformatf("\tundefined sigma, C/m2\n")); +#else + output_msg(sformatf("\tundefined sigma, C/m²\n")); +#endif + } + output_msg(sformatf("\t%11.3e psi, plane 0, V\n", + (double) (-master_ptr0->s->la * LOG_10 * R_KJ_DEG_MOL * tk_x / F_KJ_V_EQ))); + output_msg(sformatf("\t%11.3e psi, plane 1, V\n", + (double) (-master_ptr1->s->la * LOG_10 * R_KJ_DEG_MOL * tk_x / F_KJ_V_EQ))); + output_msg(sformatf("\t%11.3e psi, plane 2, V\n\n", + (double) (-master_ptr2->s->la * LOG_10 * R_KJ_DEG_MOL * tk_x / F_KJ_V_EQ))); + output_msg(sformatf("\t%11.3e exp(-F*psi/RT), plane 0\n", + (double) (exp(master_ptr0->s->la * LOG_10)))); + output_msg(sformatf("\t%11.3e exp(-F*psi/RT), plane 1\n", + (double) (exp(master_ptr1->s->la * LOG_10)))); + output_msg(sformatf( + "\t%11.3e exp(-F*psi/RT), plane 2\n\n", + (double) (exp(master_ptr2->s->la * LOG_10)))); + + output_msg(sformatf("\t%11.3e capacitance 0-1, F/m^2\n", + (double) (charge_ptr->Get_capacitance0()))); + output_msg(sformatf("\t%11.3e capacitance 1-2, F/m^2\n", + (double) (charge_ptr->Get_capacitance1()))); + cxxSurfaceComp * comp_ptr = surface_ptr->Find_comp(x[j]->surface_comp); + if (comp_ptr->Get_phase_name().size() > 0) + { + output_msg(sformatf( + "\t%11.3e specific area, m^2/mol %s\n", + (double) charge_ptr->Get_specific_area(), + comp_ptr->Get_phase_name().c_str())); + output_msg(sformatf( + "\t%11.3e m^2 for %11.3e moles of %s\n\n", + (double) (charge_ptr->Get_grams() * + charge_ptr->Get_specific_area()), + (double) charge_ptr->Get_grams(), + comp_ptr->Get_phase_name().c_str())); + } + else if (comp_ptr->Get_rate_name().size() > 0) + { + output_msg(sformatf( + "\t%11.3e specific area, m^2/mol %s\n", + (double) charge_ptr->Get_specific_area(), + comp_ptr->Get_rate_name().c_str())); + output_msg(sformatf( + "\t%11.3e m^2 for %11.3e moles of %s\n\n", + (double) (charge_ptr->Get_grams() * + charge_ptr->Get_specific_area()), + (double) charge_ptr->Get_grams(), + comp_ptr->Get_rate_name().c_str())); + } + else + { + output_msg(sformatf( + "\t%11.3e specific area, m^2/g\n", + (double) charge_ptr->Get_specific_area())); + output_msg(sformatf("\t%11.3e m^2 for %11.3e g\n\n", + (double) (charge_ptr->Get_specific_area() * + charge_ptr->Get_grams()), + (double) charge_ptr->Get_grams())); + } + if (dl_type_x != cxxSurface::NO_DL) + print_diffuse_layer(charge_ptr); + output_msg(sformatf("\n")); +/* + * Heading for species + */ + for (int k = j - 1; k < count_unknowns; k++) + { + if (x[k]->type != SURFACE) + continue; + if (x[j] != x[k]->potential_unknown) + continue; + master_ptr = x[k]->master[0]; + output_msg(sformatf("%-14s\n", + x[k]->master[0]->elt->name)); + output_msg(sformatf("\t%11.3e moles", + (double) x[k]->moles)); + cxxSurfaceComp * comp_k_ptr = surface_ptr->Find_comp(x[k]->surface_comp); + if (comp_k_ptr->Get_phase_name().size() > 0) + { + output_msg(sformatf("\t[%g mol/(mol %s)]\n", + (double) comp_k_ptr->Get_phase_proportion(), + comp_k_ptr->Get_phase_name().c_str())); + } + else if (comp_k_ptr->Get_rate_name().size() > 0) + { + output_msg(sformatf( + "\t[%g mol/(mol kinetic reactant %s)]\n", + (double) comp_k_ptr->Get_phase_proportion(), + comp_k_ptr->Get_rate_name().c_str())); + } + else + { + output_msg(sformatf("\n")); + } + output_msg(sformatf("\t%-20s%12s%12s%12s%12s\n", " ", + " ", "Mole", " ", "Log")); + output_msg(sformatf("\t%-20s%12s%12s%12s%12s\n\n", + "Species", "Moles", "Fraction", "Molality", + "Molality")); + for (int i = 0; i < count_species_list; i++) + { + if (species_list[i].master_s != master_ptr->s) + continue; +/* + * Print species data + */ + if (x[k]->moles >= MIN_RELATED_SURFACE) + { + molfrac = + (LDBLE) (species_list[i].s->moles) / x[k]->moles * + species_list[i].s->equiv; + } + else + { + molfrac = 0.0; + } + output_msg(sformatf( + "\t%-20s%12.3e%12.3f%12.3e%12.3f\n", + species_list[i].s->name, + (double) species_list[i].s->moles, + (double) molfrac, + (double) (species_list[i].s->moles / + mass_water_aq_x), + log10(species_list[i].s->moles / + mass_water_aq_x))); + } + output_msg(sformatf("\n")); + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_totals(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Print total concentrations of elements, molality and moles. + */ + int i, pure_water; + LDBLE dens; + + if (pr.totals == FALSE || pr.all == FALSE) + return (OK); + print_centered("Solution composition"); + pure_water = TRUE; + output_msg(sformatf("\t%-15s%12s%12s\n\n", "Elements", "Molality", + "Moles")); + for (i = 0; i < count_unknowns; i++) + { + if (x[i] == alkalinity_unknown) + { + output_msg(sformatf("\t%-15s%12.3e%12.3e\n", + "Alkalinity", + (double) (x[i]->f / mass_water_aq_x), + (double) x[i]->f)); + pure_water = FALSE; + } + if (x[i] == ph_unknown) + continue; + if (x[i] == pe_unknown) + continue; + if (x[i] == charge_balance_unknown) + { + output_msg(sformatf("\t%-15s%12.3e%12.3e", + x[i]->description, + (double) (x[i]->sum / mass_water_aq_x), + (double) x[i]->sum)); + output_msg(sformatf(" Charge balance\n")); + pure_water = FALSE; + continue; + } + if (x[i]->type == SOLUTION_PHASE_BOUNDARY) + { + output_msg(sformatf("\t%-15s%12.3e%12.3e", + x[i]->description, + (double) (x[i]->sum / mass_water_aq_x), + (double) x[i]->sum)); + output_msg(sformatf(" Equilibrium with %s\n", + x[i]->phase->name)); + pure_water = FALSE; + continue; + } + if (x[i]->type == MB) + { + output_msg(sformatf("\t%-15s%12.3e%12.3e\n", + x[i]->description, + (double) (x[i]->sum / mass_water_aq_x), + (double) x[i]->sum)); + pure_water = FALSE; + } + } + + if (pure_water == TRUE) + { + output_msg(sformatf("\t%-15s\n", "Pure water")); + } +/* + * Description of solution + */ + output_msg(sformatf("\n")); + print_centered("Description of solution"); +/* + * pH + */ + output_msg(sformatf("%45s%7.3f ", "pH = ", + (double) (-(s_hplus->la)))); + if (ph_unknown == NULL) + { + output_msg(sformatf("\n")); + } + else if (ph_unknown == charge_balance_unknown) + { + output_msg(sformatf(" Charge balance\n")); + } + else if (ph_unknown->type == SOLUTION_PHASE_BOUNDARY) + { + output_msg(sformatf(" Equilibrium with %s\n", + ph_unknown->phase->name)); + } + else if (ph_unknown->type == ALK) + { + output_msg(sformatf(" Adjust alkalinity\n")); + } +/* + * pe + */ + output_msg(sformatf("%45s%7.3f ", "pe = ", + (double) (-(s_eminus->la)))); + if (pe_unknown == NULL) + { + output_msg(sformatf("\n")); + } + else if (pe_unknown == charge_balance_unknown) + { + output_msg(sformatf(" Charge balance\n")); + } + else if (pe_unknown->type == SOLUTION_PHASE_BOUNDARY) + { + output_msg(sformatf(" Equilibrium with %s\n", + pe_unknown->phase->name)); + } + else if (pe_unknown->type == MH) + { + output_msg(sformatf(" Adjusted to redox equilibrium\n")); + } +/* + * Others + */ + calc_SC(); + if (SC > 0) + { + //output_msg(sformatf("%36s%i%7s%i\n", + output_msg(sformatf("%35s%3.0f%7s%i\n", +#ifdef NO_UTF8_ENCODING + "Specific Conductance (uS/cm, ", tc_x, "oC) = ", (int) SC)); +#else + "Specific Conductance (µS/cm, ", tc_x, "°C) = ", (int) SC)); +#endif + } +/* VP: Density Start */ + if (print_density) + { + dens = calc_dens(); +#ifdef NO_UTF8_ENCODING + output_msg(sformatf("%45s%9.5f", "Density (g/cm3) = ", +#else + output_msg(sformatf("%45s%9.5f", "Density (g/cm³) = ", +#endif + (double) dens)); + if (state == INITIAL_SOLUTION && use.Get_solution_ptr()->Get_initial_data()->Get_calc_density()) + { + output_msg(sformatf(" (Iterated) ")); + } + if (dens > 1.999) output_msg(sformatf("%18s", " (Program limit)")); + output_msg(sformatf("\n")); + output_msg(sformatf("%45s%9.5f\n", " Volume (L) = ", + (double) calc_solution_volume())); + } +/* VP: Density End */ +#ifdef NPP + if (print_viscosity) + { + output_msg(sformatf("%45s%9.5f", "Viscosity (mPa s) = ", + (double) viscos)); + if (tc_x > 200 && !pure_water) + { + output_msg(sformatf("%18s\n", +#ifdef NO_UTF8_ENCODING + " (solute contributions limited to 200 oC)")); +#else + " (solute contributions limited to 200 °C)")); +#endif + } + else output_msg(sformatf("\n")); + } +#endif + output_msg(sformatf("%45s%7.3f\n", "Activity of water = ", + exp(s_h2o->la * LOG_10))); + output_msg(sformatf("%45s%11.3e\n", "Ionic strength (mol/kgw) = ", + (double) mu_x)); + output_msg(sformatf("%45s%11.3e\n", "Mass of water (kg) = ", + (double) mass_water_aq_x)); + if (alkalinity_unknown == NULL) + { + output_msg(sformatf("%45s%11.3e\n", + "Total alkalinity (eq/kg) = ", + (double) (total_alkalinity / mass_water_aq_x))); + } + if (carbon_unknown == NULL && total_carbon) + { + output_msg(sformatf("%45s%11.3e\n", + "Total carbon (mol/kg) = ", + (double) (total_carbon / mass_water_aq_x))); + } + if (total_co2) + output_msg(sformatf("%45s%11.3e\n", "Total CO2 (mol/kg) = ", + (double) (total_co2 / mass_water_aq_x))); +#ifdef NO_UTF8_ENCODING + output_msg(sformatf("%45s%6.2f\n", "Temperature (oC) = ", +#else + output_msg(sformatf("%45s%6.2f\n", "Temperature (°C) = ", +#endif + (double) tc_x)); + + if (patm_x != 1.0) + { + /* only print if different than default */ + output_msg(sformatf("%45s%5.2f\n", "Pressure (atm) = ", + (double) patm_x)); + } + + if (potV_x) + { + output_msg(sformatf("%45s%5.2f\n", "Electrical Potential (Volt) = ", + (double)potV_x)); + } + + output_msg(sformatf("%45s%11.3e\n", "Electrical balance (eq) = ", + (double) cb_x)); + output_msg(sformatf("%45s%6.2f\n", + "Percent error, 100*(Cat-|An|)/(Cat+|An|) = ", + (double) (100 * cb_x / total_ions_x))); + output_msg(sformatf("%45s%3d\n", "Iterations = ", iterations)); + if (pitzer_model == TRUE || sit_model == TRUE) + { + if (always_full_pitzer == FALSE) + { + output_msg(sformatf("%45s%3d\n", "Gamma iterations = ", + gamma_iterations)); + } + else + { + output_msg(sformatf("%45s%3d\n", "Gamma iterations = ", + iterations)); + } + output_msg(sformatf("%45s%9.5f\n", "Osmotic coefficient = ", + (double) COSMOT)); + if (print_density) output_msg(sformatf("%45s%9.5f\n", "Density of water = ", + (double) DW0)); + } + output_msg(sformatf("%45s%e\n", "Total H = ", (double) total_h_x)); + output_msg(sformatf("%45s%e\n", "Total O = ", (double) total_o_x)); + output_msg(sformatf("\n")); + + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_user_print(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Print with user defined BASIC print routine + */ + cxxKinetics *kinetics_ptr; + + char l_command[] = "run"; + + if (pr.user_print == FALSE || pr.all == FALSE) + return (OK); + if (user_print->commands == NULL) + return (OK); + kinetics_ptr = NULL; + if (use.Get_kinetics_in() == TRUE) + { + kinetics_ptr = use.Get_kinetics_ptr(); + if (state == TRANSPORT || state == PHAST || state == ADVECTION) + { + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_kinetics_user())); + } + else + { + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, -2)); + } + } + print_centered("User print"); + if (user_print->new_def == TRUE) + { + /* basic_renumber(user_print->commands, &user_print->linebase, &user_print->varbase, &user_print->loopbase); */ + if (basic_compile + (user_print->commands, &user_print->linebase, + &user_print->varbase, &user_print->loopbase) != 0) + { + error_msg("Fatal Basic error in USER_PRINT.", STOP); + } + user_print->new_def = FALSE; + } + if (basic_run + (l_command, user_print->linebase, user_print->varbase, + user_print->loopbase) != 0) + { + error_msg("Fatal Basic error in USER_PRINT.", STOP); + } + output_msg(sformatf("\n")); + if (use.Get_kinetics_in() == TRUE) + { + use.Set_kinetics_ptr(kinetics_ptr); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_using(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Print entities used in calculation + */ + cxxMix * mix_ptr; + cxxSolution *solution_ptr; + + if (pr.use == FALSE || pr.all == FALSE) + return (OK); + if (state < REACTION || phast == TRUE) + return (OK); +/* + * Mixture or Solution + */ + if (use.Get_mix_in() == TRUE) + { + if (state == TRANSPORT) + { + mix_ptr = Utilities::Rxn_find(Rxn_mix_map, use.Get_n_mix_user()); + } + else + { + mix_ptr = Utilities::Rxn_find(Rxn_mix_map, use.Get_n_mix_user_orig()); + } + if (mix_ptr == NULL) + { + mix_ptr = use.Get_mix_ptr(); + } + if (mix_ptr != NULL) + { + if (state == TRANSPORT) + { + output_msg(sformatf("Using mix %d.\t%s\n", + use.Get_n_mix_user(), mix_ptr->Get_description().c_str())); + } + else + { + output_msg(sformatf("Using mix %d.\t%s\n", + use.Get_n_mix_user_orig(), mix_ptr->Get_description().c_str())); + } + + } + } + else + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, use.Get_n_solution_user()); + output_msg(sformatf("Using solution %d.\t%s\n", + use.Get_n_solution_user(), solution_ptr->Get_description().c_str())); + } +/* + * Exchange and surface + */ + if (use.Get_exchange_in()) + { + cxxExchange *exchange_ptr = Utilities::Rxn_find(Rxn_exchange_map, use.Get_n_exchange_user()); + output_msg(sformatf("Using exchange %d.\t%s\n", + use.Get_n_exchange_user(), exchange_ptr->Get_description().c_str())); + } + if (use.Get_surface_in()) + { + cxxSurface *surface_ptr = Utilities::Rxn_find(Rxn_surface_map, use.Get_n_surface_user()); + output_msg(sformatf("Using surface %d.\t%s\n", + use.Get_n_surface_user(), surface_ptr->Get_description().c_str())); + } + if (use.Get_pp_assemblage_in() == TRUE) + { + cxxPPassemblage * pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, use.Get_n_pp_assemblage_user()); + output_msg(sformatf("Using pure phase assemblage %d.\t%s\n", + use.Get_n_pp_assemblage_user(), pp_assemblage_ptr->Get_description().c_str())); + } + if (use.Get_ss_assemblage_in() == TRUE) + { + cxxSSassemblage * ss_assemblage_ptr = Utilities::Rxn_find(Rxn_ss_assemblage_map, use.Get_n_ss_assemblage_user()); + output_msg(sformatf( + "Using solid solution assemblage %d.\t%s\n", + use.Get_n_ss_assemblage_user(), + ss_assemblage_ptr->Get_description().c_str())); + } + if (use.Get_gas_phase_in()) + { + cxxGasPhase * gas_phase_ptr = Utilities::Rxn_find(Rxn_gas_phase_map, use.Get_n_gas_phase_user()); + output_msg(sformatf("Using gas phase %d.\t%s\n", + use.Get_n_gas_phase_user(), gas_phase_ptr->Get_description().c_str())); + } + if (use.Get_temperature_in()) + { + cxxTemperature *temperature_ptr = Utilities::Rxn_find(Rxn_temperature_map, use.Get_n_temperature_user()); + output_msg(sformatf("Using temperature %d.\t%s\n", + use.Get_n_temperature_user(), temperature_ptr->Get_description().c_str())); + } + if (use.Get_pressure_in()) + { + cxxPressure *pressure_ptr = Utilities::Rxn_find(Rxn_pressure_map, use.Get_n_pressure_user()); + output_msg(sformatf("Using pressure %d.\t%s\n", + use.Get_n_pressure_user(), pressure_ptr->Get_description().c_str())); + } + if (use.Get_reaction_in()) + { + if (state != TRANSPORT || transport_step > 0) + { + cxxReaction *reaction_ptr = Utilities::Rxn_find(Rxn_reaction_map, use.Get_n_reaction_user()); + output_msg(sformatf("Using reaction %d.\t%s\n", + use.Get_n_reaction_user(), reaction_ptr->Get_description().c_str())); + } + } + if (use.Get_kinetics_in()) + { + cxxKinetics * kinetics_ptr; + if (state == TRANSPORT || state == PHAST || state == ADVECTION) + { + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_kinetics_user()); + } + else + { + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, -2); + } + output_msg(sformatf("Using kinetics %d.\t%s\n", + use.Get_n_kinetics_user(), kinetics_ptr->Get_description().c_str())); + } + output_msg(sformatf("\n")); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_gas_phase(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints selected gas phase data + */ + //int i; + LDBLE p, total_moles, volume; + LDBLE moles; + bool PR = false; + + //if (punch.count_gases <= 0) + if (current_selected_output->Get_gases().size() == 0) + return (OK); + p = 0.0; + total_moles = 0.0; + volume = 0.0; + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + + if (gas_unknown != NULL && use.Get_gas_phase_ptr() != NULL) + { + if (gas_phase_ptr->Get_v_m() >= 0.01) + { + PR = true; + } + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + if (gas_unknown->moles >= 1e-12) + { + gas_phase_ptr->Set_total_moles(gas_unknown->moles); + gas_phase_ptr->Set_volume( + gas_phase_ptr->Get_total_moles() * R_LITER_ATM * tk_x / + gas_phase_ptr->Get_total_p()); + if (PR) + { + gas_phase_ptr->Set_volume(gas_phase_ptr->Get_v_m() * gas_unknown->moles); + } + } + else + { + gas_phase_ptr->Set_volume(0); + } + } + p = gas_phase_ptr->Get_total_p(); + total_moles = gas_phase_ptr->Get_total_moles(); + //volume = total_moles * R_LITER_ATM * tk_x / gas_phase_ptr->Get_total_p(); + //if (gas_phase_ptr->Get_v_m() > 0.03) + // volume = 0.03 * gas_phase_ptr->Get_total_moles(); + volume = gas_phase_ptr->Get_volume(); + + } + if (!current_selected_output->Get_high_precision()) + { + fpunchf("pressure", "%12.4e\t", (double) p); + fpunchf("total mol", "%12.4e\t", (double) total_moles); + fpunchf("volume", "%12.4e\t", (double) volume); + } + else + { + fpunchf("pressure", "%20.12e\t", (double) p); + fpunchf("total mol", "%20.12e\t", (double) total_moles); + fpunchf("volume", "%20.12e\t", (double) volume); + } + for (size_t i = 0; i < current_selected_output->Get_gases().size(); i++) + { + moles = 0.0; + if (gas_phase_ptr != NULL && current_selected_output->Get_gases()[i].second != NULL) + { + for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++) + { + cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[j]); + int k; + struct phase *phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str() , &k, FALSE); + if (phase_ptr != current_selected_output->Get_gases()[i].second) + continue; + moles = phase_ptr->moles_x; + if (moles <= MIN_TOTAL) + moles = 0.0; + break; + } + } + if (!current_selected_output->Get_high_precision()) + { + fpunchf(sformatf("g_%s", current_selected_output->Get_gases()[i].first.c_str()), "%12.4e\t", (double) moles); + } + else + { + fpunchf(sformatf("g_%s", current_selected_output->Get_gases()[i].first.c_str()), "%20.12e\t", + (double) moles); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_ss_assemblage(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints solid solution composition if present + */ + //int j, k; + int found; + LDBLE moles; + +/* + * Print solid solutions + */ + for (size_t k = 0; k < current_selected_output->Get_s_s().size(); k++) + { + found = FALSE; + if (use.Get_ss_assemblage_ptr() != NULL) + { + std::vector ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize(); + for (int j = 0; j < (int) ss_ptrs.size(); j++) + { + cxxSS * ss_ptr = ss_ptrs[j]; + for (int i = 0; i < (int) ss_ptr->Get_ss_comps().size(); i++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[i]); + + if (strcmp_nocase(current_selected_output->Get_s_s()[k].first.c_str(), comp_ptr->Get_name().c_str()) == 0) + { + if (ss_ptr->Get_ss_in()) + { + moles = comp_ptr->Get_moles(); + } + else + { + moles = 0; + } + if (!current_selected_output->Get_high_precision()) + { + fpunchf(sformatf("s_%s", current_selected_output->Get_s_s()[k].first.c_str()), + "%12.4e\t", (double) moles); + } + else + { + fpunchf(sformatf("s_%s", current_selected_output->Get_s_s()[k].first.c_str()), + "%20.12e\t", (double) moles); + } + found = TRUE; + break; + } + } + if (found == TRUE) + break; + } + } + if (found == FALSE) + { + if (!current_selected_output->Get_high_precision()) + { + fpunchf(sformatf("s_%s", current_selected_output->Get_s_s()[k].first.c_str()), "%12.4e\t", (double) 0.0); + } + else + { + fpunchf(sformatf("s_%s", current_selected_output->Get_s_s()[k].first.c_str()), "%20.12e\t", + (double) 0.0); + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_totals(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Print total concentrations of elements, molality and moles. + */ + //int j; + LDBLE molality; + + for (size_t j = 0; j < current_selected_output->Get_totals().size(); j++) + { + if (current_selected_output->Get_totals()[j].second == NULL) + { + molality = 0.0; + } + else if (((struct master *) current_selected_output->Get_totals()[j].second)->primary == TRUE) + { + if (strncmp(current_selected_output->Get_totals()[j].first.c_str(), "Alkalinity", 20) == 0) + { + molality = total_alkalinity / mass_water_aq_x; + } else + { + molality = ((struct master *) current_selected_output->Get_totals()[j].second)->total_primary / mass_water_aq_x; + } + } + else + { + molality = ((struct master *) current_selected_output->Get_totals()[j].second)->total / mass_water_aq_x; + } + if (!current_selected_output->Get_high_precision()) + { + fpunchf(sformatf("%s(mol/kgw)", current_selected_output->Get_totals()[j].first.c_str()), + "%12.4e\t", (double) molality); + } + else + { + fpunchf(sformatf("%s(mol/kgw)", current_selected_output->Get_totals()[j].first.c_str()), + "%20.12e\t", (double) molality); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_molalities(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Print concentrations of species (aq, ex, surf) + */ + //int j; + LDBLE molality; + + for (size_t j = 0; j < current_selected_output->Get_molalities().size(); j++) + { + molality = 0.0; + if (current_selected_output->Get_molalities()[j].second != NULL + && ((struct species *) current_selected_output->Get_molalities()[j].second)->in == TRUE) + { + molality = ((struct species *) current_selected_output->Get_molalities()[j].second)->moles / mass_water_aq_x; + } + if (!current_selected_output->Get_high_precision()) + { + fpunchf(sformatf("m_%s(mol/kgw)", current_selected_output->Get_molalities()[j].first.c_str()), + "%12.4e\t", (double) molality); + } + else + { + fpunchf(sformatf("m_%s(mol/kgw)", current_selected_output->Get_molalities()[j].first.c_str()), + "%20.12e\t", (double) molality); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_activities(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Print concentrations of species (aq, ex, surf) + */ + //int j; + LDBLE la; + + for (size_t j = 0; j < current_selected_output->Get_activities().size(); j++) + { + la = -999.999; + if (current_selected_output->Get_activities()[j].second != NULL + && ((struct species *) current_selected_output->Get_activities()[j].second)->in == TRUE) + { + /*la = punch.activities[j].s->lm + punch.activities[j].s->lg; */ + la = log_activity(current_selected_output->Get_activities()[j].first.c_str()); + } + if (!current_selected_output->Get_high_precision()) + { + fpunchf(sformatf("la_%s", current_selected_output->Get_activities()[j].first.c_str()), "%12.4e\t", + (double) la); + } + else + { + fpunchf(sformatf("la_%s", current_selected_output->Get_activities()[j].first.c_str()), + "%20.12e\t", (double) la); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_pp_assemblage(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints masses of selected pure_phases in pp_assemblage + */ + //int i, j; + LDBLE moles, delta_moles; + for (size_t i = 0; i < current_selected_output->Get_pure_phases().size(); i++) + { + delta_moles = 0; + moles = 0.0; + if (current_selected_output->Get_pure_phases()[i].second != NULL) + { + for (int j = 0; j < count_unknowns; j++) + { + if (x == NULL || x[j]->type != PP) + continue; + //cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[j]->pp_assemblage_comp_name); + cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp * ) x[j]->pp_assemblage_comp_ptr; +/* + * Print pure phase assemblage data + */ + if (current_selected_output->Get_pure_phases()[i].second != x[j]->phase) + continue; + if (state != TRANSPORT && state != PHAST) + { + moles = x[j]->moles; + delta_moles = + x[j]->moles - comp_ptr->Get_moles() - + comp_ptr->Get_delta(); + } + else + { + moles = x[j]->moles; + delta_moles = + x[j]->moles - comp_ptr->Get_initial_moles(); + } + break; + } + } + if (!current_selected_output->Get_high_precision()) + { + fpunchf(current_selected_output->Get_pure_phases()[i].first.c_str(), "%12.4e\t", (double) moles); + fpunchf(sformatf("d_%s", current_selected_output->Get_pure_phases()[i].first.c_str()), "%12.4e\t", + (double) delta_moles); + } + else + { + fpunchf(current_selected_output->Get_pure_phases()[i].first.c_str(), "%20.12e\t", (double) moles); + fpunchf(sformatf("d_%s", current_selected_output->Get_pure_phases()[i].first.c_str()), + "%20.12e\t", (double) delta_moles); + } + } + return (OK); +} + +#define PHAST_NULL(x) (phast ? NULL : x) +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_identifiers(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * prints series of integers to identify simulation number, + * state of calculations, reaction or transport step number, + * and temp, ph, pe, and mass of water for each line + * of selected output. + */ + const char *sformat; + const char *dformat; + const char *gformat; + int i; + char token[MAX_LENGTH]; + + //if (punch.in == FALSE) + // return (OK); + if (!current_selected_output->Get_high_precision()) + { + sformat = "%12s\t"; + dformat = "%12d\t"; + gformat = "%12g\t"; + } + else + { + sformat = "%20s\t"; + dformat = "%20d\t"; + gformat = "%20g\t"; + } + +/* + * simulation or simul_tr + */ + + if (current_selected_output->Get_sim()) + { + if (state != TRANSPORT && state != PHAST) + { + fpunchf(PHAST_NULL("sim"), dformat, simulation); + } + else + { + fpunchf(PHAST_NULL("sim"), dformat, simul_tr); + } + } + if (current_selected_output->Get_state()) + { + switch (state) + { + case 0: + strcpy(token, "init"); + break; + case 1: + strcpy(token, "i_soln"); + break; + case 2: + strcpy(token, "i_exch"); + break; + case 3: + strcpy(token, "i_surf"); + break; + case 4: + strcpy(token, "i_gas"); + break; + case 5: + strcpy(token, "react"); + break; + case 6: + strcpy(token, "inverse"); + break; + case 7: + strcpy(token, "advect"); + break; + case 8: + strcpy(token, "transp"); + break; + default: + strcpy(token, "unknown"); + break; + } + fpunchf(PHAST_NULL("state"), sformat, token); + + } +/* + * solution number or cell number and time + */ + if (current_selected_output->Get_soln()) + { + if (state == TRANSPORT || state == PHAST) + { + fpunchf(PHAST_NULL("soln"), dformat, cell); + } + else if (state == ADVECTION) + { + fpunchf(PHAST_NULL("soln"), dformat, use.Get_n_solution_user()); + } + else if (state < REACTION) + { + fpunchf(PHAST_NULL("soln"), dformat, use.Get_solution_ptr()->Get_n_user()); + } + else + { + if (use.Get_mix_in() == TRUE) + { + if (state != TRANSPORT) + { + fpunchf(PHAST_NULL("soln"), dformat, use.Get_n_mix_user_orig()); + } + else + { + fpunchf(PHAST_NULL("soln"), dformat, use.Get_n_mix_user()); + } + + } + else + { + fpunchf(PHAST_NULL("soln"), dformat, use.Get_n_solution_user()); + } + } + } + if (current_selected_output->Get_dist()) + { + if (state == ADVECTION) + { + fpunchf(PHAST_NULL("dist_x"), gformat, (double)use.Get_n_solution_user()); + } + else if (state == TRANSPORT) + { + fpunchf(PHAST_NULL("dist_x"), gformat, + (double) cell_data[cell].mid_cell_x); + } + else + { + fpunchf(PHAST_NULL("dist_x"), gformat, (double)-99); + } + } + if (current_selected_output->Get_time()) + { + LDBLE reaction_time = kin_time_x; + if (state == REACTION && incremental_reactions == TRUE + && use.Get_kinetics_ptr() != NULL) + { + if (!use.Get_kinetics_ptr()->Get_equalIncrements()) + { + reaction_time = 0.0; + for (i = 0; i < reaction_step; i++) + { + if (i < (int) use.Get_kinetics_ptr()->Get_steps().size()) + { + reaction_time += use.Get_kinetics_ptr()->Get_steps()[i]; + } + else + { + reaction_time += + use.Get_kinetics_ptr()->Get_steps().back(); + } + } + } + else + { + if (reaction_step > use.Get_kinetics_ptr()->Get_count()) + { + reaction_time = use.Get_kinetics_ptr()->Get_steps().front(); + } + else + { + reaction_time = + reaction_step * use.Get_kinetics_ptr()->Get_steps().front() / + ((LDBLE) (use.Get_kinetics_ptr()->Get_count())); + } + } + } + if (state == REACTION) + { + fpunchf(PHAST_NULL("time"), gformat, (double) reaction_time); + } + else if (state == TRANSPORT || state == PHAST) + { + fpunchf(PHAST_NULL("time"), gformat, + (double) (initial_total_time + rate_sim_time)); + } + else if (state == ADVECTION) + { + if (advection_kin_time_defined == TRUE) + { + fpunchf(PHAST_NULL("time"), gformat, + (double) (initial_total_time + rate_sim_time)); + } + else + { + fpunchf(PHAST_NULL("time"), gformat, (double)advection_step); + } + } + else + { + fpunchf(PHAST_NULL("time"), gformat, (double)-99); + } + } + +/* + * reaction or transport step + */ + if (current_selected_output->Get_step()) + { + if (state == REACTION) + { + fpunchf(PHAST_NULL("step"), dformat, reaction_step); + } + else if (state == ADVECTION) + { + fpunchf(PHAST_NULL("step"), dformat, advection_step); + } + else if (state == TRANSPORT) + { + fpunchf(PHAST_NULL("step"), dformat, transport_step); + } + else + { + fpunchf(PHAST_NULL("step"), dformat, -99); + } + } + if (current_selected_output->Get_ph()) + { + if (!current_selected_output->Get_high_precision()) + { + fpunchf("pH", "%12g\t", (double) (-s_hplus->la)); + } + else + { + fpunchf("pH", "%20.12e\t", (double) (-s_hplus->la)); + } + } + if (current_selected_output->Get_pe()) + { + if (!current_selected_output->Get_high_precision()) + { + fpunchf("pe", "%12g\t", (double) (-s_eminus->la)); + } + else + { + fpunchf("pe", "%20.12e\t", (double) (-s_eminus->la)); + } + } + if (current_selected_output->Get_rxn()) + { + if (state >= REACTION && use.Get_reaction_in() == TRUE) + { + if (!current_selected_output->Get_high_precision()) + { + fpunchf("reaction", "%12.4e\t", (double) step_x); + } + else + { + fpunchf("reaction", "%20.12e\t", (double) step_x); + } + } + else + { + if (!current_selected_output->Get_high_precision()) + { + fpunchf("reaction", "%12d\t", -99); + } + else + { + fpunchf("reaction", "%20d\t", -99); + } + } + } + if (current_selected_output->Get_temp()) + { + if (!current_selected_output->Get_high_precision()) + { + fpunchf("temp(C)", "%12.3f\t", (double) tc_x); + } + else + { + fpunchf("temp(C)", "%20.12e\t", (double) tc_x); + } + } + if (current_selected_output->Get_alk()) + { + if (!current_selected_output->Get_high_precision()) + { + fpunchf("Alk(eq/kgw)", "%12g\t", + (double) (total_alkalinity / mass_water_aq_x)); + } + else + { + fpunchf("Alk(eq/kgw)", "%20.12e\t", + (double) (total_alkalinity / mass_water_aq_x)); + } + } + if (current_selected_output->Get_mu()) + { + if (!current_selected_output->Get_high_precision()) + { + fpunchf("mu", "%12g\t", (double) mu_x); + } + else + { + fpunchf("mu", "%20.12e\t", (double) mu_x); + } + } + if (current_selected_output->Get_water()) + { + if (!current_selected_output->Get_high_precision()) + { + fpunchf("mass_H2O", "%12g\t", (double) mass_water_aq_x); + } + else + { + fpunchf("mass_H2O", "%20.12e\t", (double) mass_water_aq_x); + } + } + if (current_selected_output->Get_charge_balance()) + { + if (!current_selected_output->Get_high_precision()) + { + fpunchf("charge(eq)", "%12g\t", (double) cb_x); + } + else + { + fpunchf("charge(eq)", "%20.12e\t", (double) cb_x); + } + } + if (current_selected_output->Get_percent_error()) + { + if (!current_selected_output->Get_high_precision()) + { + fpunchf("pct_err", "%12g\t", + (double) (100 * cb_x / total_ions_x)); + } + else + { + fpunchf("pct_err", "%20.12e\t", + (double) (100 * cb_x / total_ions_x)); + } + } + punch_flush(); + return (OK); +} + +#undef PHAST_NULL +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_saturation_indices(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints saturation indices of selected phases + */ + //int i; + LDBLE si, iap; + struct rxn_token *rxn_ptr; + + for (size_t i = 0; i < current_selected_output->Get_si().size(); i++) + { + if (current_selected_output->Get_si()[i].second == NULL || ((struct phase *) current_selected_output->Get_si()[i].second)->in == FALSE) + { + si = -999.999; + } + else + { +/* + * Print saturation index + */ + iap = 0.0; + for (rxn_ptr = ((struct phase *) current_selected_output->Get_si()[i].second)->rxn_x->token + 1; + rxn_ptr->s != NULL; rxn_ptr++) + { + iap += rxn_ptr->s->la * rxn_ptr->coef; + } + si = -((struct phase *) current_selected_output->Get_si()[i].second)->lk + iap; + } + if (!current_selected_output->Get_high_precision()) + { + fpunchf(sformatf("si_%s", current_selected_output->Get_si()[i].first.c_str()), "%12.4f\t", (double) si); + } + else + { + fpunchf(sformatf("si_%s", current_selected_output->Get_si()[i].first.c_str()), "%20.12e\t", (double) si); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_kinetics(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * prints kinetic reaction, + * should be called only on final kinetic step + */ + cxxKinetics *kinetics_ptr; + LDBLE moles, delta_moles; + + kinetics_ptr = NULL; + if (use.Get_kinetics_in() == TRUE) + { + if (state == TRANSPORT || state == PHAST || state == ADVECTION) + { + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_kinetics_user()); + } + else + { + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, -2); + } + } + for (size_t i = 0; i < current_selected_output->Get_kinetics().size(); i++) + { + moles = 0.0; + delta_moles = 0.0; + if (kinetics_ptr != NULL) + { + for (size_t j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + if (strcmp_nocase + (current_selected_output->Get_kinetics()[i].first.c_str(), + kinetics_comp_ptr->Get_rate_name().c_str()) == 0) + { + if (state != TRANSPORT && state != PHAST) + { + moles = kinetics_comp_ptr->Get_m(); + delta_moles = - kinetics_comp_ptr->Get_moles(); + } + else + { + moles = kinetics_comp_ptr->Get_m(); + delta_moles = + kinetics_comp_ptr->Get_m() - + kinetics_comp_ptr->Get_initial_moles(); + } + break; + } + } + } + if (!current_selected_output->Get_high_precision()) + { + fpunchf(sformatf("k_%s", current_selected_output->Get_kinetics()[i].first.c_str()), "%12.4e\t", + (double) moles); + fpunchf(sformatf("dk_%s", current_selected_output->Get_kinetics()[i].first.c_str()), "%12.4e\t", + (double) delta_moles); + } + else + { + fpunchf(sformatf("k_%s", current_selected_output->Get_kinetics()[i].first.c_str()), "%20.12e\t", + (double) moles); + fpunchf(sformatf("dk_%s", current_selected_output->Get_kinetics()[i].first.c_str()), "%20.12e\t", + (double) delta_moles); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_user_punch(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Punch with user defined BASIC print routine + */ + char l_command[] = "run"; + + n_user_punch_index = 0; + //if (punch.user_punch == FALSE) + // return (OK); + if (current_user_punch == NULL || !current_selected_output->Get_user_punch()) + return OK; + + struct rate * user_punch = current_user_punch->Get_rate(); + + if (user_punch->commands == NULL) + return (OK); + if (user_punch->new_def == TRUE) + { + if (basic_compile + (user_punch->commands, &user_punch->linebase, + &user_punch->varbase, &user_punch->loopbase) != 0) + { + error_msg("Fatal Basic error in USER_PUNCH.", STOP); + } + user_punch->new_def = FALSE; + } + if (basic_run + (l_command, user_punch->linebase, user_punch->varbase, + user_punch->loopbase) != 0) + { + error_msg("Fatal Basic error in USER_PUNCH.", STOP); + } + return (OK); +} + +#if defined PHREEQ98 +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_user_graph(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Graph with user defined BASIC print routine + */ + char command[] = "run"; + + colnr = 0; +/* if (pr.user_graph == FALSE || pr.all == FALSE) return(OK); */ +/* if (punch.user_punch == FALSE) return(OK); */ +/* if (punch.in == FALSE) return(OK); */ + if (user_graph->commands == NULL) + return (OK); + if (((state == INITIAL_SOLUTION) || (state == INITIAL_EXCHANGE) + || (state == INITIAL_SURFACE) || (state == INITIAL_GAS_PHASE)) + && (graph_initial_solutions == FALSE)) + return (OK); + if (FirstCallToUSER_GRAPH) + AddSeries = TRUE; + if (state == REACTION) + { + /*if (reaction_step == 1) AddSeries = TRUE; + else AddSeries = FALSE; */ + if (reaction_step == 1 && !connect_simulations) + AddSeries = TRUE; + if (reaction_step > 1) + AddSeries = FALSE; + } + if (state == ADVECTION) + { + if (advection_step == 0 && graph_initial_solutions == FALSE) + return (OK); + if (((chart_type == 1) && (advection_step == punch_ad_modulus)) || + ((chart_type == 0) && (advection_step != prev_advection_step))) + AddSeries = TRUE; + else + AddSeries = FALSE; + } + if (state == TRANSPORT) + { + if (transport_step == 0 && graph_initial_solutions == FALSE) + return (OK); + if (((chart_type == 1) && (transport_step == punch_modulus)) || + ((chart_type == 0) && (transport_step != prev_transport_step))) + AddSeries = TRUE; + else + AddSeries = FALSE; + } + if (user_graph->new_def == TRUE) + { + if (basic_compile + (user_graph->commands, &user_graph->linebase, + &user_graph->varbase, &user_graph->loopbase) != 0) + { + error_msg("Fatal Basic error in USER_GRAPH.", STOP); + } + user_graph->new_def = FALSE; + } + if (basic_run + (command, user_graph->linebase, user_graph->varbase, + user_graph->loopbase) != 0) + { + error_msg("Fatal Basic error in USER_GRAPH.", STOP); + } + if (state == ADVECTION) + prev_advection_step = advection_step; + if (state == TRANSPORT) + prev_transport_step = transport_step; + /*if (state == REACTION) prev_reaction_step = reaction_step; */ + + if (FirstCallToUSER_GRAPH) + { + start_chart(0); + } + + FirstCallToUSER_GRAPH = FALSE; + + return (OK); +} +#endif +#if defined(MULTICHART) +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +punch_user_graph(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Graph with user defined BASIC print routine + */ + char command[] = "run"; + ChartObject *chart = chart_handler.Get_current_chart(); + if (chart == NULL) return OK; + + chart->Set_AddSeries(false); + + if (chart->Get_rate_command_list().size() == 0) + return (OK); + + // Skip initial calculations if initial_solutions == false + if (((state == INITIAL_SOLUTION) || (state == INITIAL_EXCHANGE) + || (state == INITIAL_SURFACE) || (state == INITIAL_GAS_PHASE)) + && (chart->Get_graph_initial_solutions() == false)) + return (OK); + + if (state == REACTION) + { + /*if (reaction_step == 1) AddSeries = TRUE; + else AddSeries = FALSE; */ + if (reaction_step == 1) + chart->Set_AddSeries(true); + if (reaction_step > 1) + chart->Set_AddSeries(false); + } + + if (state == ADVECTION) + { + + if (advection_step == 0 && chart->Get_graph_initial_solutions() == false) + return (OK); + if ( + ((chart->Get_chart_type() == 1) && (advection_step == punch_ad_modulus)) || + ((chart->Get_chart_type() == 0) && (advection_step != chart->Get_prev_advection_step())) + ) + { + chart->Set_AddSeries(true); + } + else + chart->Set_AddSeries(false); + } + if (state == TRANSPORT) + { + if (transport_step == 0 && chart->Get_graph_initial_solutions() == FALSE) + return (OK); + if ( + ((chart->Get_chart_type() == 1) && (transport_step == punch_modulus)) || + ((chart->Get_chart_type() == 0) && (transport_step != chart->Get_prev_transport_step())) + ) + { + chart->Set_AddSeries(true); + } + else + { + chart->Set_AddSeries(false); + } + } + + // From cmdplot_xy merged into transport and advection above + + // From plotXY + if (chart->Get_FirstCallToUSER_GRAPH()) + chart->Set_prev_sim_no(simulation); + else + { + if (simulation != chart->Get_prev_sim_no()) + { + chart->Set_AddSeries(true); + } + } + chart->Set_prev_sim_no(simulation); + if (chart->Get_AddSeries() && !chart->Get_connect_simulations()) + { + chart->Add_new_series(); + } + + chart->Set_colnr(chart->Get_ColumnOffset()); + chart->Initialize_graph_pts(); + if (chart->Get_rate_new_def()) + { + if (basic_compile + (chart->Get_user_graph()->commands, &chart->Get_user_graph()->linebase, + &chart->Get_user_graph()->varbase, &chart->Get_user_graph()->loopbase) != 0) + { + error_msg("Fatal Basic error in USER_GRAPH.", STOP); + } + chart->Set_rate_new_def(false); + } + + // basic_run calculates points for all graph and plotxy curves + // colnr identifies the curve and is incremented as each Y/Y2 curve point is added + if (basic_run + (command, chart->Get_user_graph()->linebase, + chart->Get_user_graph()->varbase, chart->Get_user_graph()->loopbase) != 0) + { + error_msg("Fatal Basic error in USER_GRAPH.", STOP); + } + chart->Finalize_graph_pts(); + + if (state == ADVECTION) + chart->Set_prev_advection_step(advection_step); + if (state == TRANSPORT) + chart->Set_prev_transport_step(transport_step); + + if (chart->Get_FirstCallToUSER_GRAPH()) + { + chart->start_chart(); + } + chart->Set_new_ug(false); + + chart->Set_FirstCallToUSER_GRAPH(false); + + return (OK); +} +#endif // MULTICHART + +char * Phreeqc:: +sformatf(const char *format, ...) +{ + bool success = false; + do + { + va_list args; + va_start(args, format); + int j = vsnprintf(sformatf_buffer, sformatf_buffer_size, format, args); + success = (j > 0 && j < (int) sformatf_buffer_size); + va_end(args); + if (!success) + { + sformatf_buffer_size *= 2; + sformatf_buffer = (char *) PHRQ_realloc(sformatf_buffer, sformatf_buffer_size * sizeof(char)); + if (sformatf_buffer == NULL) malloc_error(); + } + } + while (!success); + + return sformatf_buffer; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_alkalinity(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints description of solution, uses array species_list for + * order of aqueous species. + */ + int i, j; + struct species_list *alk_list; + int count_alk_list; + LDBLE min; + + if (pr.alkalinity == FALSE || pr.all == FALSE) + return (OK); + print_centered("Distribution of alkalinity"); + alk_list = + (struct species_list *) + PHRQ_malloc((size_t) (count_s_x * sizeof(struct species_list))); + if (alk_list == NULL) + { + malloc_error(); + return (OK); + } + j = 0; + for (i = 0; i < count_s_x; i++) + { + if (s_x[i]->alk == 0.0) + continue; + alk_list[j].master_s = s_hplus; + alk_list[j].s = s_x[i]; + alk_list[j].coef = s_x[i]->alk; + j++; + } + count_alk_list = j; + min = fabs(censor * total_alkalinity / mass_water_aq_x); + if (count_alk_list > 0) + { + output_msg(sformatf("\t%26s%11.3e\n\n", + "Total alkalinity (eq/kgw) = ", + (double) (total_alkalinity / mass_water_aq_x))); + output_msg(sformatf("\t%-15s%12s%12s%10s\n\n", "Species", + "Alkalinity", "Molality", "Alk/Mol")); + qsort(&alk_list[0], (size_t) count_alk_list, + (size_t) sizeof(struct species_list), species_list_compare_alk); + for (i = 0; i < count_alk_list; i++) + { + if (fabs + (alk_list[i].s->alk * (alk_list[i].s->moles) / + mass_water_aq_x) < min) + continue; + output_msg(sformatf("\t%-15s%12.3e%12.3e%10.2f\n", + alk_list[i].s->name, + (double) (alk_list[i].s->alk * + (alk_list[i].s->moles) / mass_water_aq_x), + (double) ((alk_list[i].s->moles) / mass_water_aq_x), + (double) (alk_list[i].s->alk))); + } + } + + output_msg(sformatf("\n")); + alk_list = (struct species_list *) free_check_null(alk_list); + return (OK); +} + diff --git a/phreeqcpp/read.cpp b/phreeqcpp/read.cpp new file mode 100644 index 00000000..405cb7d8 --- /dev/null +++ b/phreeqcpp/read.cpp @@ -0,0 +1,11519 @@ +#include +#include +#include + +#include "Utils.h" +#include "Phreeqc.h" +#include "phqalloc.h" +#include "Pressure.h" +#include "Temperature.h" +#include "Parser.h" +#include "cxxMix.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "Reaction.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "cxxKinetics.h" +#include "Solution.h" +#include "SelectedOutput.h" +#include "UserPunch.h" + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_input(void) +/* ---------------------------------------------------------------------- */ +{ + int i, j, l; + char *ptr; + char token[2 * MAX_LENGTH]; +#define LAST_C_KEYWORD 61 + + parse_error = 0; + input_error = 0; + next_keyword = Keywords::KEY_NONE; + count_warnings = 0; + + Rxn_new_exchange.clear(); + Rxn_new_gas_phase.clear(); + Rxn_new_kinetics.clear(); // not used + Rxn_new_mix.clear(); // not used + Rxn_new_pp_assemblage.clear(); + Rxn_new_pressure.clear(); // not used + Rxn_new_reaction.clear(); // not used + Rxn_new_solution.clear(); + Rxn_new_ss_assemblage.clear(); + Rxn_new_surface.clear(); + Rxn_new_temperature.clear(); // not used + phrq_io->Set_echo_on(true); // **appt +/* + * Initialize keyword counters + */ + for (i = 0; i < Keywords::KEY_COUNT_KEYWORDS; i++) + { + keycount[i] = 0; + } +/* + * Initialize use and save pointers + */ + use.init(); + + save.solution = FALSE; + save.mix = FALSE; + save.reaction = FALSE; + save.kinetics = FALSE; + save.pp_assemblage = FALSE; + save.exchange = FALSE; + save.surface = FALSE; + save.gas_phase = FALSE; + save.ss_assemblage = FALSE; + title_x = (char *) free_check_null(title_x); + + while ((i = check_line("Subroutine Read", FALSE, TRUE, TRUE, TRUE)) != KEYWORD) + { + /* empty, eof, keyword, print */ + + if (i == EOF) + return (EOF); + error_string = sformatf( + "Unknown input, no keyword has been specified."); + warning_msg(error_string); + } + + for (;;) + { + if (next_keyword > 0) + { + if (next_keyword != Keywords::KEY_DATABASE && !reading_database()) + { + first_read_input = FALSE; + } + } + // mark keyword read + if (next_keyword > 0 && next_keyword < Keywords::KEY_COUNT_KEYWORDS) + { + keycount[next_keyword]++; + } + switch (next_keyword) + { + case Keywords::KEY_NONE: /* Have not read line with keyword */ + error_string = sformatf( "Unknown input, no keyword has been specified."); + warning_msg(error_string); + while ((j = check_line("No keyword", FALSE, TRUE, TRUE, TRUE)) != KEYWORD && j != EOF) + { + warning_msg(error_string); + } + break; + case Keywords::KEY_END: + goto END_OF_SIMULATION_INPUT; + case Keywords::KEY_SOLUTION_SPECIES: /* Read aqueous model */ + read_species(); + break; + case Keywords::KEY_SOLUTION_MASTER_SPECIES: /* Read master species */ + read_master_species(); + break; + case Keywords::KEY_SOLUTION: /* Read solution data */ + read_solution(); + break; + case Keywords::KEY_PHASES: + read_phases(); + break; + case Keywords::KEY_EQUILIBRIUM_PHASES: + read_pp_assemblage(); + break; + case Keywords::KEY_REACTION: + read_reaction(); + break; + case Keywords::KEY_MIX: + read_mix(); + break; + case Keywords::KEY_SOLUTION_MIX: + //read_solution_mix(); + read_entity_mix(Rxn_solution_mix_map); + break; + case Keywords::KEY_EXCHANGE_MIX: + read_entity_mix(Rxn_exchange_mix_map); + break; + case Keywords::KEY_GAS_PHASE_MIX: + read_entity_mix(Rxn_gas_phase_mix_map); + break; + case Keywords::KEY_KINETICS_MIX: + read_entity_mix(Rxn_kinetics_mix_map); + break; + case Keywords::KEY_PPASSEMBLAGE_MIX: + read_entity_mix(Rxn_pp_assemblage_mix_map); + break; + case Keywords::KEY_SSASSEMBLAGE_MIX: + read_entity_mix(Rxn_ss_assemblage_mix_map); + break; + case Keywords::KEY_SURFACE_MIX: + read_entity_mix(Rxn_surface_mix_map); + break; + case Keywords::KEY_USE: + read_use(); + break; + case Keywords::KEY_SAVE: + read_save(); + break; + case Keywords::KEY_EXCHANGE_SPECIES: + read_exchange_species(); + break; + case Keywords::KEY_EXCHANGE_MASTER_SPECIES: + read_exchange_master_species(); + break; + case Keywords::KEY_EXCHANGE: + read_exchange(); + break; + case Keywords::KEY_SURFACE_SPECIES: + read_surface_species(); + break; + case Keywords::KEY_SURFACE_MASTER_SPECIES: + read_surface_master_species(); + break; + case Keywords::KEY_SURFACE: + read_surface(); + break; + case Keywords::KEY_REACTION_TEMPERATURE: + read_temperature(); + break; + case Keywords::KEY_INVERSE_MODELING: + read_inverse(); + break; + case Keywords::KEY_GAS_PHASE: + read_gas_phase(); + break; + case Keywords::KEY_TRANSPORT: + read_transport(); + break; + case Keywords::KEY_KNOBS: + read_debug(); + break; + case Keywords::KEY_SELECTED_OUTPUT: + read_selected_output(); + break; + case Keywords::KEY_PRINT: + read_print(); + break; + case Keywords::KEY_TITLE: + read_title(); + break; + case Keywords::KEY_ADVECTION: + read_advection(); + break; + case Keywords::KEY_KINETICS: + read_kinetics(); + break; + case Keywords::KEY_INCREMENTAL_REACTIONS: + read_incremental_reactions(); + break; + case Keywords::KEY_RATES: + read_rates(); + break; + case Keywords::KEY_SOLUTION_SPREAD: + read_solution_spread(); + break; + case Keywords::KEY_USER_PRINT: + read_user_print(); + break; + case Keywords::KEY_USER_PUNCH: + read_user_punch(); + break; + case Keywords::KEY_SOLID_SOLUTIONS: + read_solid_solutions(); + break; + case Keywords::KEY_USER_GRAPH: +#if defined PHREEQ98 + read_user_graph(); +#elif defined MULTICHART + read_user_graph_handler(); +# else + for (;;) + { + j = check_line("Reading user_graph", FALSE, TRUE, TRUE, TRUE); + if (j == EOF || j == KEYWORD) + { + break; + } + } +#endif + break; + case Keywords::KEY_LLNL_AQUEOUS_MODEL_PARAMETERS: + read_llnl_aqueous_model_parameters(); + break; + case Keywords::KEY_DATABASE: + if (reading_database()) + { + /* warning_msg("DATABASE is ignored in the database file."); */ + } + else if (first_read_input == FALSE) + { + error_msg("DATABASE must be the first keyword in the input file.", CONTINUE); + input_error++; + } + else + { + ptr = line; + copy_token(token, &ptr, &l); +#if defined(SWIG_SHARED_OBJ) + warning_msg("DATABASE keyword is ignored by IPhreeqc."); +#else + + user_database = (char *) free_check_null(user_database); + user_database = string_duplicate(ptr); + if (string_trim(user_database) == EMPTY) + { + error_msg("DATABASE file name is missing.", CONTINUE); + input_error++; + user_database = (char *) free_check_null(user_database); + } + first_read_input = FALSE; +#endif + } + j = check_line("Reading after DATABASE", FALSE, TRUE, TRUE, TRUE); + break; + case Keywords::KEY_NAMED_EXPRESSIONS: + read_named_logk(); + break; + case Keywords::KEY_ISOTOPES: + read_isotopes(); + break; + case Keywords::KEY_CALCULATE_VALUES: + read_calculate_values(); + break; + case Keywords::KEY_ISOTOPE_RATIOS: + read_isotope_ratios(); + break; + case Keywords::KEY_ISOTOPE_ALPHAS: + read_isotope_alphas(); + break; + case Keywords::KEY_COPY: + read_copy(); + break; + case Keywords::KEY_PITZER: + read_pitzer(); + break; + case Keywords::KEY_SIT: + read_sit(); + break; + case Keywords::KEY_SOLUTION_RAW: + Utilities::Rxn_read_raw(Rxn_solution_map, Rxn_new_solution, this); + break; + case Keywords::KEY_EXCHANGE_RAW: + Utilities::Rxn_read_raw(Rxn_exchange_map, Rxn_new_exchange, this); + break; + case Keywords::KEY_SURFACE_RAW: + Utilities::Rxn_read_raw(Rxn_surface_map, Rxn_new_surface, this); + break; + case Keywords::KEY_EQUILIBRIUM_PHASES_RAW: + Utilities::Rxn_read_raw(Rxn_pp_assemblage_map, Rxn_new_pp_assemblage, this); + break; + case Keywords::KEY_KINETICS_RAW: + Utilities::Rxn_read_raw(Rxn_kinetics_map, Rxn_new_kinetics, this); + break; + case Keywords::KEY_SOLID_SOLUTIONS_RAW: + Utilities::Rxn_read_raw(Rxn_ss_assemblage_map, Rxn_new_ss_assemblage, this); + break; + case Keywords::KEY_GAS_PHASE_RAW: + Utilities::Rxn_read_raw(Rxn_gas_phase_map, Rxn_new_gas_phase, this); + break; + case Keywords::KEY_REACTION_RAW: + Utilities::Rxn_read_raw(Rxn_reaction_map, Rxn_new_reaction, this); + break; + case Keywords::KEY_MIX_RAW: + Utilities::Rxn_read_raw(Rxn_mix_map, Rxn_new_mix, this); + break; + case Keywords::KEY_REACTION_TEMPERATURE_RAW: + Utilities::Rxn_read_raw(Rxn_temperature_map, Rxn_new_temperature, this); + break; + case Keywords::KEY_DUMP: + read_dump(); + break; + case Keywords::KEY_SOLUTION_MODIFY: + Utilities::Rxn_read_modify(Rxn_solution_map, Rxn_new_solution, this); + break; + case Keywords::KEY_EQUILIBRIUM_PHASES_MODIFY: + Utilities::Rxn_read_modify(Rxn_pp_assemblage_map, Rxn_new_pp_assemblage, this); + break; + case Keywords::KEY_EXCHANGE_MODIFY: + Utilities::Rxn_read_modify(Rxn_exchange_map, Rxn_new_exchange, this); + break; + case Keywords::KEY_SURFACE_MODIFY: + Utilities::Rxn_read_modify(Rxn_surface_map, Rxn_new_surface, this); + break; + case Keywords::KEY_SOLID_SOLUTIONS_MODIFY: + Utilities::Rxn_read_modify(Rxn_ss_assemblage_map, Rxn_new_ss_assemblage, this); + break; + case Keywords::KEY_GAS_PHASE_MODIFY: + Utilities::Rxn_read_modify(Rxn_gas_phase_map, Rxn_new_gas_phase, this); + break; + case Keywords::KEY_KINETICS_MODIFY: + Utilities::Rxn_read_modify(Rxn_kinetics_map, Rxn_new_kinetics, this); + break; + case Keywords::KEY_DELETE: + read_delete(); + break; + case Keywords::KEY_RUN_CELLS: + read_run_cells(); + break; + case Keywords::KEY_REACTION_MODIFY: + Utilities::Rxn_read_modify(Rxn_reaction_map, Rxn_new_reaction, this); + break; + case Keywords::KEY_REACTION_TEMPERATURE_MODIFY: + Utilities::Rxn_read_modify(Rxn_temperature_map, Rxn_new_temperature, this); + break; + //case LAST_C_KEYWORD + 22: //reaction_temperature_modify + // keyword[LAST_C_KEYWORD + 22].keycount++; + // read_reaction_temperature_modify(); + // break; + case Keywords::KEY_REACTION_PRESSURE: + read_reaction_pressure(); + break; + case Keywords::KEY_REACTION_PRESSURE_RAW: + Utilities::Rxn_read_raw(Rxn_pressure_map, Rxn_new_pressure, this); + break; + case Keywords::KEY_REACTION_PRESSURE_MODIFY: + Utilities::Rxn_read_modify(Rxn_pressure_map, Rxn_new_pressure, this); + break; + default: + error_msg("Error in keyword switch", STOP); + break; + } + } + END_OF_SIMULATION_INPUT: + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_exchange_species(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read data for exchange species, parse equations + */ + int i; + int association; + char token[MAX_LENGTH]; + char *ptr; + struct phase *phase_ptr; + + struct species *s_ptr; + struct elt_list *next_elt; + struct rxn_token *token_ptr; + //LDBLE exchange_coef; + LDBLE offset; + + int return_value, opt, opt_save; + char *next_char; + const char *opt_list[] = { + "no_check", /* 0 */ + "check", /* 1 */ + "mb", /* 2 */ + "mass_balance", /* 3 */ + "log_k", /* 4 */ + "logk", /* 5 */ + "delta_h", /* 6 */ + "deltah", /* 7 */ + "analytical_expression", /* 8 */ + "a_e", /* 9 */ + "ae", /* 10 */ + "mole_balance", /* 11 */ + "gamma", /* 12 */ + "davies", /* 13 */ + "offset", /* 14 */ + "llnl_gamma", /* 15 */ + "add_logk", /* 16 */ + "add_log_k", /* 17 */ + "add_constant", /* 18 */ + "vm" /* 19, molar volume, must replace delta_v */ + }; + int count_opt_list = 20; + + association = TRUE; + s_ptr = NULL; +/* + * Read eqn from file and call parser + */ + opt_save = OPTION_DEFAULT; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in EXCHANGE_SPECIES keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* no_check */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->check_equation = FALSE; + break; + case 1: /* check */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->check_equation = TRUE; + break; + case 2: /* mb */ + case 3: /* mass_balance */ + case 11: /* mole_balance */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + count_elts = 0; + paren_count = 0; + copy_token(token, &next_char, &i); + s_ptr->mole_balance = string_hsave(token); + ptr = token; + get_secondary_in_species(&ptr, 1.0); + s_ptr->next_secondary = + (struct elt_list *) free_check_null(s_ptr->next_secondary); + s_ptr->next_secondary = elt_list_save(); +/* debug + for (i = 0; i < count_elts; i++) { + output_msg(sformatf("%s\t%f\n", elt_list[i].elt->name, + elt_list[i].coef)); + } + */ + opt_save = OPTION_DEFAULT; + break; + case 4: /* log_k */ + case 5: /* logk */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_log_k_only(next_char, &s_ptr->logk[0]); + opt_save = OPTION_DEFAULT; + break; + case 6: /* delta_h */ + case 7: /* deltah */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_delta_h_only(next_char, &s_ptr->logk[1], + &s_ptr->original_units); + opt_save = OPTION_DEFAULT; + break; + case 8: /* analytical_expression */ + case 9: /* a_e */ + case 10: /* ae */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_analytical_expression_only(next_char, &(s_ptr->logk[T_A1])); + opt_save = OPTION_DEFAULT; + break; + case 12: /* gamma */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->exch_gflag = 2; + s_ptr->a_f = 0; + i = sscanf(next_char, SCANFORMAT SCANFORMAT SCANFORMAT, &s_ptr->dha, + &s_ptr->dhb, &s_ptr->a_f); + if (i < 2) + { + error_string = sformatf( "Expecting 2 activity-" + "coefficient parameters, a and b."); + warning_msg(error_string); + } + if (s_ptr->dha == 0 && s_ptr->dhb == 0) + { + s_ptr->dhb = 99.9; + s_ptr->exch_gflag = 1; + } + opt_save = OPTION_DEFAULT; + break; + case 13: /* davies */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->exch_gflag = 1; + s_ptr->dha = 0; + s_ptr->dhb = 99.9; + opt_save = OPTION_DEFAULT; + break; + case 14: /* offset */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + if (sscanf(next_char, SCANFORMAT, &offset) != 1) + { + error_msg("No offset for log K given", STOP); + } + s_ptr->logk[0] += offset; + opt_save = OPTION_DEFAULT; + break; + case 15: /* llnl_gamma */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->exch_gflag = 7; /* llnl D-H */ + i = sscanf(next_char, SCANFORMAT, &s_ptr->dha); + if (i < 1) + { + error_string = sformatf( + "Expecting activity-coefficient parameter, a."); + warning_msg(error_string); + } + opt_save = OPTION_DEFAULT; + break; + case 16: /* add_logk */ + case 17: /* add_log_k */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + if (s_ptr->count_add_logk == 0) + { + s_ptr->add_logk = + (struct name_coef *) + PHRQ_malloc(sizeof(struct name_coef)); + if (s_ptr->add_logk == NULL) + { + malloc_error(); + return (OK); + } + } + else + { + s_ptr->add_logk = + (struct name_coef *) PHRQ_realloc(s_ptr->add_logk, + (size_t) ((s_ptr-> + count_add_logk + + + 1) * + sizeof + (struct + name_coef))); + if (s_ptr->add_logk == NULL) + { + malloc_error(); + return (OK); + } + } + /* read name */ + if (copy_token(token, &next_char, &i) == EMPTY) + { + input_error++; + error_string = sformatf( + "Expected the name of a NAMED_EXPRESSION."); + error_msg(error_string, CONTINUE); + break; + } + s_ptr->add_logk[s_ptr->count_add_logk].name = string_hsave(token); + /* read coef */ + i = sscanf(next_char, SCANFORMAT, + &s_ptr->add_logk[s_ptr->count_add_logk].coef); + if (i <= 0) + { + s_ptr->add_logk[s_ptr->count_add_logk].coef = 1; + } + s_ptr->count_add_logk++; + opt_save = OPTION_DEFAULT; + break; + case 18: /* add_constant */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + if (s_ptr->count_add_logk == 0) + { + s_ptr->add_logk = + (struct name_coef *) + PHRQ_malloc(sizeof(struct name_coef)); + if (s_ptr->add_logk == NULL) + { + malloc_error(); + return (OK); + } + } + else + { + s_ptr->add_logk = + (struct name_coef *) PHRQ_realloc(s_ptr->add_logk, + (size_t) ((s_ptr-> + count_add_logk + + + 1) * + sizeof + (struct + name_coef))); + if (s_ptr->add_logk == NULL) + { + malloc_error(); + return (OK); + } + } + i = sscanf(next_char, SCANFORMAT, + &s_ptr->add_logk[s_ptr->count_add_logk].coef); + if (i <= 0) + { + input_error++; + error_string = sformatf( + "Expected the constant to add for log_K definition."); + error_msg(error_string, CONTINUE); + break; + } + /* set name */ + s_ptr->add_logk[s_ptr->count_add_logk].name = + string_hsave("XconstantX"); + /* read coef */ + s_ptr->count_add_logk++; + opt_save = OPTION_DEFAULT; + break; + case 19: /* vm, molar volume */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_vm_only(next_char, &s_ptr->logk[vm0], + &s_ptr->original_deltav_units); + opt_save = OPTION_DEFAULT; + break; + + case OPTION_DEFAULT: +/* + * Get exchange species information and parse equation + */ + s_ptr = NULL; + if (parse_eq(line, &next_elt, association) == ERROR) + { + parse_error++; + error_msg("Parsing equation.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + } +/* + * Get pointer to each species in the reaction, store new species if necessary + */ + trxn.token[0].s = + s_store(trxn.token[0].name, trxn.token[0].z, TRUE); + for (i = 1; i < count_trxn; i++) + { + trxn.token[i].s = + s_store(trxn.token[i].name, trxn.token[i].z, FALSE); + } +/* + * Save element list and carbon, hydrogen, and oxygen in species + */ + trxn.token[0].s->next_elt = next_elt; + for (; next_elt->elt != NULL; next_elt++) + { + if (strcmp(next_elt->elt->name, "C") == 0) + { + trxn.token[0].s->carbon = next_elt->coef; + } + if (strcmp(next_elt->elt->name, "H") == 0) + { + trxn.token[0].s->h = next_elt->coef; + } + if (strcmp(next_elt->elt->name, "O") == 0) + { + trxn.token[0].s->o = next_elt->coef; + } + } +#ifdef SKIP + // Need to do this in tidy species in case X- is not first species read. +/* + * Find valence of cation from coefficients of reaction components + * Changed to be coefficient of exchanger + */ + exchange_coef = 0.0; + for (i = 1; i < count_trxn; i++) + { + if (trxn.token[i].s->type == EX) + { + exchange_coef = trxn.token[i].coef; + } + } + trxn.token[0].s->equiv = exchange_coef; +#endif +/* + * Malloc space for species reaction + */ + trxn.token[0].s->rxn = rxn_alloc(count_trxn + 1); +/* + * Copy reaction to reaction for species + */ + token_ptr = trxn.token[0].s->rxn->token; + for (i = 0; i < count_trxn; i++) + { + token_ptr[i].s = trxn.token[i].s; + token_ptr[i].coef = trxn.token[i].coef; + } + token_ptr[i].s = NULL; +/* + * Set type for species + */ + trxn.token[0].s->type = EX; + s_ptr = trxn.token[0].s; +/* + * Set gamma data + */ + s_ptr->gflag = 4; + s_ptr->exch_gflag = 3; + s_ptr->dha = 0.0; + s_ptr->dhb = 0.0; + opt_save = OPTION_DEFAULT; +/* + * Save as a phase for inverse modeling only + */ + phase_ptr = phase_store(s_ptr->name); + if (phase_ptr == NULL) + { + input_error++; + error_string = sformatf( "Copying exchange to phases."); + error_msg(error_string, CONTINUE); + } + else + { + phase_ptr->formula = s_ptr->name; + phase_ptr->check_equation = FALSE; + phase_ptr->type = EX; + phase_ptr->next_elt = elt_list_dup(s_ptr->next_elt); + phase_ptr->rxn = rxn_dup(s_ptr->rxn); + } + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_exchange(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads exchange data + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int n_user, n_user_end; + LDBLE conc; + char *ptr; + char *description; + + int return_value, opt; + char *next_char; + const char *opt_list[] = { + "equilibrate", /* 0 */ + "equil", /* 1 */ + "pitzer_exchange_gammas", /* 2 */ + "exchange_gammas", /* 3 */ + "gammas", /* 4 */ + "equilibrium" /* 5 */ + }; + int count_opt_list = 6; +/* + * kin_exch is for exchangers, related to kinetically reacting minerals + * they are defined if "sites" is followed by mineral name: + * Z Manganite ('equi' or 'kine') 0.25 + * ^Name ^equi or kinetic mineral ^switch ^prop.factor + */ +/* + * Read exchange number and description + */ + + ptr = line; + read_number_description(ptr, &n_user, &n_user_end, &description); +/* + * Default values + n_user, description + */ + cxxExchange temp_exchange; + cxxExchComp *comp_ptr = NULL; + temp_exchange.Set_new_def(true); + temp_exchange.Set_n_user(n_user); + temp_exchange.Set_n_user_end(n_user_end); + temp_exchange.Set_description(description); + free_check_null(description); +/* + * Set use data + */ + if (use.Get_exchange_in() == FALSE) + { + use.Set_exchange_in(true); + use.Set_n_exchange_user(n_user); + } +/* + * Read exchange data + */ + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in EXCHANGE keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* equilibrate */ + case 1: /* equil */ + case 5: /* equilibrium */ + /* + * Read solution to equilibrate with + */ + for (;;) + { + std::string token; + int i = copy_token(token, &next_char); + if (i == DIGIT) + { + int n_solution; + sscanf(token.c_str(), "%d", &n_solution); + temp_exchange.Set_n_solution(n_solution); + temp_exchange.Set_new_def(true); + temp_exchange.Set_solution_equilibria(true); + break; + } + if (i == EMPTY) + { + error_msg + ("Expected a solution number with which to equilibrate exchanger.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + } + break; + case 2: /* pitzer_exchange_gammas */ + case 3: /* exchange_gammas */ + case 4: /* gammas */ + temp_exchange.Set_pitzer_exchange_gammas( + get_true_false(next_char, TRUE) == TRUE); + break; + case OPTION_DEFAULT: + { + std::string token; + ptr = line; + int i = copy_token(token, &ptr); + /* + * Species formula is stored in token + */ + if (i != UPPER && token[0] != '[') + { /* maybe a bit more clear? */ + error_string = sformatf( + "Expected exchanger name to begin with a capital letter, but found:\n %s", + line_save); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + cxxExchComp temp_comp; + temp_exchange.Get_exchange_comps().push_back(temp_comp); + comp_ptr = &(temp_exchange.Get_exchange_comps().back()); + comp_ptr->Set_formula(token.c_str()); + prev_next_char = ptr; + std::string token1; + i = copy_token(token1, &ptr); + if (i == DIGIT) + { + /* + * Read exchange concentration + */ + + /* exchanger conc. is read directly .. */ + if (sscanf(token1.c_str(), SCANFORMAT, &conc) < 1) + { + error_string = sformatf( + "Expected concentration of exchanger, but found:\n %s", + line_save); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + prev_next_char = ptr; + int j = copy_token(token1, &ptr); + if (j == UPPER || j == LOWER) + { + comp_ptr->Set_rate_name(token1.c_str()); + if (copy_token(token1, &ptr) != DIGIT) + { + error_string = sformatf( + "Expected a coefficient to relate exchange to kinetic reaction, but found:\n %s", + prev_next_char); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + LDBLE p; + sscanf(token1.c_str(), SCANFORMAT, &p); + comp_ptr->Set_phase_proportion(p); + } + /* + * Read equilibrium phase name or kinetics rate name + */ + } + else if (i != EMPTY) + { + + /* exchanger conc. is related to mineral or kinetics */ + comp_ptr->Set_phase_name(token1.c_str()); + prev_next_char = ptr; + int j = copy_token(token1, &ptr); + if (j != DIGIT) + { + if (token1[0] == 'K' || token1[0] == 'k') + { + comp_ptr->Set_rate_name(comp_ptr->Get_phase_name().c_str()); + comp_ptr->Set_phase_name(""); + } + else if (token1[0] != 'E' && token1[0] != 'e') + { + error_string = sformatf( + "Character string expected to be 'equilibrium_phase' or 'kinetics'\n to relate exchange to mineral or kinetic reaction, but found:\n %s", + prev_next_char); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + prev_next_char = ptr; + j = copy_token(token1, &ptr); + } + + + if (j != DIGIT) + { + error_string = sformatf( + "Expected a coefficient to relate exchanger to mineral or kinetic reaction, but found:\n %s", + prev_next_char); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + LDBLE p; + sscanf(token1.c_str(), SCANFORMAT, &p); + comp_ptr->Set_phase_proportion(p); + /* real conc must be defined in tidy_model */ + conc = 1.0; + } + else + { + error_msg + ("Expected concentration of exchanger, mineral name, or kinetic reaction name.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + /* + * Accumulate elements in elt_list + */ + count_elts = 0; + paren_count = 0; + char * formula = string_duplicate(token.c_str()); + ptr = formula; + get_elts_in_species(&ptr, conc); + + /* + * save formula for adjusting number of exchange sites + */ + ptr = formula; + char *name = string_duplicate(token.c_str()); + name[0] = '\0'; + LDBLE z; + int l; + get_token(&ptr, name, &z, &l); + comp_ptr->Set_formula_z(z); + free_check_null(formula); + free_check_null(name); + /* + * Save elt_list + */ + comp_ptr->Set_totals(elt_list_NameDouble()); + comp_ptr->Set_charge_balance(0.0); + } + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + Rxn_exchange_map[n_user] = temp_exchange; + Rxn_new_exchange.insert(n_user); + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_exchange_master_species(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads master species data from data file or input file + */ + int j, l; + char *ptr, *ptr1; + LDBLE l_z; + struct element *elts_ptr; + struct species *s_ptr; + char token[MAX_LENGTH], token1[MAX_LENGTH]; + for (;;) + { + j = check_line("Exchange species equation", FALSE, TRUE, TRUE, TRUE); + if (j == EOF || j == KEYWORD) + { + break; + } +/* + * Get element name with valence, allocate space, store + */ + ptr = line; +/* + * Get element name and save pointer to character string + */ + if (copy_token(token, &ptr, &l) != UPPER && token[0] != '[') + { + parse_error++; + error_msg("Reading element for master species.", CONTINUE); + error_msg(line_save, CONTINUE); + continue; + } + /* + if (token[0] == '[') { + ptr1 = token; + get_elt(&ptr, element, &l); + strcpy(token, element); + } + */ + replace("(+", "(", token); +/* + * Delete master if it exists + */ + master_delete(token); +/* + * Increase pointer array, if necessary, and malloc space + */ + if (count_master >= max_master) + { + space((void **) ((void *) &master), count_master + 1, + &max_master, sizeof(struct master *)); + } + master[count_master] = master_alloc(); +/* + * Set type to EX + */ + master[count_master]->type = EX; +/* + * Save element name + */ + master[count_master]->elt = element_store(token); +/* + * Save pointer to species data for master species + */ + if ((copy_token(token, &ptr, &l) != UPPER) && + token[0] != '[' && (strcmp_nocase_arg1(token, "e-") != 0)) + { + parse_error++; + error_msg("Reading master species name.", CONTINUE); + error_msg(line_save, CONTINUE); + continue; + } + s_ptr = s_search(token); + if (s_ptr != NULL) + { + master[count_master]->s = s_ptr; + } + else + { + ptr1 = token; + get_token(&ptr1, token1, &l_z, &l); + master[count_master]->s = s_store(token1, l_z, FALSE); + } +/* + * MAKE LISTS OF PRIMARY AND SECONDARY MASTER SPECIES + */ + master[count_master]->primary = TRUE; + if (strcmp(master[count_master]->elt->name, "E") != 0) + { + elts_ptr = element_store(master[count_master]->elt->name); + elts_ptr->gfw = 0.0; + } + + count_master++; + if (count_master >= max_master) + { + space((void **) ((void *) &master), count_master, &max_master, + sizeof(struct master *)); + } + } + return (j); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_gas_phase(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads gas phase data + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int i, j, l; + int n_user, n_user_end; + char *ptr; + char *description; + char token[MAX_LENGTH]; + cxxGasPhase temp_gas_phase; + int return_value, opt; + char *next_char; + const char *opt_list[] = { + "pressure", /* 0 */ + "volume", /* 1 */ + "temp", /* 2 */ + "temperature", /* 3 */ + "fixed_pressure", /* 4 */ + "fixed_volume", /* 5 */ + "equilibrium", /* 6 */ + "equilibrate", /* 7 */ + "equil" /* 8 */ + }; + int count_opt_list = 9; +/* + * Read gas_phase number + */ + ptr = line; + read_number_description(ptr, &n_user, &n_user_end, &description); + + temp_gas_phase.Set_n_user(n_user); + temp_gas_phase.Set_n_user_end(n_user_end); + temp_gas_phase.Set_description(description); + temp_gas_phase.Set_new_def(true); + free_check_null(description); +/* + * Set use data to first read + */ + if (use.Get_gas_phase_in() == FALSE) + { + use.Set_gas_phase_in(true); + use.Set_n_gas_phase_user(n_user); + } +/* + * Read phases + */ + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in GAS_PHASE keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* pressure */ + sscanf(next_char, SCANFORMAT, &dummy); + temp_gas_phase.Set_total_p(dummy); + break; + case 1: /* Volume */ + sscanf(next_char, SCANFORMAT, &dummy); + temp_gas_phase.Set_volume(dummy); + break; + case 2: /* Temperature */ + case 3: + j = sscanf(next_char, SCANFORMAT, &dummy); + if (j == 1) + { + temp_gas_phase.Set_temperature(dummy + 273.15); + } + break; + case 4: /* fixed_pressure */ + temp_gas_phase.Set_type(cxxGasPhase::GP_PRESSURE); + break; + case 5: /* fixed_volume */ + temp_gas_phase.Set_type(cxxGasPhase::GP_VOLUME); + break; + case 6: /* equilibrate */ + case 7: /* equilibrium */ + case 8: /* equil */ +/* + * Read solution to equilibrate with + */ + for (;;) + { + i = copy_token(token, &next_char, &l); + if (i == DIGIT) + { + sscanf(token, "%d", &l); + temp_gas_phase.Set_n_solution(l); + temp_gas_phase.Set_new_def(true); + temp_gas_phase.Set_solution_equilibria(true); + break; + } + if (i == EMPTY) + { + error_msg + ("Expected a solution number with which to equilibrate gas phase.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + } + break; + case OPTION_DEFAULT: + { + cxxGasComp temp_comp; + /* + * Read name + */ + ptr = line; + copy_token(token, &ptr, &l); + temp_comp.Set_phase_name(token); + if ((j = copy_token(token, &ptr, &l)) == EMPTY) + { + temp_comp.Set_p_read(NAN); + temp_gas_phase.Get_gas_comps().push_back(temp_comp); + break; + } + /* + * Read initial partial pressure of gas + */ + + j = sscanf(token, SCANFORMAT, &dummy); + + if (j != 1) + { + error_msg("Expected partial pressure of gas in gas phase.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + else + { + temp_comp.Set_p_read(dummy); + temp_gas_phase.Get_gas_comps().push_back(temp_comp); + } + } + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + + // sort components + std::map gc; + for (size_t i = 0; i < temp_gas_phase.Get_gas_comps().size(); i++) + { + cxxGasComp *gc_ptr = &(temp_gas_phase.Get_gas_comps()[i]); + gc[gc_ptr->Get_phase_name()] = *gc_ptr; + } + std::vector vgc; + std::map::iterator it = gc.begin(); + for ( ; it != gc.end(); it++) + { + vgc.push_back(it->second); + } + temp_gas_phase.Set_gas_comps(vgc); + + + Rxn_gas_phase_map[n_user] = temp_gas_phase; + Rxn_new_gas_phase.insert(n_user); + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_inverse(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads data for mass_balance calculations + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int n, j; + int n_user, n_user_end; + char *ptr; + char *description; + LDBLE range_max, inv_tol, water_uncertainty; + + int return_value, opt, opt_save; + char *next_char; + const char *opt_list[] = { + "solutions", /* 0 */ + "uncertainty", /* 1 */ + "uncertainties", /* 2 */ + "balances", /* 3 */ + "phase_data", /* 4 */ + "range", /* 5 */ + "minimal", /* 6 */ + "minimum", /* 7 */ + "balance", /* 8 */ + "bal", /* 9 */ + "sol", /* 10 */ + "phases", /* 11 */ + "ranges", /* 12 */ + "tolerance", /* 13 */ + "u_water", /* 14 */ + "uncertainty_water", /* 15 */ + "force", /* 16 */ + "force_solution", /* 17 */ + "force_solutions", /* 18 */ + "isotopes", /* 19 */ + "mineral_water", /* 20 */ + "phase", /* 21 */ + "multiple_precision", /* 22 */ + "mp_tolerance", /* 23 */ + "censor_mp", /* 24 */ + "lon_netpath", /* 25 */ + "pat_netpath" /* 26 */ + }; + int count_opt_list = 27; + + ptr = line; +/* + * Read solution number and description + */ + read_number_description(ptr, &n_user, &n_user_end, &description); +/* + * Malloc space for solution data + */ + if (inverse_search(n_user, &n) != NULL) + { + inverse_delete(n); + } + inverse_alloc(); + n = count_inverse - 1; +/* + * Initialize structure and use + */ + inverse[n].new_def = TRUE; + inverse[n].n_user = n_user; + inverse[n].range = FALSE; + inverse[n].range_max = 1000.; + inverse[n].tolerance = 1e-10; + inverse[n].minimal = FALSE; + inverse[n].description = description; + inverse[n].count_uncertainties = 1; + inverse[n].uncertainties[0] = 0.05; + inverse[n].count_ph_uncertainties = 1; + inverse[n].ph_uncertainties[0] = 0.05; + inverse[n].water_uncertainty = 0.0; + inverse[n].mineral_water = TRUE; + inverse[n].mp = FALSE; + inverse[n].mp_tolerance = 1e-12; + inverse[n].mp_censor = 1e-20; + inverse[n].netpath = NULL; + inverse[n].pat = NULL; +/* + * Read data for inverse modeling + */ + opt_save = OPTION_ERROR; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = opt; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_DEFAULT: + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in INVERSE_MODELING keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* solutions */ + case 10: /* solution */ + inverse[n].solns = + read_list_ints(&next_char, &inverse[n].count_solns, TRUE); + opt_save = OPTION_ERROR; + break; + case 1: /* uncertainty */ + case 2: /* uncertainties */ + inverse[n].uncertainties = + (LDBLE *) free_check_null(inverse[n].uncertainties); + inverse[n].uncertainties = + read_list_doubles(&next_char, + &inverse[n].count_uncertainties); + opt_save = OPTION_ERROR; + break; + case 3: /* balances */ + case 8: /* balance */ + case 9: /* bal */ + read_inv_balances(&(inverse[n]), next_char); + break; + case 4: /* phase_data */ + case 11: /* phases */ + case 21: /* phase */ + read_inv_phases(&(inverse[n]), next_char); + break; + case 5: /* range */ + case 12: /* ranges */ + inverse[n].range = TRUE; + j = sscanf(next_char, SCANFORMAT, &range_max); + if (j == 1) + { + inverse[n].range_max = range_max; + } + opt_save = OPTION_ERROR; + break; + case 6: /* minimal */ + case 7: /* minimum */ + inverse[n].minimal = TRUE; + opt_save = OPTION_ERROR; + break; + case 13: /* tolerance */ + j = sscanf(next_char, SCANFORMAT, &inv_tol); + if (j == 1) + { + inverse[n].tolerance = inv_tol; + } + opt_save = OPTION_ERROR; + break; + case 14: /* u_water */ + case 15: /* uncertainty_water */ + j = sscanf(next_char, SCANFORMAT, &water_uncertainty); + if (j == 1) + { + inverse[n].water_uncertainty = water_uncertainty; + } + opt_save = OPTION_ERROR; + break; + case 16: /* force */ + case 17: /* force_solution */ + case 18: /* force_solutions */ + inverse[n].force_solns = + (int *) free_check_null(inverse[n].force_solns); + inverse[n].force_solns = + read_list_t_f(&next_char, &inverse[n].count_force_solns); + opt_save = OPTION_ERROR; + break; + case 19: /* isotope values */ + read_inv_isotopes(&(inverse[n]), next_char); + break; + case 20: /* mineral_water */ + inverse[n].mineral_water = get_true_false(next_char, TRUE); + opt_save = OPTION_ERROR; + break; + case 22: /* multiple_precision */ + inverse[n].mp = get_true_false(next_char, TRUE); + opt_save = OPTION_ERROR; + break; + case 23: /* mp_tolerance */ + j = sscanf(next_char, SCANFORMAT, &inv_tol); + if (j == 1) + { + inverse[n].mp_tolerance = fabs(inv_tol); + } + opt_save = OPTION_ERROR; + break; + case 24: /* censor_mp */ + j = sscanf(next_char, SCANFORMAT, &inv_tol); + if (j == 1) + { + inverse[n].mp_censor = fabs(inv_tol); + } + opt_save = OPTION_ERROR; + break; + case 25: /* lon_netpath */ + /*copy_token(file_name, &next_char, &l); */ + if (string_trim(next_char) != EMPTY) + { + inverse[n].netpath = string_hsave(next_char); + } + else + { + inverse[n].netpath = string_hsave("netpath"); + } + opt_save = OPTION_ERROR; + break; + case 26: /* pat_netpath */ + /*copy_token(file_name, &next_char, &l); */ + if (string_trim(next_char) != EMPTY) + { + inverse[n].pat = string_hsave(next_char); + } + else + { + inverse[n].pat = string_hsave("netpath"); + } + opt_save = OPTION_ERROR; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } +/* + * Default: soln 1 -> soln 2 + */ + if (inverse[n].count_solns == 0) + { + inverse[n].solns = (int *) PHRQ_malloc(2 * sizeof(int)); + if (inverse[n].solns == NULL) + malloc_error(); + inverse[n].solns[0] = 1; + inverse[n].solns[1] = 2; + inverse[n].count_solns = 2; + } +/* + * Sort isotopes + */ + if (inverse[n].count_isotopes > 0) + { + qsort(inverse[n].isotopes, + (size_t) inverse[n].count_isotopes, + (size_t) sizeof(struct inv_isotope), inverse_isotope_compare); + } + + if (inverse[n].count_i_u > 0) + { + qsort(inverse[n].i_u, + (size_t) inverse[n].count_i_u, + (size_t) sizeof(struct inv_isotope), inverse_isotope_compare); + } + + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_inv_balances(struct inverse *inverse_ptr, char *ptr) +/* ---------------------------------------------------------------------- */ +{ + int j, l, count; + char token[MAX_LENGTH]; +/* + * Read element name + */ + j = copy_token(token, &ptr, &l); + if (j == EMPTY) + { + return (OK); + } + else if (j == LOWER && strcmp_nocase_arg1(token, "ph") != 0) + { + error_msg("Expecting element name.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + else if (strcmp_nocase_arg1(token, "ph") != 0) + { + inverse_ptr->elts = + (struct inv_elts *) PHRQ_realloc(inverse_ptr->elts, + (size_t) (inverse_ptr-> + count_elts + + 1) * + sizeof(struct inv_elts)); + if (inverse_ptr->elts == NULL) + malloc_error(); + replace("(+", "(", token); + inverse_ptr->elts[inverse_ptr->count_elts].name = string_hsave(token); +/* + * Read element uncertainties + */ + inverse_ptr->elts[inverse_ptr->count_elts].uncertainties = + read_list_doubles(&ptr, &count); + inverse_ptr->elts[inverse_ptr->count_elts].count_uncertainties = + count; + inverse_ptr->count_elts++; + } + else if (strcmp_nocase_arg1(token, "ph") == 0) + { + inverse_ptr->ph_uncertainties = + (LDBLE *) free_check_null(inverse_ptr->ph_uncertainties); + inverse_ptr->ph_uncertainties = read_list_doubles(&ptr, &count); + inverse_ptr->count_ph_uncertainties = count; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_inv_isotopes(struct inverse *inverse_ptr, char *ptr) +/* ---------------------------------------------------------------------- */ +{ + int i, j, l, l1, l2, count; + LDBLE isotope_number; + char token[MAX_LENGTH], token1[MAX_LENGTH]; + char *ptr1, *ptr2; + const char * redox_name, *element_name; +/* + * Read element name + */ + ptr1 = ptr; + j = copy_token(token, &ptr1, &l); +/* + * ptr1 is start of uncertainties + */ + if (j == EMPTY) + { + return (OK); + } + else if (j != DIGIT) + { + error_msg("Expecting isotope to begin with isotope number.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + return (ERROR); + } +/* + * Read isotope name + */ + ptr2 = token; + get_num(&ptr2, &isotope_number); + if (ptr2[0] == '\0' || isupper((int) ptr2[0]) == FALSE) + { + error_msg("Expecting element name.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + return (ERROR); + } + + /* redox state name with parentheses */ + redox_name = string_hsave(ptr2); + + copy_token(token, &ptr2, &l1); + replace("(", " ", token); + ptr2 = token; + + /* element name, without parentheses */ + copy_token(token1, &ptr2, &l2); + element_name = string_hsave(token1); + +/* + * add element name to inv_ptr->isotopes + */ + for (i = 0; i < inverse_ptr->count_isotopes; i++) + { + if (element_name == inverse_ptr->isotopes[i].elt_name) + break; + } + if (i == inverse_ptr->count_isotopes) + { + inverse_ptr->isotopes = + (struct inv_isotope *) PHRQ_realloc(inverse_ptr->isotopes, + (size_t) (inverse_ptr-> + count_isotopes + + 1) * + sizeof(struct inv_isotope)); + if (inverse_ptr->isotopes == NULL) + malloc_error(); + inverse_ptr->isotopes[inverse_ptr->count_isotopes].isotope_number = + isotope_number; + inverse_ptr->isotopes[inverse_ptr->count_isotopes].elt_name = + element_name; + inverse_ptr->isotopes[inverse_ptr->count_isotopes].uncertainties = + (LDBLE *) PHRQ_malloc((size_t) sizeof(LDBLE)); + if (inverse_ptr->isotopes[inverse_ptr->count_isotopes]. + uncertainties == NULL) + malloc_error(); + inverse_ptr->count_isotopes++; + } +/* + * add redox state name to inv_ptr->i_u + */ + inverse_ptr->i_u = + (struct inv_isotope *) PHRQ_realloc(inverse_ptr->i_u, + (size_t) (inverse_ptr-> + count_i_u + + 1) * + sizeof(struct inv_isotope)); + if (inverse_ptr->i_u == NULL) + { + malloc_error(); + return (OK); + } + inverse_ptr->i_u[inverse_ptr->count_i_u].elt_name = redox_name; + inverse_ptr->i_u[inverse_ptr->count_i_u].isotope_number = isotope_number; +/* + * Read isotope uncertainties + */ + inverse_ptr->i_u[inverse_ptr->count_i_u].uncertainties = + read_list_doubles(&ptr1, &count); + inverse_ptr->i_u[inverse_ptr->count_i_u].count_uncertainties = count; + inverse_ptr->count_i_u++; + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_inv_phases(struct inverse *inverse_ptr, char *ptr) +/* ---------------------------------------------------------------------- */ +{ + int j, l; + char token[MAX_LENGTH], token1[MAX_LENGTH]; + char *ptr1; + std::vector isotopes; +/* + * Read phase name + */ + j = copy_token(token, &ptr, &l); + if (j == EMPTY) + return (OK); + inverse_ptr->phases = + (struct inv_phases *) PHRQ_realloc(inverse_ptr->phases, + (size_t) (inverse_ptr-> + count_phases + + 1) * + sizeof(struct inv_phases)); + if (inverse_ptr->phases == NULL) + malloc_error(); + inverse_ptr->phases[inverse_ptr->count_phases].name = string_hsave(token); +/* + * Read constraint, force, and isotopes + */ + inverse_ptr->phases[inverse_ptr->count_phases].constraint = EITHER; + inverse_ptr->phases[inverse_ptr->count_phases].force = FALSE; + for (;;) + { + cxxSolutionIsotope temp_isotope; + j = copy_token(token, &ptr, &l); + if (j == EMPTY) + break; + strcpy(token1, token); + str_tolower(token1); + if (token1[0] == 'p') + { + inverse_ptr->phases[inverse_ptr->count_phases].constraint = + PRECIPITATE; + } + else if (token1[0] == 'd') + { + inverse_ptr->phases[inverse_ptr->count_phases].constraint = + DISSOLVE; + } + else if (token[0] == 'f') + { + inverse_ptr->phases[inverse_ptr->count_phases].force = TRUE; + } + else if (j == DIGIT) + { +/* + * read isotope data + */ + ptr1 = token; + + /* isotope number */ + get_num(&ptr1, &dummy); + temp_isotope.Set_isotope_number(dummy); + if (ptr1[0] == '\0' || isupper((int) ptr1[0]) == FALSE) + { + error_string = sformatf( "Expecting element name: %s.", ptr1); + error_msg(error_string, CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + + /* element name */ + temp_isotope.Set_elt_name(ptr1); + + /* ratio */ + j = copy_token(token, &ptr, &l); + if (j != DIGIT) + { + error_msg("Expecting isotope ratio for phase.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + sscanf(token, SCANFORMAT, &dummy); + temp_isotope.Set_ratio(dummy); + + /* read and store isotope ratio uncertainty */ + prev_next_char = ptr; + if (copy_token(token, &ptr, &l) != DIGIT) + { + input_error++; + error_string = sformatf( + "Expected numeric value for uncertainty in isotope ratio, but found:\n %s", + prev_next_char); + error_msg(error_string, CONTINUE); + continue; + } + sscanf(token, SCANFORMAT, &dummy); + temp_isotope.Set_ratio_uncertainty(dummy); + temp_isotope.Set_ratio_uncertainty_defined(true); + isotopes.push_back(temp_isotope); + } + else + { + error_string = sformatf( + "Unknown option for inverse modeling phase."); + warning_msg(error_string); + } + } + if (isotopes.size() > 0) + { + inverse_ptr->phases[inverse_ptr->count_phases].isotopes = + (struct isotope *) PHRQ_malloc(isotopes.size() * sizeof(struct isotope)); + for (size_t i = 0; i < isotopes.size(); i++) + { + struct isotope *iso_ptr = &(inverse_ptr->phases[inverse_ptr->count_phases].isotopes[i]); + iso_ptr->isotope_number = isotopes[i].Get_isotope_number(); + iso_ptr->elt_name = string_hsave(isotopes[i].Get_elt_name().c_str()); + iso_ptr->isotope_name = string_hsave(isotopes[i].Get_isotope_name().c_str()); + iso_ptr->total = isotopes[i].Get_total(); + iso_ptr->ratio = isotopes[i].Get_ratio(); + if (isotopes[i].Get_ratio_uncertainty_defined()) + iso_ptr->ratio_uncertainty = isotopes[i].Get_ratio_uncertainty(); + else + iso_ptr->ratio_uncertainty = NAN; + iso_ptr->x_ratio_uncertainty = isotopes[i].Get_x_ratio_uncertainty(); + iso_ptr->coef = isotopes[i].Get_coef(); + iso_ptr->master = NULL; + iso_ptr->primary = NULL; + } + inverse_ptr->phases[inverse_ptr->count_phases].count_isotopes = (int) isotopes.size(); + } + else + { + inverse_ptr->phases[inverse_ptr->count_phases].isotopes = NULL; + inverse_ptr->phases[inverse_ptr->count_phases].count_isotopes = 0; + } + inverse_ptr->count_phases++; + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_kinetics(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads kinetics data + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ +/* + * Read kinetics + */ + char *ptr; + char *description; + std::string token; + int n_user, n_user_end; + LDBLE step; + + int return_value, opt; + char *next_char; + const char *opt_list[] = { + "tol", /* 0 */ + "m", /* 1 */ + "m0", /* 2 */ + "parms", /* 3 */ + "formula", /* 4 */ + "steps", /* 5 */ + "step_divide", /* 6 */ + "parameters", /* 7 */ + "runge-kutta", /* 8 */ + "runge_kutta", /* 9 */ + "rk", /* 10 */ + "bad_step_max", /* 11 */ + "cvode", /* 12 */ + "cvode_steps", /* 13 */ + "cvode_order", /* 14 */ + "time_steps" /* 15 */ + }; + int count_opt_list = 16; + +/* + * Read kinetics number + */ + ptr = line; + read_number_description(ptr, &n_user, &n_user_end, &description); + cxxKinetics temp_kinetics; + temp_kinetics.Set_n_user(n_user); + temp_kinetics.Set_n_user_end(n_user_end); + temp_kinetics.Set_description(description); + description = (char *) free_check_null(description); + cxxKineticsComp *kinetics_comp_ptr = NULL; + std::string stdunits; +/* + * Set use data to first read + */ + if (use.Get_kinetics_in() == FALSE) + { + use.Set_kinetics_in(true); + use.Set_n_kinetics_user(n_user); + } +/* + * Read kinetics data + */ + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_DEFAULT: /* allocate space, read new name */ + if (kinetics_comp_ptr) + { + temp_kinetics.Get_kinetics_comps().push_back(*kinetics_comp_ptr); + delete kinetics_comp_ptr; + } + kinetics_comp_ptr = new cxxKineticsComp; + ptr = line; + copy_token(token, &ptr); + kinetics_comp_ptr->Set_rate_name(token.c_str()); + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in KINETICS keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* tolerance */ + if (kinetics_comp_ptr == NULL) + { + error_string = sformatf( "No rate name has been defined."); + error_msg(error_string, CONTINUE); + input_error++; + } + else + { + prev_next_char = next_char; + if (copy_token(token, &next_char) == DIGIT) + { + kinetics_comp_ptr->Set_tol(strtod(token.c_str(), &ptr)); + } + else + { + error_string = sformatf( + "Expecting numerical value for tolerance, but found:\n %s", + prev_next_char); + error_msg(error_string, CONTINUE); + input_error++; + } + } + break; + case 1: /* m */ + if (kinetics_comp_ptr == NULL) + { + error_string = sformatf( "No rate name has been defined."); + error_msg(error_string, CONTINUE); + input_error++; + } + else + { + prev_next_char = next_char; + if (copy_token(token, &next_char) == DIGIT) + { + kinetics_comp_ptr->Set_m(strtod(token.c_str(), &ptr)); + } + else + { + error_string = sformatf( + "Expecting numerical value for moles of reactant (m), but found:\n %s", + prev_next_char); + error_msg(error_string, CONTINUE); + input_error++; + } + } + break; + case 2: /* m0 */ + if (kinetics_comp_ptr == NULL) + { + error_string = sformatf( "No rate name has been defined."); + error_msg(error_string, CONTINUE); + input_error++; + } + else + { + prev_next_char = next_char; + if (copy_token(token, &next_char) == DIGIT) + { + kinetics_comp_ptr->Set_m0(strtod(token.c_str(), &ptr)); + } + else + { + error_string = sformatf( + "Expecting numerical value for initial moles of reactant (m0), but found:\n %s", + prev_next_char); + error_msg(error_string, CONTINUE); + input_error++; + } + } + break; + case 3: /* parms */ + case 7: /* parameters */ + if (kinetics_comp_ptr == NULL) + { + error_string = sformatf( "No rate name has been defined."); + error_msg(error_string, CONTINUE); + input_error++; + } + else + { + int j; + while ((j = copy_token(token, &next_char)) != EMPTY) + { + /* + * Store a LDBLE parameter + */ + if (j == DIGIT) + { + kinetics_comp_ptr->Get_d_params().push_back(strtod(token.c_str(), &ptr)); + } + else + { + /* + * Store a character parameter + */ + kinetics_comp_ptr->Get_c_params().push_back(token); + } + } + } + break; + case 4: /* formula */ + if (kinetics_comp_ptr == NULL) + { + error_string = sformatf( "No rate name has been defined."); + error_msg(error_string, CONTINUE); + input_error++; + } + else + { + /* + * Store reactant name, default coefficient + */ + ptr = next_char; + bool have_name = false; + std::string name; + LDBLE coef = 1; + while (copy_token(token, &ptr) != EMPTY) + { + coef = 1; + if (isalpha((int) token[0]) || (token[0] == '(') + || (token[0] == '[')) + { + if (have_name) + { + kinetics_comp_ptr->Get_namecoef().add(name.c_str(), coef); + } + name = token; + have_name = true; + } + else + { + if (!have_name) + { + error_string = sformatf( "No phase or chemical formula has been defined."); + error_msg(error_string, CONTINUE); + input_error++; + } + /* + * Store relative coefficient + */ + int j = sscanf(token.c_str(), SCANFORMAT, &coef); + + if (j == 1) + { + kinetics_comp_ptr->Get_namecoef().add(name.c_str(), coef); + have_name = false; + } + else + { + error_msg + ("Reading relative coefficient of reactant.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + } + } + if (have_name) + { + kinetics_comp_ptr->Get_namecoef().add(name.c_str(), coef); + } + } + break; + case 5: /* steps */ + case 15: /* time_steps */ + /* + * Read one or more kinetics time increments + */ + { + int j; + while ((j = copy_token(token, &next_char)) != EMPTY) + { + if (j == DIGIT) + { + /* Read next step increment(s) */ + /* multiple, equal timesteps 15 aug. 2005 */ + if (Utilities::replace("*", " ", token)) + { + int k; + if (sscanf(token.c_str(), "%d" SCANFORMAT, &k, &step) == 2) + { + for (int i = 0; i < k; i++) + { + temp_kinetics.Get_steps().push_back(step); + } + } + else + { + input_error++; + error_msg + ("Format error in multiple, equal KINETICS timesteps.\nCorrect is (for example): 20 4*10 2*5 3\n", + CONTINUE); + } + } + else + { + step = strtod(token.c_str(), &ptr); + temp_kinetics.Get_steps().push_back(step); + } + } + else + { + Utilities::str_tolower(token); + if (token.substr(0,1) == "i" ) + { + /* + * Read number of increments + */ + if (temp_kinetics.Get_steps().size() != 1) + { + error_msg + ("To define equal time increments, only one total time should be defined.", + CONTINUE); + input_error++; + break; + } + temp_kinetics.Set_equalIncrements(true); + do + { + int i = 1; + j = sscanf(token.c_str(), "%d", &i); + if (j == 1) + { + temp_kinetics.Set_count(abs(i)); + break; + } + else if (j == 1 && i < 0) + { + error_msg + ("Expecting positive number for number of equal " + "time increments for kinetics.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + } + while (copy_token(token, &next_char) != EMPTY); + } + else + { + stdunits = token; + } + } + } + } + break; + case 6: /* step_divide */ + if (copy_token(token, &next_char) == DIGIT) + { + sscanf(token.c_str(), SCANFORMAT, &dummy); + temp_kinetics.Set_step_divide(dummy); + } + else + { + error_string = sformatf( + "Expecting numerical value for step_divide."); + error_msg(error_string, CONTINUE); + input_error++; + } + break; + case 8: /* runge-kutta */ + case 9: /* runge_kutta */ + case 10: /* rk */ + { + int j = copy_token(token, &next_char); + if (j == DIGIT) + { + temp_kinetics.Set_rk((int) strtod(token.c_str(), &ptr)); + } + else if (j == EMPTY) + { + } + else + { + error_string = sformatf( + "Expecting order for Runge-Kutta method."); + error_msg(error_string, CONTINUE); + input_error++; + } + } + break; + case 11: /* bad_step_max */ + { + int j = copy_token(token, &next_char); + if (j == DIGIT) + { + temp_kinetics.Set_bad_step_max((int) strtod(token.c_str(), &ptr)); + } + else if (j == EMPTY) + { + } + else + { + error_string = sformatf( "Expecting maximal bad steps number."); + error_msg(error_string, CONTINUE); + input_error++; + } + } + break; + case 12: /* cvode */ + temp_kinetics.Set_use_cvode(get_true_false(next_char, TRUE) == TRUE); + break; + case 13: /* cvode_steps */ + { + int j = copy_token(token, &next_char); + if (j == DIGIT) + { + temp_kinetics.Set_cvode_steps((int) strtod(token.c_str(), &ptr)); + } + else if (j == EMPTY) + { + } + else + { + error_string = sformatf( + "Expecting maximum number of steps for one call to cvode."); + error_msg(error_string, CONTINUE); + input_error++; + } + } + break; + case 14: /* cvode_order */ + { + int j = copy_token(token, &next_char); + if (j == DIGIT) + { + temp_kinetics.Set_cvode_order((int) strtod(token.c_str(), &ptr)); + } + else if (j == EMPTY) + { + } + else + { + error_string = sformatf( + "Expecting number of terms (order) used in cvode (1-5)."); + error_msg(error_string, CONTINUE); + input_error++; + } + } + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + + // save last comp + if (kinetics_comp_ptr) + { + temp_kinetics.Get_kinetics_comps().push_back(*kinetics_comp_ptr); + delete kinetics_comp_ptr; + } +/* + * Default reactant + */ + for (size_t i = 0; i < temp_kinetics.Get_kinetics_comps().size(); i++) + { + cxxKineticsComp *kinetics_comp_ptr = &(temp_kinetics.Get_kinetics_comps()[i]); + if (kinetics_comp_ptr->Get_namecoef().size() == 0) + { + kinetics_comp_ptr->Get_namecoef().add(kinetics_comp_ptr->Get_rate_name().c_str(), 1.0); + } + } +/* + * Default 1 sec + */ + if (temp_kinetics.Get_steps().size() == 0) + { + temp_kinetics.Get_steps().push_back(1.0); + } + else if (stdunits.size() > 0) + { + std::vector::iterator it; + for (it = temp_kinetics.Get_steps().begin(); it != temp_kinetics.Get_steps().end(); it++) + { + *it = Utilities::convert_time(*it, stdunits, "s"); + } + } +/* + * set defaults for moles + */ + for (size_t i = 0; i < temp_kinetics.Get_kinetics_comps().size(); i++) + { + cxxKineticsComp *kinetics_comp_ptr = &(temp_kinetics.Get_kinetics_comps()[i]); + if (kinetics_comp_ptr->Get_m0() < 0) + { + if (kinetics_comp_ptr->Get_m() < 0) + { + kinetics_comp_ptr->Set_m0(1); + } + else + { + kinetics_comp_ptr->Set_m0(kinetics_comp_ptr->Get_m()); + } + } + if (kinetics_comp_ptr->Get_m() < 0) + { + kinetics_comp_ptr->Set_m(kinetics_comp_ptr->Get_m0()); + } + } + Rxn_kinetics_map[n_user] = temp_kinetics; + return (return_value); +} +/* ---------------------------------------------------------------------- */ +LDBLE * Phreeqc:: +read_list_doubles(char **ptr, int *count_doubles) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads a list of LDBLE numbers until end of line is reached or + * a LDBLE cannot be read from a token. + * + * Arguments: + * ptr entry: points to line to read from + * exit: points to next non-LDBLE token or end of line + * + * count_doubles exit: number of LDBLEs read + * + * Returns: + * pointer to a list of count_doubles LDBLEs. + */ + + LDBLE *LDBLE_list; + char token[MAX_LENGTH]; + LDBLE value; + char *ptr_save; + int l; + + LDBLE_list = (LDBLE *) PHRQ_malloc(sizeof(LDBLE)); + if (LDBLE_list == NULL) + malloc_error(); + *count_doubles = 0; + + ptr_save = *ptr; + while (copy_token(token, ptr, &l) != EMPTY) + { + if (sscanf(token, SCANFORMAT, &value) == 1) + { + *count_doubles = *count_doubles + 1; + LDBLE_list = + (LDBLE *) PHRQ_realloc(LDBLE_list, + (size_t) (*count_doubles) * + sizeof(LDBLE)); + if (LDBLE_list == NULL) + malloc_error(); + LDBLE_list[(*count_doubles) - 1] = value; + ptr_save = *ptr; + } + else + { + *ptr = ptr_save; + break; + } + } + return (LDBLE_list); +} + +/* ---------------------------------------------------------------------- */ +int * Phreeqc:: +read_list_ints(char **ptr, int *count_ints, int positive) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads a list of int numbers until end of line is reached or + * an int cannot be read from a token. + * + * Arguments: + * ptr entry: points to line to read from + * exit: points to next non-int token or end of line + * + * count_ints exit: number of LDBLEs read + * + * positive entry: if TRUE, expects to read only positive integers + * + * Returns: + * pointer to a list of count_ints ints. + */ + int *int_list; + char token[MAX_LENGTH]; + int value; + int l; + char *ptr_save; + + int_list = (int *) PHRQ_malloc(sizeof(int)); + if (int_list == NULL) + malloc_error(); + *count_ints = 0; + + ptr_save = *ptr; + while (copy_token(token, ptr, &l) != EMPTY) + { + if (sscanf(token, "%d", &value) == 1) + { + (*count_ints)++; + int_list = + (int *) PHRQ_realloc(int_list, + (size_t) (*count_ints) * sizeof(int)); + if (int_list == NULL) + { + malloc_error(); + return (NULL); + } + int_list[(*count_ints) - 1] = value; + if (value <= 0 && positive == TRUE) + { + error_msg("Expected an integer greater than zero.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + ptr_save = *ptr; + } + else + { + *ptr = ptr_save; + break; + } + } + return (int_list); +} + +/* ---------------------------------------------------------------------- */ +int * Phreeqc:: +read_list_ints_range(char **ptr, int *count_ints, int positive, int *int_list) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads a list of int numbers until end of line is reached or + * an int cannot be read from a token. + * + * Arguments: + * ptr entry: points to line to read from + * exit: points to next non-int token or end of line + * + * count_ints entry: number of ints already in list + * + * positive entry: if TRUE, expects to read only positive integers + * + * Returns: + * pointer to a list of count_ints ints + */ + char token[MAX_LENGTH]; + int value, value1, value2; + int i, l; + char *ptr_save; + + if (int_list == NULL) + { + int_list = (int *) PHRQ_malloc(sizeof(int)); + if (int_list == NULL) + { + malloc_error(); + return (NULL); + } + *count_ints = 0; + } + ptr_save = *ptr; + while (copy_token(token, ptr, &l) != EMPTY) + { + if (sscanf(token, "%d", &value) == 1) + { + /* Read an integer */ + (*count_ints)++; + int_list = + (int *) PHRQ_realloc(int_list, + (size_t) (*count_ints) * sizeof(int)); + if (int_list == NULL) + { + malloc_error(); + return (NULL); + } + int_list[(*count_ints) - 1] = value; + if (value <= 0 && positive == TRUE) + { + error_msg("Expected an integer greater than zero.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + /* Read range of integers */ + if (replace("-", " ", token) == TRUE) + { + if (sscanf(token, "%d %d", &value1, &value2) != 2) + { + error_msg("Expected an integer range n-m.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + else if (value2 < value1) + { + error_msg("Expected an integer range n-m, with n <= m.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + else if (value2 <= 0 && positive == TRUE) + { + error_msg("Expected an integer greater than zero.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + else + { + for (i = value1 + 1; i <= value2; i++) + { + (*count_ints)++; + int_list = + (int *) PHRQ_realloc(int_list, + (size_t) (*count_ints) * + sizeof(int)); + if (int_list == NULL) + { + malloc_error(); + return (NULL); + } + int_list[(*count_ints) - 1] = i; + } + } + } + ptr_save = *ptr; + } + else + { + *ptr = ptr_save; + break; + } + } + return (int_list); +} + +/* ---------------------------------------------------------------------- */ +int * Phreeqc:: +read_list_t_f(char **ptr, int *count_ints) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads a list of true and false until end of line is reached or + * until non- t or f is found + * + * Arguments: + * ptr entry: points to line to read from + * exit: points to next non-int token or end of line + * + * count_ints exit: number of LDBLEs read + * + * positive entry: if TRUE, expects to read only positive integers + * + * Returns: + * pointer to a list of count_ints ints. + */ + int *int_list; + char token[MAX_LENGTH]; + int value; + int l; + + int_list = (int *) PHRQ_malloc(sizeof(int)); + if (int_list == NULL) + malloc_error(); + *count_ints = 0; + + while (copy_token(token, ptr, &l) != EMPTY) + { + str_tolower(token); + if (token[0] == 't') + { + value = TRUE; + } + else if (token[0] == 'f') + { + value = FALSE; + } + else + { + error_msg("Expected TRUE or FALSE.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + (*count_ints)++; + int_list = + (int *) PHRQ_realloc(int_list, + (size_t) (*count_ints) * sizeof(int)); + if (int_list == NULL) + malloc_error(); + int_list[(*count_ints) - 1] = value; + } + return (int_list); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_log_k_only(char *ptr, LDBLE * log_k) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read log k + */ + *log_k = 0.0; + replace("=", " ", ptr); + if (sscanf(ptr, SCANFORMAT, log_k) < 1) + { + input_error++; + error_msg("Expecting log k.", CONTINUE); + return (ERROR); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_t_c_only(char *ptr, LDBLE *t_c) +/* ---------------------------------------------------------------------- */ +{ + *t_c = 0.0; + replace("=", " ", ptr); + if (sscanf(ptr, SCANFORMAT, t_c) < 1) + { + input_error++; + error_msg("Expecting numeric value for critical temperature T_c (K)", CONTINUE); + return (ERROR); + } + return OK; +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_p_c_only(char *ptr, LDBLE * p_c) +/* ---------------------------------------------------------------------- */ +{ + *p_c = 0.0; + replace("=", " ", ptr); + if (sscanf(ptr, SCANFORMAT, p_c) < 1) + { + input_error++; + error_msg("Expecting numeric value for critical pressure P_c (atm)", CONTINUE); + return (ERROR); + } + return OK; +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_omega_only(char *ptr, LDBLE *omega) +/* ---------------------------------------------------------------------- */ +{ + *omega = 0.0; + replace("=", " ", ptr); + if (sscanf(ptr, SCANFORMAT, omega) < 1) + { + input_error++; + error_msg("Expecting numeric value for acentric factor Omega", CONTINUE); + return (ERROR); + } + return OK; +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_aq_species_vm_parms(char *ptr, LDBLE * delta_v) +/* ---------------------------------------------------------------------- */ +{ + int j; + /* + * Read supcrt parms and Ionic strength terms + */ + for (j = 0; j < 9; j++) + { + delta_v[j] = 0.0; + } + delta_v[9] = 1.0; +/* Vmax, dmax... + delta_v[10] = 999.0; + delta_v[11] = 1.0; */ + j = sscanf(ptr, SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT /*SCANFORMAT SCANFORMAT */, + /* a1..a4 */ + &(delta_v[0]), &(delta_v[1]), &(delta_v[2]), &(delta_v[3]), + /* wref */ + &(delta_v[4]), + /* b_Av */ + &(delta_v[5]), + /* c1..c4 */ + &(delta_v[6]), &(delta_v[7]), &(delta_v[8]), &(delta_v[9])); + /* vmax, dmax + &(delta_v[10]), &(delta_v[11])); */ + if (j < 1) + { + input_error++; + error_msg("Expecting numeric values for calculating the species molar volume.", + CONTINUE); + return (ERROR); + } + /* multiply with factors. a1 is in cal/mol/bar. a2 in cal/mol, a3, a4 in cal K/mol + 41.84004 converts cal/mol/bar to cm3/mol. */ + delta_v[0] *= 41.84004e-1; + delta_v[1] *= 41.84004e2; + delta_v[2] *= 41.84004; + delta_v[3] *= 41.84004e4; + /* wref in cal/mol/bar */ + delta_v[4] *= 1e5; + + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_vm_only(char *ptr, LDBLE * delta_v, DELTA_V_UNIT * units) +/* ---------------------------------------------------------------------- */ +{ + int j, l; + char token[MAX_LENGTH]; + /* + * Read analytical expression + */ + for (j = 0; j < 8; j++) + { + delta_v[j] = 0.0; + } + j = sscanf(ptr, SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT, + &(delta_v[0]), &(delta_v[1]), &(delta_v[2]), &(delta_v[3]), + &(delta_v[4]), &(delta_v[5]), &(delta_v[6]), &(delta_v[7])); + if (j < 1) + { + input_error++; + error_msg("Expecting numeric value for the phase's molar volume, vm.", + CONTINUE); + return (ERROR); + } + /* + * Read delta V units + */ + *units = cm3_per_mol; + do + { + j = copy_token(token, &ptr, &l); + } while (j == DIGIT); + + if (j == EMPTY) + { + return (OK); + } + + LDBLE factor = 1.0; + if (j == UPPER || j == LOWER) + { + str_tolower(token); + if (strstr(token, "cm3") != NULL) + { + /* cm3/mol */ + ; + } + else if (strstr(token, "dm3") != NULL) + { + /* Convert dm3/mol to cm3/mol */ + factor = 1e3; + } + else if (strstr(token, "m3") != NULL) + { + /* Convert m3/mol to cm3/mol */ + factor = 1e6; + } + + for (int i = 0; i < 8; i++) + { + delta_v[i] *= factor; + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_phase_vm(char *ptr, LDBLE * delta_v, DELTA_V_UNIT * units) +/* ---------------------------------------------------------------------- */ +{ + int j, l; + char token[MAX_LENGTH]; + /* + * Read analytical expression + */ + for (j = 0; j < 1; j++) + { + delta_v[j] = 0.0; + } + j = sscanf(ptr, SCANFORMAT /*SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT*/, + &(delta_v[0])/*, &(delta_v[1]), &(delta_v[2]), &(delta_v[3]), + &(delta_v[4]), &(delta_v[5]), &(delta_v[6]), &(delta_v[7])*/); + if (j < 1) + { + input_error++; + error_msg("Expecting numeric value for the phase's molar volume, vm.", + CONTINUE); + return (ERROR); + } + /* + * Read delta V units + */ + *units = cm3_per_mol; + do + { + j = copy_token(token, &ptr, &l); + } while (j == DIGIT); + + if (j == EMPTY) + { + return (OK); + } + + LDBLE factor = 1.0; + if (j == UPPER || j == LOWER) + { + str_tolower(token); + if (strstr(token, "cm3") != NULL) + { + /* cm3/mol */ + ; + } + else if (strstr(token, "dm3") != NULL) + { + /* Convert dm3/mol to cm3/mol */ + factor = 1e3; + *units = dm3_per_mol; + } + else if (strstr(token, "m3") != NULL) + { + /* Convert m3/mol to cm3/mol */ + factor = 1e6; + *units = m3_per_mol; + } + + for (int i = 0; i < 1; i++) + { + delta_v[i] *= factor; + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_delta_h_only(char *ptr, LDBLE * delta_h, DELTA_H_UNIT * units) +/* ---------------------------------------------------------------------- */ +{ + int j, l, kilo, joul; + char token[MAX_LENGTH]; +/* + * Read delta H + */ + *delta_h = 0.0; + replace("=", " ", ptr); + j = copy_token(token, &ptr, &l); + if (j == EMPTY) + { + input_error++; + error_msg("Expecting numeric value for delta H.", CONTINUE); + return (ERROR); + } + if (sscanf(token, SCANFORMAT, delta_h) < 1) + { + input_error++; + error_msg("Expecting numeric value for delta H.", CONTINUE); + return (ERROR); + } +/* + * Read delta H units + */ + j = copy_token(token, &ptr, &l); + *units = kjoules; + kilo = TRUE; + joul = TRUE; + if (j == EMPTY) + { + return (OK); + } + if (j == UPPER || j == LOWER) + { + str_tolower(token); + if (strstr(token, "k") != token) + { + /* convert to kilo */ + kilo = FALSE; + *delta_h /= 1000.; + } + if (strstr(token, "c") != NULL) + { + /* convert to joules */ + *delta_h *= JOULES_PER_CALORIE; + joul = FALSE; + } + } + if (kilo == TRUE && joul == TRUE) + { + *units = kjoules; + } + else if (kilo == FALSE && joul == TRUE) + { + *units = joules; + } + else if (kilo == TRUE && joul == FALSE) + { + *units = kcal; + } + else if (kilo == FALSE && joul == FALSE) + { + *units = cal; + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_analytical_expression_only(char *ptr, LDBLE * log_k) +/* ---------------------------------------------------------------------- */ +{ + int j; + int num_terms = T_A6 - T_A1 + 1; +/* + * Read analytical expression + */ + for (j = 0; j < num_terms; j++) + { + log_k[j] = 0.0; + } + j = sscanf(ptr, SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT, + &(log_k[0]), &(log_k[1]), &(log_k[2]), &(log_k[3]), + &(log_k[4]), &(log_k[5])); + if (j < 1) + { + input_error++; + error_msg("Expecting numeric values for analytical expression.", + CONTINUE); + return (ERROR); + } + return (OK); +} + +/* VP: Density Start */ +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_millero_abcdef (char *ptr, LDBLE * abcdef) +/* ---------------------------------------------------------------------- */ +{ + int j; +/* + * Read a, b, c, d, e, f, and kappa parameters for Millero density model. + */ + for (j = 0; j < 7; j++) + { + abcdef[j] = 0.0; + } + j = + sscanf (ptr, SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT, + &(abcdef[0]), &(abcdef[1]), &(abcdef[2]), &(abcdef[3]), &(abcdef[4]), &(abcdef[5]), &(abcdef[6])); + if (j < 1) + { + input_error++; + error_msg ("Expecting numeric values for analytical expression.", + CONTINUE); + return (ERROR); + } + return (OK); +} +/* VP: Density End */ + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_viscosity_parms(char *ptr, LDBLE * Jones_Dole) +/* ---------------------------------------------------------------------- */ +{ + int j; +/* + * Read . + */ + for (j = 0; j <= 9; j++) + { + Jones_Dole[j] = 0.0; + } + j = + sscanf (ptr, SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT, + &(Jones_Dole[0]), &(Jones_Dole[1]), &(Jones_Dole[2]), &(Jones_Dole[3]), &(Jones_Dole[4]), &(Jones_Dole[5]), &(Jones_Dole[6]), &(Jones_Dole[7]), &(Jones_Dole[8]), &(Jones_Dole[9])); + if (j < 1) + { + input_error++; + error_msg ("Expecting numeric values for viscosity calculation.", + CONTINUE); + return (ERROR); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_incremental_reactions(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Define flow only + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int j, l; + char *ptr; + char token[MAX_LENGTH]; + + ptr = line; + /* read keyword */ + copy_token(token, &ptr, &l); + + /* read true or false */ + incremental_reactions = get_true_false(ptr, TRUE); +/* + * find next keyword + */ + while ((j = + check_line("Subroutine Read", FALSE, TRUE, TRUE, + FALSE)) != KEYWORD) + { + /* empty, eof, keyword, print */ + if (j == EOF) + return (EOF); + error_string = sformatf( "Unknown input: %s", line); + error_msg(error_string, CONTINUE); + input_error++; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_master_species(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads master species data from data file or input file + */ + int j, i, l; + char *ptr, *ptr1; + LDBLE l_z; + struct element *elts_ptr; + struct species *s_ptr; + char token[MAX_LENGTH], token1[MAX_LENGTH]; + + elts_ptr = NULL; + for (;;) + { + j = check_line("Master species", FALSE, TRUE, TRUE, TRUE); + if (j == EOF || j == KEYWORD) + { + break; + } +/* + * Get element name with valence, allocate space, store + */ + ptr = line; +/* + * Get element name and save pointer to character string + */ + if (copy_token(token, &ptr, &l) != UPPER && token[0] != '[') + { + parse_error++; + error_msg("Reading element for master species.", CONTINUE); + error_msg(line_save, CONTINUE); + continue; + } + /* + if (token[0] == '[') { + ptr1 = token; + get_elt(&ptr, element, &l); + strcpy(token, element); + } + */ + replace("(+", "(", token); +/* + * Delete master if it exists + */ + master_delete(token); +/* + * Increase pointer array, if necessary, and malloc space + */ + if (count_master >= max_master) + { + space((void **) ((void *) &master), count_master + 1, + &max_master, sizeof(struct master *)); + } + master[count_master++] = master_alloc(); +/* + * Set type to AQ + */ + master[count_master-1]->type = AQ; +/* + * Save element name + */ + master[count_master-1]->elt = element_store(token); + std::string ename = token; +/* + * Save pointer to species data for master species + */ + if ((copy_token(token, &ptr, &l) != UPPER) && + token[0] != '[' && (strcmp_nocase_arg1(token, "e-") != 0)) + { + parse_error++; + error_msg("Reading master species name.", CONTINUE); + error_msg(line_save, CONTINUE); + continue; + } + + s_ptr = s_search(token); + if (s_ptr != NULL) + { + master[count_master-1]->s = s_ptr; + } + else + { + ptr1 = token; + get_token(&ptr1, token1, &l_z, &l); + master[count_master-1]->s = s_store(token1, l_z, FALSE); + } + + std::string sname = token; + replace("("," ", ename); + std::istringstream iss(ename); + iss >> ename; + if (ename != "e" && ename != "E" && ename != "Alkalinity" && std::string::npos == sname.find(ename)) + { + input_error++; + std::ostringstream oss; + oss << "Master species, " << sname << " must contain the element, " << ename; + error_msg(oss.str().c_str(), CONTINUE); + continue; + } + +/* + * Read alkalinity for species + */ + copy_token(token, &ptr, &l); + i = sscanf(token, SCANFORMAT, &master[count_master-1]->alk); + if (i != 1) + { + input_error++; + if (elts_ptr != NULL) + { + error_string = sformatf( + "Expected alkalinity for master species, %s, in master species input.", + elts_ptr->name); + } + else + { + error_string = sformatf( + "Expected alkalinity for master species in master species input."); + } + error_msg(error_string, CONTINUE); + continue; + } +/* + * Read default gfw for species + */ + i = copy_token(token, &ptr, &l); + if (i == DIGIT) + { + sscanf(token, SCANFORMAT, &master[count_master-1]->gfw); + } + else if (i == UPPER) + { + master[count_master-1]->gfw_formula = string_hsave(token); + } + else + { + input_error++; + if (elts_ptr != NULL) + { + error_string = sformatf( + "Expected gram formula weight for master species, %s, in master species input.", + elts_ptr->name); + } + else + { + error_string = sformatf( + "Expected gram formula weight for master species in master species input."); + } + error_msg(error_string, CONTINUE); + continue; + } +/* + * MAKE LISTS OF PRIMARY AND SECONDARY MASTER SPECIES + */ + if (strchr(master[count_master-1]->elt->name, '(') == NULL) + { + master[count_master-1]->primary = TRUE; + /* Read gram formula weight for primary */ + if (strcmp(master[count_master-1]->elt->name, "E") != 0) + { + elts_ptr = master[count_master-1]->elt; + i = copy_token(token, &ptr, &l); + if (i == DIGIT) + { + sscanf(token, SCANFORMAT, &elts_ptr->gfw); + } + else + { + input_error++; + if (elts_ptr != NULL) + { + error_string = sformatf( + "Expected gram formula weight for element, %s.", + elts_ptr->name); + } + else + { + error_string = sformatf( + "Expected gram formula weight for element."); + } + + error_msg(error_string, CONTINUE); + continue; + } + } + } + else + { + master[count_master-1]->primary = FALSE; + } + if (count_master >= max_master) + { + space((void **) ((void *) &master), count_master, &max_master, + sizeof(struct master *)); + } + + } + gfw_map.clear(); + return (j); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_mix(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads mixing fractions + */ + int n_user, n_user_end; + int return_value; + int n_solution; + LDBLE fraction; + int j, i, l; + char *ptr; + char token[MAX_LENGTH]; + char *description; + cxxMix temp_mix; + +/* + * Read mix number + */ + ptr = line; + read_number_description(ptr, &n_user, &n_user_end, &description); + + temp_mix.Set_n_user(n_user); + temp_mix.Set_n_user_end(n_user); + temp_mix.Set_description(description); + free_check_null(description); + +/* + * Set use data to first read + */ + if (use.Get_mix_in() == FALSE) + { + use.Set_mix_in(true); + use.Set_n_mix_user(n_user); + } +/* + * Read mixture data + */ + for (;;) + { + return_value = check_line("Mixture data", FALSE, TRUE, TRUE, TRUE); + /* empty, eof, keyword, print */ + if (return_value == EOF || return_value == KEYWORD) + { + break; + } + ptr = line; +/* + * Read n_user + */ + i = copy_token(token, &ptr, &l); + if (i == DIGIT) + { + sscanf(token, "%d ", &n_solution); + } + else + { + input_error++; + error_msg("Expected a solution number in mix input.", CONTINUE); + error_msg(line_save, CONTINUE); + continue; + } +/* + * Read fraction for solution + */ + copy_token(token, &ptr, &l); + j = sscanf(token, SCANFORMAT, &fraction); + if (j != 1) + { + input_error++; + error_msg("Expected a mixing fraction.", CONTINUE); + error_msg(line_save, CONTINUE); + continue; + } + +/* + * Save data + */ + temp_mix.Add(n_solution ,fraction); + } + if (temp_mix.Get_mixComps().size() == 0) + { + input_error++; + error_msg + ("Must define at least one solution number and mixing fraction for MIX input.", + CONTINUE); + } + Rxn_mix_map[n_user] = temp_mix; + + // copy if needed + if (n_user_end > n_user) + { + int i; + for (i = n_user + 1; i <= n_user_end; i++) + { + Utilities::Rxn_copy(Rxn_mix_map, n_user, i); + } + } + + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_entity_mix(std::map &mix_map) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads mixing fractions + */ + int return_value; + int n_solution; + LDBLE fraction; + int j, i, l; + char *ptr; + char token[MAX_LENGTH]; + cxxMix temp_mix; + +/* + * Read mix number + */ + ptr = line; + temp_mix.read_number_description(line); +/* + * Read mixture data + */ + for (;;) + { + return_value = check_line("Mix raw data", FALSE, TRUE, TRUE, TRUE); + /* empty, eof, keyword, print */ + if (return_value == EOF || return_value == KEYWORD) + { + break; + } + ptr = line; +/* + * Read n_user + */ + i = copy_token(token, &ptr, &l); + if (i == DIGIT) + { + sscanf(token, "%d ", &n_solution); + } + else + { + input_error++; + error_msg("Expected a number in mix input.", CONTINUE); + error_msg(line_save, CONTINUE); + continue; + } +/* + * Read fraction for entity + */ + copy_token(token, &ptr, &l); + j = sscanf(token, SCANFORMAT, &fraction); + if (j != 1) + { + input_error++; + error_msg("Expected a mixing fraction.", CONTINUE); + error_msg(line_save, CONTINUE); + continue; + } +/* + * Save data + */ + temp_mix.Add(n_solution ,fraction); + } + if (temp_mix.Get_mixComps().size() == 0) + { + input_error++; + error_msg + ("Must define at least one number and mixing fraction for mix input.", + CONTINUE); + } + mix_map[temp_mix.Get_n_user()] = temp_mix; + return (return_value); +} +#ifdef SKIP +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_solution_mix(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads mixing fractions + */ + int n_user, n_user_end; + int return_value; + int n_solution; + LDBLE fraction; + int j, i, l; + char *ptr; + char token[MAX_LENGTH]; + char *description; + cxxMix temp_mix; + +/* + * Read mix number + */ + ptr = line; + read_number_description(ptr, &n_user, &n_user_end, &description); + + temp_mix.Set_n_user(n_user); + temp_mix.Set_n_user_end(n_user_end); + temp_mix.Set_description(description); + free_check_null(description); +#ifdef SKIP +/* + * Set use data to first read + */ + if (use.Get_mix_in() == FALSE) + { + use.Set_mix_in(true); + use.Set_n_mix_user(n_user); + } +#endif +/* + * Read mixture data + */ + for (;;) + { + return_value = check_line("Mix raw data", FALSE, TRUE, TRUE, TRUE); + /* empty, eof, keyword, print */ + if (return_value == EOF || return_value == KEYWORD) + { + break; + } + ptr = line; +/* + * Read n_user + */ + i = copy_token(token, &ptr, &l); + if (i == DIGIT) + { + sscanf(token, "%d ", &n_solution); + } + else + { + input_error++; + error_msg("Expected a solution number in mix_raw input.", CONTINUE); + error_msg(line_save, CONTINUE); + continue; + } +/* + * Read fraction for solution + */ + copy_token(token, &ptr, &l); + j = sscanf(token, SCANFORMAT, &fraction); + if (j != 1) + { + input_error++; + error_msg("Expected a mixing fraction.", CONTINUE); + error_msg(line_save, CONTINUE); + continue; + } + +/* + * Save data + */ + temp_mix.Add(n_solution ,fraction); + } + if (temp_mix.Get_mixComps().size() == 0) + { + input_error++; + error_msg + ("Must define at least one solution number and mixing fraction for MIX_RAW input.", + CONTINUE); + } + Rxn_solution_mix_map[n_user] = temp_mix; +#ifdef SKIP + // copy if needed + if (n_user_end > n_user) + { + int i; + for (i = n_user + 1; i <= n_user_end; i++) + { + Utilities::Rxn_copy(Rxn_mix_map, n_user, i); + } + } +#endif + return (return_value); +} +#endif +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_number_description(char *ptr, int *n_user, + int *n_user_end, char **description, int allow_negative) +/* ---------------------------------------------------------------------- */ +{ + int l, n; + char token[MAX_LENGTH]; + char *ptr1; +/* + * Read user number, allow negative numbers Oct 3, 2011 + */ + copy_token(token, &ptr, &l); // keyword + ptr1 = ptr; + copy_token(token, &ptr, &l); + + if (!isdigit(token[0]) && token[0] != '-') + { + *n_user = 1; + *n_user_end = 1; + } + else + { + if (replace("-", " ", &token[1])) + { + n = sscanf(token, "%d%d", n_user, n_user_end); + if (n != 2) + { + if (next_keyword >= 0) + { + error_string = sformatf( "Reading number range for %s.", Keywords::Keyword_name_search(next_keyword).c_str()); + } + else + { + error_string = sformatf( "Reading number range for keyword."); + } + error_msg(error_string, CONTINUE); + input_error++; + } + ptr1 = ptr; + } + else + { + n = sscanf(token, "%d", n_user); + if (n != 1) + { + if (next_keyword >= 0) + { + error_string = sformatf( "Reading number range for %s.", Keywords::Keyword_name_search(next_keyword).c_str()); + } + else + { + error_string = sformatf( "Reading number range for keyword."); + } + error_msg(error_string, CONTINUE); + input_error++; + } + *n_user_end = *n_user; + ptr1 = ptr; + }; + } + if (*n_user < 0 && allow_negative == FALSE) + { + error_string = sformatf( "Negative number in number range not allowed for keyword."); + error_msg(error_string, CONTINUE); + input_error++; + } +/* + * Read description + */ + for (; isspace((int) ptr1[0]); ptr1++); + *description = string_duplicate(ptr1); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_phases(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read data for phases, parse equations + */ + int j, i, l; + int association; + char *ptr; + char token[MAX_LENGTH]; + char token1[MAX_LENGTH]; + struct phase *phase_ptr; + struct elt_list *next_elt; + struct rxn_token *token_ptr; + + int return_value, opt, opt_save; + char *next_char; + const char *opt_list[] = { + "no_check", /* 0 */ + "check", /* 1 */ + "log_k", /* 2 */ + "logk", /* 3 */ + "delta_h", /* 4 */ + "deltah", /* 5 */ + "analytical_expression", /* 6 */ + "a_e", /* 7 */ + "ae", /* 8 */ + "add_logk", /* 9 */ + "add_log_k", /* 10 */ + "add_constant", /* 11 */ + "t_c", /* 12 */ + "p_c", /* 13 */ + "omega", /* 14 */ + "vm" /* 15, molar volume, must replace delta_v */ + }; + int count_opt_list = 16; + association = FALSE; +/* + * Read eqn from file and call parser + */ + opt_save = OPTION_DEFAULT; + return_value = UNKNOWN; + phase_ptr = NULL; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in PHASES keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* no_check */ + if (phase_ptr == NULL) + break; + phase_ptr->check_equation = FALSE; + break; + case 1: /* check */ + if (phase_ptr == NULL) + break; + phase_ptr->check_equation = TRUE; + break; + case 2: /* log_k */ + case 3: /* logk */ + if (phase_ptr == NULL) + break; + read_log_k_only(next_char, &phase_ptr->logk[0]); + opt_save = OPTION_DEFAULT; + break; + case 4: /* delta_h */ + case 5: /* deltah */ + if (phase_ptr == NULL) + break; + read_delta_h_only(next_char, &phase_ptr->logk[1], + &phase_ptr->original_units); + opt_save = OPTION_DEFAULT; + break; + case 6: /* analytical_expression */ + case 7: /* a_e */ + case 8: /* ae */ + if (phase_ptr == NULL) + break; + read_analytical_expression_only(next_char, &(phase_ptr->logk[T_A1])); + opt_save = OPTION_DEFAULT; + break; + case 9: /* add_logk */ + case 10: /* add_log_k */ + if (phase_ptr == NULL) + break; + if (phase_ptr->count_add_logk == 0) + { + phase_ptr->add_logk = + (struct name_coef *) + PHRQ_malloc(sizeof(struct name_coef)); + if (phase_ptr->add_logk == NULL) + malloc_error(); + } + else + { + phase_ptr->add_logk = + (struct name_coef *) PHRQ_realloc(phase_ptr->add_logk, + (size_t) ((phase_ptr-> + count_add_logk + + + 1) * + sizeof + (struct + name_coef))); + if (phase_ptr->add_logk == NULL) + malloc_error(); + } + /* read name */ + if (copy_token(token, &next_char, &i) == EMPTY) + { + input_error++; + error_string = sformatf( + "Expected the name of a NAMED_EXPRESSION."); + error_msg(error_string, CONTINUE); + break; + } + phase_ptr->add_logk[phase_ptr->count_add_logk].name = + string_hsave(token); + /* read coef */ + i = sscanf(next_char, SCANFORMAT, + &phase_ptr->add_logk[phase_ptr->count_add_logk].coef); + if (i <= 0) + { + phase_ptr->add_logk[phase_ptr->count_add_logk].coef = 1; + } + phase_ptr->count_add_logk++; + opt_save = OPTION_DEFAULT; + break; + case 11: /* add_constant */ + if (phase_ptr == NULL) + break; + if (phase_ptr->count_add_logk == 0) + { + phase_ptr->add_logk = + (struct name_coef *) + PHRQ_malloc(sizeof(struct name_coef)); + if (phase_ptr->add_logk == NULL) + malloc_error(); + } + else + { + phase_ptr->add_logk = + (struct name_coef *) PHRQ_realloc(phase_ptr->add_logk, + (size_t) ((phase_ptr-> + count_add_logk + + + 1) * + sizeof + (struct + name_coef))); + if (phase_ptr->add_logk == NULL) + malloc_error(); + } + i = sscanf(next_char, SCANFORMAT, + &phase_ptr->add_logk[phase_ptr->count_add_logk].coef); + if (i <= 0) + { + input_error++; + error_string = sformatf( + "Expected the constant to add for log_K definition."); + error_msg(error_string, CONTINUE); + break; + } + /* set name */ + phase_ptr->add_logk[phase_ptr->count_add_logk].name = + string_hsave("XconstantX"); + /* read coef */ + phase_ptr->count_add_logk++; + opt_save = OPTION_DEFAULT; + break; + case 12: /* T_c */ + if (phase_ptr == NULL) + break; + read_t_c_only(next_char, &phase_ptr->t_c); + opt_save = OPTION_DEFAULT; + break; + case 13: /* P_c */ + if (phase_ptr == NULL) + break; + read_p_c_only(next_char, &phase_ptr->p_c); + opt_save = OPTION_DEFAULT; + break; + case 14: /* Omega */ + if (phase_ptr == NULL) + break; + read_omega_only(next_char, &phase_ptr->omega); + opt_save = OPTION_DEFAULT; + break; + case 15: /* vm, molar volume */ + if (phase_ptr == NULL) + break; + read_phase_vm(next_char, &(phase_ptr->logk[vm0]), + &phase_ptr->original_deltav_units); + phase_ptr->delta_v[1] = phase_ptr->logk[vm0]; + opt_save = OPTION_DEFAULT; + break; + case OPTION_DEFAULT: +/* + * Get element name and save pointer to character string + */ + phase_ptr = NULL; + ptr = line; + copy_token(token, &ptr, &l); +/* + * Get and parse equation + */ + j = check_line("Phase equation", FALSE, TRUE, TRUE, TRUE); + if (j == EOF || j == KEYWORD) + { + return_value = j; + break; + } + if (j == OPTION) + { + parse_error++; + error_string = sformatf("Expecting equation for phase %s.", token); + error_msg(error_string, CONTINUE); + error_msg("Parsing equation.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + } + if (parse_eq(line, &next_elt, association) == ERROR) + { + parse_error++; + error_msg("Parsing equation.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + } + phase_ptr = phase_store(token); +/* + * Get pointer to each species in the reaction, store new species if necessary + */ + strcpy(token1, trxn.token[0].name); + replace("(g)", "", token1); + replace("(s)", "", token1); + replace("(G)", "", token1); + replace("(S)", "", token1); + phase_ptr->formula = string_hsave(token1); + for (i = 1; i < count_trxn; i++) + { + if ((strstr(trxn.token[i].name, "(s)") == NULL) && + (strstr(trxn.token[i].name, "(g)") == NULL) && + (strstr(trxn.token[i].name, "(S)") == NULL) && + (strstr(trxn.token[i].name, "(G)") == NULL)) + { + strcpy(token1, trxn.token[i].name); + replace("(aq)", "", token1); + replace("(AQ)", "", token1); + replace("H2O(l)", "H2O", token1); + replace("(H2O(L)", "H2O", token1); + trxn.token[i].s = s_store(token1, trxn.token[i].z, FALSE); + } + else + { + trxn.token[i].s = NULL; + } + } +/* + * Save element list + */ + phase_ptr->next_elt = next_elt; +/* + * Malloc space for phase reaction + */ + phase_ptr->rxn = rxn_alloc(count_trxn + 1); +/* + * Copy reaction to reaction for phase, first token (token[0]) is not used + * except to check that coef of phase formula = 1.0 + */ + token_ptr = phase_ptr->rxn->token; + /* token_ptr[0].coef=0; */ + token_ptr[0].coef = trxn.token[0].coef; + token_ptr[0].s = trxn.token[1].s; + for (i = 1; i < count_trxn; i++) + { + token_ptr[i].name = NULL; + token_ptr[i].s = trxn.token[i].s; + token_ptr[i].coef = trxn.token[i].coef; + if (token_ptr[i].s == NULL) + { + token_ptr[i].name = trxn.token[i].name; + } + } + token_ptr[0].name = trxn.token[1].name; + /* + token_ptr[0].name=phase_ptr->name; + token_ptr[0].s=NULL; + */ + token_ptr[i].s = NULL; + token_ptr[i].name = NULL; +/* + * Set type for phase + */ + phase_ptr->type = SOLID; + opt_save = OPTION_DEFAULT; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_pp_assemblage(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads pp_assemblage data + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int j; + int return_value; + int n_user, n_user_end; + char *ptr; + char *description; + std::string token; + int opt, opt_save; + char *next_char; + const char *opt_list[] = { + "force_equality" /* 0 */ + }; + int count_opt_list = 1; + + ptr = line; + /* + * Read pp_assemblage number + */ + read_number_description(ptr, &n_user, &n_user_end, &description); + /* + * Find pp_assemblage or realloc space for pp_assemblage + */ + cxxPPassemblage temp_pp_assemblage; + cxxPPassemblageComp *comp = NULL; + std::map comps; + temp_pp_assemblage.Set_new_def(true); + temp_pp_assemblage.Set_n_user(n_user); + temp_pp_assemblage.Set_n_user_end(n_user_end); + temp_pp_assemblage.Set_description(description); + free_check_null(description); + /* + * Set use data to first read + */ + if (use.Get_pp_assemblage_in() == FALSE) + { + use.Set_pp_assemblage_in(true); + use.Set_n_pp_assemblage_user(n_user); + } + /* + * Read equilibrium phase data + */ + opt_save = OPTION_DEFAULT; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in EQUILIBRIUM_PHASES keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* force_equality */ + if (comp == NULL) + { + error_msg + ("Force_equality defined before equilibrium phase has been defined.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + else + { + comp->Set_force_equality(get_true_false(next_char, TRUE) == TRUE); + } + break; + case OPTION_DEFAULT: + /* + * Make space, set default + */ + if (comp) + { + comps[comp->Get_name()] = *comp; + } + delete comp; + comp = new cxxPPassemblageComp; + /* + * Read name + */ + ptr = line; + copy_token(token, &ptr); + comp->Set_name(token.c_str()); + + if ((j = copy_token(token, &ptr)) == EMPTY) + continue; + /* + * Read saturation index + */ + j = sscanf(token.c_str(), SCANFORMAT, &dummy); + comp->Set_si(dummy); + comp->Set_si_org(dummy); + if (j != 1) + { + error_msg("Expected saturation index.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + continue; + } + /* + * Adding a reaction to the phase boundary + */ + if ((j = copy_token(token, &ptr)) == EMPTY) + continue; + if (j == UPPER || j == LOWER) + { + comp->Set_add_formula(token.c_str()); + j = copy_token(token, &ptr); + } + /* + * Read amount + */ + if (j == EMPTY) + continue; + j = sscanf(token.c_str(), SCANFORMAT, &dummy); + if (dummy < 0) + { + error_string = sformatf( "Moles of mineral < 0, reset to 0."); + dummy = 0; + warning_msg(error_string); + } + comp->Set_moles(dummy); + if (j != 1) + { + error_msg("Expected amount of mineral.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + continue; + } + if ((j = copy_token(token, &ptr)) == EMPTY) + continue; + Utilities::str_tolower(token); + if (strstr(token.c_str(), "d") == token.c_str()) + { + comp->Set_dissolve_only(true); + comp->Set_precipitate_only(false); + } else if (strstr(token.c_str(), "p") == token.c_str()) + { + comp->Set_precipitate_only(true); + comp->Set_dissolve_only(false); + } + else + { + error_msg + ("Unexpected data at end of equilibrium-phase definition.", + CONTINUE); + input_error++; + continue; + } + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + if (comp) + { + comps[comp->Get_name()] = *comp; + delete comp; + comp = NULL; + } + temp_pp_assemblage.Set_pp_assemblage_comps(comps); + Rxn_pp_assemblage_map[n_user] = temp_pp_assemblage; + Rxn_new_pp_assemblage.insert(n_user); + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_reaction(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads reaction data + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ +/* + * Read reaction + */ + int l; + char *ptr; + char *description; + char token[MAX_LENGTH]; + int return_value; + int n_user, n_user_end; + +/* + * Read reaction number + */ + ptr = line; + read_number_description(ptr, &n_user, &n_user_end, &description); + +/* + * Set use data to first read + */ + if (use.Get_reaction_in() == FALSE) + { + use.Set_reaction_in(true); + use.Set_n_reaction_user(n_user); + } +/* + * Defaults + */ + cxxReaction temp_reaction; + temp_reaction.Set_n_user(n_user); + temp_reaction.Set_n_user_end(n_user_end); + temp_reaction.Set_description(description); + free_check_null(description); +/* + * Read reaction data + */ + for (;;) + { +/* + * Read line + */ + return_value = check_line("Reaction data", FALSE, TRUE, TRUE, TRUE); + /* empty, eof, keyword, print */ + if (return_value == EOF || return_value == KEYWORD) + { + break; + } + ptr = line; + copy_token(token, &ptr, &l); + if (isalpha((int) token[0]) || (token[0] == '(') || (token[0] == '[')) + { +/* + * Read reactant information + */ + read_reaction_reactants(&temp_reaction); + } + else + { +/* + * Read steps information + */ + read_reaction_steps(&temp_reaction); + } + } +/* + * Default 1 mol of reaction + */ + if (temp_reaction.Get_steps().size() == 0) + { + std::vector v; + v.push_back(1.0); + temp_reaction.Set_steps(v); + } + if (temp_reaction.Get_equalIncrements()) + { + if (temp_reaction.Get_countSteps() == 0) + { + temp_reaction.Set_countSteps(1); + } + } + Rxn_reaction_map[n_user] = temp_reaction; + // copy if needed + Utilities::Rxn_copies(Rxn_reaction_map, n_user, n_user_end); + + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_reaction_reactants(cxxReaction *reaction_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read reactants, may be a chemical formula or a pure_phase name + * followed by relative reaction coefficient, default 1.0. + * + */ + std::string token, last_token; + LDBLE coef; + char *ptr; +/* + * Read one or more reactants + */ + ptr = line; + while (copy_token(token, &ptr) != EMPTY) + { +/* + * Store reactant name, default coefficient + */ + if (isalpha((int) token[0]) || (token[0] == '(') || (token[0] == '[')) + { + + reaction_ptr->Get_reactantList()[token] = 1.0; + last_token = token; +/* + * Store relative coefficient + */ + } + else + { + int j = sscanf(token.c_str(), SCANFORMAT, &coef); + if (j == 1 && last_token.size() > 0) + { + reaction_ptr->Get_reactantList()[last_token] = coef; + } + else + { + error_msg("Reading relative coefficient of reactant.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_reaction_steps(cxxReaction *reaction_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read amount(s) of irrev reactions in one of three forms: + * + * 6 millimoles in 6 steps or + * + * 1 2 3 4 5 6 millimoles or + * + * 6*1 millimoles + * INCREMENTAL_REACTIONS + */ + + char *ptr; + std::string token, token1; + + ptr = line; +/* + * Read one or more reaction increments + */ + for (;;) + { + if (copy_token(token, &ptr) == EMPTY) + { + return (OK); + } +/* + * Read next step increment + */ +/* begin modif 29 july 2005... */ + if (replace("*", " ", token)) + { + int n; + LDBLE value; + if (sscanf(token.c_str(), "%d" SCANFORMAT, &n, &value) == 2) + { + for (int i = 0; i < n; i++) + { + reaction_ptr->Get_steps().push_back(value); + } + } + else + { + input_error++; + error_msg + ("Format error in multiple, equal REACTION steps.\nCorrect is (for example): 0.2 4*0.1 2*0.5 0.3\n", + CONTINUE); + } + } + else + { + LDBLE step; + int j = sscanf(token.c_str(), SCANFORMAT, &step); + if (j == 1) + { + reaction_ptr->Get_steps().push_back(step); + } + else + { + break; + } + } +/* ...end modif 29 july 2005 */ + } +/* + * Read units + */ + token1 = token; + token1.append("/l"); + std::string t1 = token1; + if (check_units(t1, false, false, NULL, false) == OK) + { + replace("/l", "", t1); + if (strstr(t1.c_str(), "Mol") == NULL) + { + error_string = sformatf( "Units of steps not in moles, %s.", token.c_str()); + error_msg(error_string, CONTINUE); + input_error++; + return (ERROR); + } + else + { + reaction_ptr->Set_units(t1.c_str()); + } + if (copy_token(token, &ptr) == EMPTY) + { + return (OK); + } + } +/* + * Read number of equal increments, store as negative integer + */ + if (reaction_ptr->Get_reaction_steps() != 1) + { + error_msg + ("To define equal increments, only one reaction increment should be defined.", + CONTINUE); + input_error++; + return (ERROR); + } + do + { + int i; + int j = sscanf(token.c_str(), "%d", &i); + if (j == 1 && i > 0) + { + reaction_ptr->Set_countSteps(i); + reaction_ptr->Set_equalIncrements(true); + return (OK); + } + else if (j == 1 && i <= 0) + { + break; + } + } + while (copy_token(token, &ptr) != EMPTY); + + error_msg("Expecting positive number for number of equal " + "increments to add.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + return (ERROR); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_save(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads solution, mix, irreversible reaction, and pure phases to use + * in reaction calculation + */ + int i, l, n, n_user, n_user_end; + char *ptr; + char token[MAX_LENGTH]; +/* + * Read "save" + */ + ptr = line; + copy_token(token, &ptr, &l); +/* + * Read keyword + */ + copy_token(token, &ptr, &l); + check_key(token); +/* + * Read number + */ + for (;;) + { + i = copy_token(token, &ptr, &l); + if (i == DIGIT) + { + replace("-", " ", token); + n = sscanf(token, "%d%d", &n_user, &n_user_end); + if (n == 1) + { + n_user_end = n_user; + } + if (n_user < 0) + { + error_msg("Number must be a positive integer.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + break; + } + else if (i == EMPTY) + { + error_string = sformatf( "No number given, 1 assumed."); + warning_msg(error_string); + n_user = 1; + n_user_end = 1; + break; + } + } + + switch (next_keyword) + { + case Keywords::KEY_SOLUTION: /* Solution */ + save.solution = TRUE; + save.n_solution_user = n_user; + save.n_solution_user_end = n_user_end; + break; + case Keywords::KEY_EQUILIBRIUM_PHASES: /* Pure phases */ + save.pp_assemblage = TRUE; + save.n_pp_assemblage_user = n_user; + save.n_pp_assemblage_user_end = n_user_end; + break; + case Keywords::KEY_EXCHANGE: /* exchange */ + save.exchange = TRUE; + save.n_exchange_user = n_user; + save.n_exchange_user_end = n_user_end; + break; + case Keywords::KEY_SURFACE: /* surface */ + save.surface = TRUE; + save.n_surface_user = n_user; + save.n_surface_user_end = n_user_end; + break; + case Keywords::KEY_GAS_PHASE: /* gas_phase */ + save.gas_phase = TRUE; + save.n_gas_phase_user = n_user; + save.n_gas_phase_user_end = n_user_end; + break; + case Keywords::KEY_SOLID_SOLUTIONS: /* solid_solutions */ + save.ss_assemblage = TRUE; + save.n_ss_assemblage_user = n_user; + save.n_ss_assemblage_user_end = n_user_end; + break; + default: + input_error++; + error_msg + ("Expecting keyword solution, equilibrium_phases, exchange, surface, gas_phase, or solid_solutions.", + CONTINUE); + error_msg(line_save, CONTINUE); + check_line("End of save", FALSE, TRUE, TRUE, TRUE); + /* empty, eof, keyword, print */ + return (ERROR); + } + + check_line("End of save", FALSE, TRUE, TRUE, TRUE); + /* empty, eof, keyword, print */ + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_selected_output(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read data for to output to flat file + */ + int value; + int return_value, opt, opt_save; + char *next_char; + const char *opt_list[] = { + "file", /* 0 */ + "totals", /* 1 */ + "molalities", /* 2 */ + "activities", /* 3 */ + "pure_phases", /* 4 */ + "si", /* 5 */ + "saturation_indices", /* 6 */ + "gases", /* 7 */ + "equilibrium_phases", /* 8 */ + "equilibria", /* 9 */ + "equilibrium", /* 10 */ + "pure", /* 11 */ + "inverse", /* 12 */ + "kinetic_reactants", /* 13 */ + "kinetics", /* 14 */ + "solid_solutions", /* 15 */ + "inverse_modeling", /* 16 */ + "reset", /* 17 */ + "simulation", /* 18 */ + "sim", /* 19 */ + "state", /* 20 */ + "solution", /* 21 */ + "soln", /* 22 */ + "distance", /* 23 */ + "dist", /* 24 */ + "time", /* 25 */ + "step", /* 26 */ + "reaction", /* 27 */ + "rxn", /* 28 */ + "temperature", /* 29 */ + "temp", /* 30 */ + "ph", /* 31 */ + "pe", /* 32 */ + "alkalinity", /* 33 */ + "alk", /* 34 */ + "ionic_strength", /* 35 */ + "mu", /* 36 */ + "water", /* 37 */ + "high_precision", /* 38 */ + "user_punch", /* 39 */ + "mol", /* 40 */ + "kin", /* 41 */ + "charge_balance", /* 42 */ + "percent_error", /* 43 */ + "selected_out", /* 44 */ + "selected_output", /* 45 */ + "isotopes", /* 46 */ + "calculate_values", /* 47 */ + "equilibrium_phase", /* 48 */ + "active" /* 49 */ + }; + int count_opt_list = 50; + + int i, l; + char file_name[MAX_LENGTH], token[MAX_LENGTH]; + + char *ptr; + ptr = line; + int n_user, n_user_end; + char *description; + read_number_description(ptr, &n_user, &n_user_end, &description); + + SelectedOutput temp_selected_output; + temp_selected_output.Set_new_def(false); + temp_selected_output.Set_file_name(n_user); + temp_selected_output.Set_n_user(n_user); + temp_selected_output.Set_n_user_end(n_user_end); + temp_selected_output.Set_description(description); + free_check_null(description); + + // find if it exists + std::map< int, SelectedOutput >::iterator so = SelectedOutput_map.find(n_user); + if (n_user == 1 && so != SelectedOutput_map.end()) + { + // n_user = 1, old definition, keep old definition + SelectedOutput & so_ref = so->second; + temp_selected_output.Set_active ( so_ref.Get_active() ); + temp_selected_output.Set_inverse ( so_ref.Get_inverse() ); + temp_selected_output.Set_sim ( so_ref.Get_sim() ); + temp_selected_output.Set_state ( so_ref.Get_state() ); + temp_selected_output.Set_soln ( so_ref.Get_soln() ); + temp_selected_output.Set_dist ( so_ref.Get_dist() ); + temp_selected_output.Set_time ( so_ref.Get_time() ); + temp_selected_output.Set_step ( so_ref.Get_step() ); + temp_selected_output.Set_rxn ( so_ref.Get_rxn() ); + temp_selected_output.Set_temp ( so_ref.Get_temp() ); + temp_selected_output.Set_ph ( so_ref.Get_ph() ); + temp_selected_output.Set_pe ( so_ref.Get_pe() ); + temp_selected_output.Set_alk ( so_ref.Get_alk() ); + temp_selected_output.Set_mu ( so_ref.Get_mu() ); + temp_selected_output.Set_water ( so_ref.Get_water() ); + temp_selected_output.Set_high_precision ( so_ref.Get_high_precision() ); + temp_selected_output.Set_user_punch ( so_ref.Get_user_punch() ); + temp_selected_output.Set_charge_balance ( so_ref.Get_charge_balance() ); + temp_selected_output.Set_percent_error ( so_ref.Get_percent_error() ); + temp_selected_output.Set_have_punch_name ( so_ref.Get_have_punch_name() ); + temp_selected_output.Set_file_name ( so_ref.Get_file_name() ); +#if PHREEQCI_GUI + assert(false); +#endif + } + else if (n_user == 1 && so == SelectedOutput_map.end()) + { + // n_user = 1, new definition, do nothing use; constructor default + temp_selected_output.Set_new_def(true); + } + else + { + // n_user != 1 then reset false + + temp_selected_output.Reset(false); + if (so == SelectedOutput_map.end()) + { + temp_selected_output.Set_new_def(true); + } + } + CParser parser(this->phrq_io); + +/* + * Read eqn from file and call parser + */ + opt_save = OPTION_ERROR; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = opt; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_DEFAULT: + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SELECTED_OUTPUT keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* file name */ + temp_selected_output.Set_new_def(true); + if (string_trim(next_char) != EMPTY) + { + strcpy(file_name, next_char); + temp_selected_output.Set_file_name(file_name); + temp_selected_output.Set_have_punch_name(true); + } + opt_save = OPTION_ERROR; + break; + case 1: /* totals */ + temp_selected_output.Set_new_def(true); + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + if (i != UPPER && token[0] != '[') + { + error_string = sformatf( "Expected element name to" + " begin with upper case letter."); + warning_msg(error_string); + } + else + { + std::pair< std::string, void *> t_pair(token, ((void *)0)); + temp_selected_output.Get_totals().push_back(t_pair); + } + } + break; + case 2: /* molalities */ + case 40: /* mol */ + temp_selected_output.Set_new_def(true); + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + if (i != UPPER && token[0] != '(' && (token[0] != '[')) + { + error_string = sformatf( "Expected species name to" + " begin with upper case letter."); + warning_msg(error_string); + } + else + { + std::pair< std::string, void *> t_pair(token, ((void *)0)); + temp_selected_output.Get_molalities().push_back(t_pair); + } + } + break; + case 3: /* activities */ + temp_selected_output.Set_new_def(true); + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + if (i != UPPER && token[0] != '(' && (token[0] != '[')) + { + error_string = sformatf( "Expected species name to" + " begin with upper case letter."); + warning_msg(error_string); + } + else + { + std::pair< std::string, void *> t_pair(token, ((void *)0)); + temp_selected_output.Get_activities().push_back(t_pair); + } + } + break; + case 4: /* pure_phases */ + case 8: /* equilibrium_phases */ + case 9: /* equilibria */ + case 10: /* equilibrium */ + case 11: /* pure */ + case 48: /* equilibrium_phase */ + temp_selected_output.Set_new_def(true); + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + std::pair< std::string, void *> t_pair(token, ((void *)0)); + temp_selected_output.Get_pure_phases().push_back(t_pair); + } + break; + case 5: /* si */ + case 6: /* saturation_index */ + temp_selected_output.Set_new_def(true); + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + std::pair< std::string, void *> t_pair(token, ((void *)0)); + temp_selected_output.Get_si().push_back(t_pair); + } + break; + case 7: /* gases */ + temp_selected_output.Set_new_def(true); + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + std::pair< std::string, void *> t_pair(token, ((void *)0)); + temp_selected_output.Get_gases().push_back(t_pair); + } + break; + case 12: /* inverse */ + case 16: /* inverse_modeling */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_inverse(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 13: /* kinetic_reactants */ + case 14: /* kinetics */ + case 41: /* kin */ + temp_selected_output.Set_new_def(true); + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + std::pair< std::string, void *> t_pair(token, ((void *)0)); + temp_selected_output.Get_kinetics().push_back(t_pair); + } + break; + case 15: /* solid_solutions */ + temp_selected_output.Set_new_def(true); + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + std::pair< std::string, void *> t_pair(token, ((void *)0)); + temp_selected_output.Get_s_s().push_back(t_pair); + } + break; + case 46: /* isotopes */ + temp_selected_output.Set_new_def(true); + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + if (i != UPPER && token[0] != '[') + { + error_string = sformatf( "Expected element name to" + " begin with upper case letter."); + warning_msg(error_string); + } + else + { + std::pair< std::string, void *> t_pair(token, ((void *)0)); + temp_selected_output.Get_isotopes().push_back(t_pair); + } + } + break; + case 47: /* calculate_values */ + temp_selected_output.Set_new_def(true); + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + std::pair< std::string, void *> t_pair(token, ((void *)0)); + temp_selected_output.Get_calculate_values().push_back(t_pair); + } + break; + case 17: /* reset */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + /* matches print order */ + temp_selected_output.Reset(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 18: /* simulation */ + case 19: /* sim */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_sim(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 20: /* state */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_state(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 21: /* solution */ + case 22: /* soln */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_soln(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 23: /* distance */ + case 24: /* dist */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_dist(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 25: /* time */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_time(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 26: /* step */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_step(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 27: /* reaction */ + case 28: /* rxn */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_rxn(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 29: /* temperature */ + case 30: /* temp */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_temp(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 31: /* ph */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_ph(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 32: /* pe */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_pe(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 33: /* alkalinity */ + case 34: /* alk */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_alk(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 35: /* ionic strength */ + case 36: /* mu */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_mu(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 37: /* water */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_water(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 38: /* high_precision */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_high_precision(value!=FALSE); + //if (n_user == 1) + //{ + // high_precision = (value != FALSE); + //} + if (value == TRUE) + { + convergence_tolerance = 1e-12; + } + opt_save = OPTION_ERROR; + break; + case 39: /* user_punch */ + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_user_punch(value!=FALSE); + if (so != SelectedOutput_map.end()) + { + so->second.Set_user_punch(value!=FALSE); + } opt_save = OPTION_ERROR; + break; + case 42: /* charge_balance */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_charge_balance(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 43: /* percent_error */ + temp_selected_output.Set_new_def(true); + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_percent_error(value!=FALSE); + opt_save = OPTION_ERROR; + break; + case 44: /* selected_out */ + case 45: /* selected_output */ + //warning_msg("Use PRINT; -selected_output, not SELECTED_OUTPUT; -selected_output"); + //value = get_true_false(next_char, TRUE); + //temp_selected_output.Set_active(value!=FALSE); + //opt_save = OPTION_ERROR; + //break; + case 49: /* active */ + value = get_true_false(next_char, TRUE); + temp_selected_output.Set_active(value!=FALSE); + if (so != SelectedOutput_map.end()) + { + so->second.Set_active(value!=FALSE); + } + opt_save = OPTION_ERROR; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + + if (temp_selected_output.Get_new_def() || so == SelectedOutput_map.end()) + { + // delete if exists + if (so != SelectedOutput_map.end()) + { + SelectedOutput_map.erase(so); + } + + // store new selected output + SelectedOutput_map[n_user] = temp_selected_output; + + if (punch_open(SelectedOutput_map[n_user].Get_file_name().c_str(), n_user)) + { + if (phrq_io) + { + SelectedOutput_map[n_user].Set_punch_ostream(phrq_io->Get_punch_ostream()); + phrq_io->Set_punch_ostream(NULL); + } + } + else + { + error_string = sformatf( "Can`t open file, %s.", SelectedOutput_map[n_user].Get_file_name().c_str()); + input_error++; + error_msg(error_string, CONTINUE); + } + } + + //if (!have_punch_name) + //{ + // punch_close(); + // if (!punch_open("selected.out")) + // { + // error_string = sformatf( "Can`t open file, %s.", "selected.out"); + // input_error++; + // error_msg(error_string, CONTINUE); + // } + //} + + return (return_value); +} +#ifdef SKIP +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_selected_output(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read data for to output to flat file + */ + int value; + int return_value, opt, opt_save; + char *next_char; + const char *opt_list[] = { + "file", /* 0 */ + "totals", /* 1 */ + "molalities", /* 2 */ + "activities", /* 3 */ + "pure_phases", /* 4 */ + "si", /* 5 */ + "saturation_indices", /* 6 */ + "gases", /* 7 */ + "equilibrium_phases", /* 8 */ + "equilibria", /* 9 */ + "equilibrium", /* 10 */ + "pure", /* 11 */ + "inverse", /* 12 */ + "kinetic_reactants", /* 13 */ + "kinetics", /* 14 */ + "solid_solutions", /* 15 */ + "inverse_modeling", /* 16 */ + "reset", /* 17 */ + "simulation", /* 18 */ + "sim", /* 19 */ + "state", /* 20 */ + "solution", /* 21 */ + "soln", /* 22 */ + "distance", /* 23 */ + "dist", /* 24 */ + "time", /* 25 */ + "step", /* 26 */ + "reaction", /* 27 */ + "rxn", /* 28 */ + "temperature", /* 29 */ + "temp", /* 30 */ + "ph", /* 31 */ + "pe", /* 32 */ + "alkalinity", /* 33 */ + "alk", /* 34 */ + "ionic_strength", /* 35 */ + "mu", /* 36 */ + "water", /* 37 */ + "high_precision", /* 38 */ + "user_punch", /* 39 */ + "mol", /* 40 */ + "kin", /* 41 */ + "charge_balance", /* 42 */ + "percent_error", /* 43 */ + "selected_out", /* 44 */ + "selected_output", /* 45 */ + "isotopes", /* 46 */ + "calculate_values", /* 47 */ + "equilibrium_phase" /* 48 */ + }; + int count_opt_list = 49; + + int i, l; + char file_name[MAX_LENGTH], token[MAX_LENGTH]; + + punch.in = TRUE; + punch.new_def = TRUE; + punch.count_totals = 0; + punch.count_molalities = 0; + punch.count_activities = 0; + punch.count_pure_phases = 0; + punch.count_si = 0; + punch.count_gases = 0; + punch.count_kinetics = 0; + punch.count_s_s = 0; +/* + * Read eqn from file and call parser + */ + opt_save = OPTION_ERROR; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = opt; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_DEFAULT: + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SELECTED_OUTPUT keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* file name */ + /* copy_token(file_name, &next_char, &l); */ + if (string_trim(next_char) != EMPTY) + { + strcpy(file_name, next_char); + have_punch_name = TRUE; + punch_close(); + if (!punch_open(file_name)) + { + error_string = sformatf( "Can`t open file, %s.", file_name); + input_error++; + error_msg(error_string, CONTINUE); + } + selected_output_file_name = (char*)free_check_null(selected_output_file_name); + selected_output_file_name = string_duplicate(file_name); + } + opt_save = OPTION_ERROR; + break; + case 1: /* totals */ + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + if (i != UPPER && token[0] != '[') + { + error_string = sformatf( "Expected element name to" + " begin with upper case letter."); + warning_msg(error_string); + } + else + { + punch.count_totals++; + punch.totals = + (struct name_master *) PHRQ_realloc(punch.totals, + (size_t) punch. + count_totals * + sizeof(struct + name_master)); + if (punch.totals == NULL) + malloc_error(); + punch.totals[punch.count_totals - 1].name = + string_hsave(token); + } + } + break; + case 2: /* molalities */ + case 40: /* mol */ + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + if (i != UPPER && token[0] != '(' && (token[0] != '[')) + { + error_string = sformatf( "Expected species name to" + " begin with upper case letter."); + warning_msg(error_string); + } + else + { + punch.count_molalities++; + punch.molalities = + (struct name_species *) PHRQ_realloc(punch. + molalities, + (size_t) punch. + count_molalities + * + sizeof(struct + name_species)); + if (punch.molalities == NULL) + malloc_error(); + punch.molalities[punch.count_molalities - 1].name = + string_hsave(token); + } + } + break; + case 3: /* activities */ + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + if (i != UPPER && token[0] != '(' && (token[0] != '[')) + { + error_string = sformatf( "Expected species name to" + " begin with upper case letter."); + warning_msg(error_string); + } + else + { + punch.count_activities++; + punch.activities = + (struct name_species *) PHRQ_realloc(punch. + activities, + (size_t) punch. + count_activities + * + sizeof(struct + name_species)); + if (punch.activities == NULL) + malloc_error(); + punch.activities[punch.count_activities - 1].name = + string_hsave(token); + } + } + break; + case 4: /* pure_phases */ + case 8: /* equilibrium_phases */ + case 9: /* equilibria */ + case 10: /* equilibrium */ + case 11: /* pure */ + case 48: /* equilibrium_phase */ + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + punch.count_pure_phases++; + punch.pure_phases = + (struct name_phase *) PHRQ_realloc(punch.pure_phases, + (size_t) punch. + count_pure_phases * + sizeof(struct + name_phase)); + if (punch.pure_phases == NULL) + malloc_error(); + punch.pure_phases[punch.count_pure_phases - 1].name = + string_hsave(token); + } + break; + case 5: /* si */ + case 6: /* saturation_index */ + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + punch.count_si++; + punch.si = + (struct name_phase *) PHRQ_realloc(punch.si, + (size_t) punch. + count_si * + sizeof(struct + name_phase)); + if (punch.si == NULL) + malloc_error(); + punch.si[punch.count_si - 1].name = string_hsave(token); + } + break; + case 7: /* gases */ + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + punch.count_gases++; + punch.gases = + (struct name_phase *) PHRQ_realloc(punch.gases, + (size_t) punch. + count_gases * + sizeof(struct + name_phase)); + if (punch.gases == NULL) + malloc_error(); + punch.gases[punch.count_gases - 1].name = string_hsave(token); + } + break; + case 12: /* inverse */ + case 16: /* inverse_modeling */ + punch.inverse = get_true_false(next_char, TRUE); + opt_save = OPTION_ERROR; + break; + case 13: /* kinetic_reactants */ + case 14: /* kinetics */ + case 41: /* kin */ + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + punch.count_kinetics++; + punch.kinetics = + (struct name_phase *) PHRQ_realloc(punch.kinetics, + (size_t) punch. + count_kinetics * + sizeof(struct + name_phase)); + if (punch.kinetics == NULL) + malloc_error(); + punch.kinetics[punch.count_kinetics - 1].name = + string_hsave(token); + } + break; + case 15: /* solid_solutions */ + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + punch.count_s_s++; + punch.s_s = + (struct name_phase *) PHRQ_realloc(punch.s_s, + (size_t) punch. + count_s_s * + sizeof(struct + name_phase)); + if (punch.s_s == NULL) + malloc_error(); + punch.s_s[punch.count_s_s - 1].name = string_hsave(token); + } + break; + case 46: /* isotopes */ + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + if (i != UPPER && token[0] != '[') + { + error_string = sformatf( "Expected element name to" + " begin with upper case letter."); + warning_msg(error_string); + } + else + { + punch.count_isotopes++; + punch.isotopes = + (struct name_master *) PHRQ_realloc(punch.isotopes, + (size_t) punch. + count_isotopes * + sizeof(struct + name_master)); + if (punch.isotopes == NULL) + malloc_error(); + punch.isotopes[punch.count_isotopes - 1].name = + string_hsave(token); + } + } + break; + case 47: /* calculate_values */ + while ((i = copy_token(token, &next_char, &l)) != EMPTY) + { + punch.count_calculate_values++; + punch.calculate_values = + (struct name_master *) PHRQ_realloc(punch. + calculate_values, + (size_t) punch. + count_calculate_values + * + sizeof(struct + name_master)); + if (punch.calculate_values == NULL) + malloc_error(); + punch.calculate_values[punch.count_calculate_values - + 1].name = string_hsave(token); + } + break; + case 17: /* reset */ + value = get_true_false(next_char, TRUE); + /* matches print order */ + punch.sim = value; + punch.state = value; + punch.soln = value; + punch.dist = value; + punch.time = value; + punch.step = value; + punch.ph = value; + punch.pe = value; + punch.rxn = value; + punch.temp = value; + punch.alk = value; + punch.mu = value; + punch.water = value; + punch.charge_balance = value; + punch.percent_error = value; + opt_save = OPTION_ERROR; + break; + case 18: /* simulation */ + case 19: /* sim */ + punch.sim = get_true_false(next_char, TRUE); + opt_save = OPTION_ERROR; + break; + case 20: /* state */ + punch.state = get_true_false(next_char, TRUE); + opt_save = OPTION_ERROR; + break; + case 21: /* solution */ + case 22: /* soln */ + punch.soln = get_true_false(next_char, TRUE); + opt_save = OPTION_ERROR; + break; + case 23: /* distance */ + case 24: /* dist */ + punch.dist = get_true_false(next_char, TRUE); + opt_save = OPTION_ERROR; + break; + case 25: /* time */ + punch.time = get_true_false(next_char, TRUE); + opt_save = OPTION_ERROR; + break; + case 26: /* step */ + punch.step = get_true_false(next_char, TRUE); + opt_save = OPTION_ERROR; + break; + case 27: /* reaction */ + case 28: /* rxn */ + punch.rxn = get_true_false(next_char, TRUE); + opt_save = OPTION_ERROR; + break; + case 29: /* temperature */ + case 30: /* temp */ + punch.temp = get_true_false(next_char, TRUE); + opt_save = OPTION_ERROR; + break; + case 31: /* ph */ + punch.ph = get_true_false(next_char, TRUE); + opt_save = OPTION_ERROR; + break; + case 32: /* pe */ + punch.pe = get_true_false(next_char, TRUE); + opt_save = OPTION_ERROR; + break; + case 33: /* alkalinity */ + case 34: /* alk */ + punch.alk = get_true_false(next_char, TRUE); + opt_save = OPTION_ERROR; + break; + case 35: /* ionic strength */ + case 36: /* mu */ + punch.mu = get_true_false(next_char, TRUE); + opt_save = OPTION_ERROR; + break; + case 37: /* water */ + punch.water = get_true_false(next_char, TRUE); + opt_save = OPTION_ERROR; + break; + case 38: /* high_precision */ + punch.high_precision = get_true_false(next_char, TRUE); + if (punch.high_precision == TRUE /*&& convergence_tolerance > 1e-12*/) + { + convergence_tolerance = 1e-12; + } + opt_save = OPTION_ERROR; + break; + case 39: /* user_punch */ + punch.user_punch = get_true_false(next_char, TRUE); + opt_save = OPTION_ERROR; + break; + case 42: /* charge_balance */ + punch.charge_balance = get_true_false(next_char, TRUE); + opt_save = OPTION_ERROR; + break; + case 43: /* percent_error */ + punch.percent_error = get_true_false(next_char, TRUE); + opt_save = OPTION_ERROR; + break; + case 44: /* selected_out */ + case 45: /* selected_output */ + warning_msg("Use PRINT; -selected_output, not SELECTED_OUTPUT; -selected_output"); + pr.punch = get_true_false(next_char, TRUE); + opt_save = OPTION_ERROR; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + if (!have_punch_name) + { + punch_close(); + if (!punch_open("selected.out")) + { + error_string = sformatf( "Can`t open file, %s.", "selected.out"); + input_error++; + error_msg(error_string, CONTINUE); + } + } + + return (return_value); +} +#endif +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_solution(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads solution data + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int n_user, n_user_end; + char *description; + + int return_value, opt; + char *next_char; + const char *opt_list[] = { + "temp", /* 0 */ + "temperature", /* 1 */ + "dens", /* 2 */ + "density", /* 3 */ + "units", /* 4 */ + "redox", /* 5 */ + "ph", /* 6 */ + "pe", /* 7 */ + "unit", /* 8 */ + "isotope", /* 9 */ + "water", /* 10 */ + "press", /* 11 */ + "pressure", /* 12 */ + "potential" /* 13 */ + }; + int count_opt_list = 14; +/* + * Read solution number and description + */ + char *ptr; + ptr = line; + read_number_description(ptr, &n_user, &n_user_end, &description); + + cxxSolution temp_solution; + temp_solution.Set_new_def(true); + temp_solution.Create_initial_data(); + cxxISolution *isoln_ptr = temp_solution.Get_initial_data(); + CParser parser(this->phrq_io); + + temp_solution.Set_n_user(n_user); + temp_solution.Set_n_user_end(n_user_end); + temp_solution.Set_description(description); + free_check_null(description); + + if (!use.Get_solution_in()) + { + use.Set_solution_in(true); + use.Set_n_solution_user(n_user); + } + +/* + * Read concentration data + */ + std::string token; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + ptr = next_char; + if (copy_token(token, &ptr) == CParser::TT_DIGIT) + { + opt = 9; + } + } + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SOLUTION keyword.", PHRQ_io::OT_CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* temperature */ + case 1: + if (sscanf(next_char, SCANFORMAT, &dummy) == 1) + { + temp_solution.Set_tc(dummy); + } + break; + case 2: /* density */ + case 3: + { + copy_token(token, &next_char); + if (sscanf(token.c_str(), SCANFORMAT, &dummy) != 1) + { + error_msg("Expecting numeric value for density.", PHRQ_io::OT_CONTINUE); + error_msg(line_save, PHRQ_io::OT_CONTINUE); + input_error++; + } + else + { + temp_solution.Set_density(dummy); + } + int j = copy_token(token, &next_char); + if (j != EMPTY) + { + if (token[0] != 'c' && token[0] != 'C') + { + error_msg("Only option following density is c[alculate].", PHRQ_io::OT_CONTINUE); + error_msg(line_save, PHRQ_io::OT_CONTINUE); + input_error++; + } + else + { + isoln_ptr->Set_calc_density(true); + } + } + } + break; + case 4: /* units */ + case 8: /* unit */ + if (copy_token(token, &next_char) == CParser::TT_EMPTY) + break; + { + if (check_units(token, false, false, "mMol/kgw", false) == CParser::PARSER_OK) + { + isoln_ptr->Set_units(token); + } + else + { + input_error++; + } + } + break; + case 5: /* redox */ + if (copy_token(token, &next_char) == CParser::TT_EMPTY) + break; + if (parser.parse_couple(token) == CParser::PARSER_OK) + { + const char * str = string_hsave(token.c_str()); + //isoln_ptr->Set_default_pe(token); + isoln_ptr->Set_default_pe(str); + cxxChemRxn temp_chem_reaction; + isoln_ptr->Get_pe_reactions()[token] = temp_chem_reaction; + } + else + { + input_error++; + } + break; + case 6: /* ph */ + { + cxxISolutionComp temp_comp(this->phrq_io); + if (temp_comp.read(line, &temp_solution) == CParser::PARSER_ERROR) + { + input_error++; + break; + } + + temp_solution.Set_ph(temp_comp.Get_input_conc()); + + if (temp_comp.Get_equation_name().size() == 0) + { + break; + + } + temp_comp.Set_description("H(1)"); + isoln_ptr->Get_comps()[temp_comp.Get_description()] = temp_comp; + } + break; + case 7: /* pe */ + { + cxxISolutionComp temp_comp(this->phrq_io); + if (temp_comp.read(line, &temp_solution) == CParser::PARSER_ERROR) + { + input_error++; + break; + } + temp_solution.Set_pe(temp_comp.Get_input_conc()); + if (temp_comp.Get_equation_name().size() == 0) + { + break; + } + temp_comp.Set_description("E"); + isoln_ptr->Get_comps()[temp_comp.Get_description()] = temp_comp; + } + break; + case 9: /* isotope */ + { + cxxSolutionIsotope temp_isotope; + if (copy_token(token, &next_char) != CParser::TT_DIGIT) + { + input_error++; + error_string = sformatf( "Expected isotope name to" + " begin with an isotopic number."); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "In read_solution\n"); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "token: ", token.c_str()); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "next_char: ", next_char); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "line_save: ", line_save); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + continue; + } + temp_isotope.Set_isotope_name(token.c_str()); + /* read and save element name */ + { + char *temp_iso_name = string_duplicate(token.c_str()); + char *ptr1 = temp_iso_name; + get_num(&ptr1, &dummy); + temp_isotope.Set_isotope_number(dummy); + if (ptr1[0] == '\0' || isupper((int) ptr1[0]) == FALSE) + { + error_msg("Expecting element name.", PHRQ_io::OT_CONTINUE); + error_msg(line_save, PHRQ_io::OT_CONTINUE); + input_error++; + temp_iso_name = (char*)free_check_null(temp_iso_name); + return (CParser::PARSER_ERROR); + } + temp_isotope.Set_elt_name(ptr1); + temp_iso_name = (char*)free_check_null(temp_iso_name); + } + /* read and store isotope ratio */ + if (copy_token(token, &next_char) != CParser::TT_DIGIT) + { + input_error++; + error_string = sformatf( + "Expected numeric value for isotope ratio."); + error_msg(error_string, CONTINUE); + continue; + } + sscanf(token.c_str(), SCANFORMAT, &dummy); + temp_isotope.Set_ratio(dummy); + temp_isotope.Set_ratio_uncertainty(NAN); + + /* read and store isotope ratio uncertainty */ + int j; + if ((j = copy_token(token, &next_char)) != CParser::TT_EMPTY) + { + if (j != DIGIT) + { + input_error++; + error_string = sformatf( + "Expected numeric value for uncertainty in isotope ratio."); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + continue; + } + sscanf(token.c_str(), SCANFORMAT, &dummy); + temp_isotope.Set_ratio_uncertainty(dummy); + temp_isotope.Set_ratio_uncertainty_defined(true); + } + temp_solution.Get_isotopes()[temp_isotope.Get_isotope_name()] = temp_isotope; + } + break; + case 10: /* water */ + { + int j = copy_token(token, &next_char); + if (j == EMPTY) + { + temp_solution.Set_mass_water(1.0); + } + else if (j != DIGIT) + { + input_error++; + error_string = sformatf( + "Expected numeric value for mass of water in solution."); + error_msg(error_string, CONTINUE); + } + else + { + sscanf(token.c_str(), SCANFORMAT, &dummy); + temp_solution.Set_mass_water(dummy); + } + } + break; + case 11: /* pressure */ + case 12: + { + if (sscanf(next_char, SCANFORMAT, &dummy) != 1) + { + temp_solution.Set_patm(1); + } + else + { + temp_solution.Set_patm(dummy); + } + } + break; + case 13: /* potential, Volt */ + { + if (sscanf(next_char, SCANFORMAT, &dummy) != 1) + { + temp_solution.Set_potV(0); + } + else + { + temp_solution.Set_potV(dummy); + } + } + break; + case OPTION_DEFAULT: +/* + * Read concentration + */ + { + cxxISolutionComp temp_comp(this->phrq_io); + if (temp_comp.read(line, &temp_solution) == CParser::PARSER_ERROR) + { + input_error++; + break; + } + isoln_ptr->Get_comps()[temp_comp.Get_description()] = temp_comp; + if (temp_comp.Get_pe_reaction().size() > 0) + { + cxxChemRxn temp_chem_reaction; + isoln_ptr->Get_pe_reactions()[temp_comp.Get_pe_reaction()] = temp_chem_reaction; + } + } + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } +/* + * fix up default units and default pe + */ + std::map < std::string, cxxISolutionComp >::iterator it; + for (it = isoln_ptr->Get_comps().begin(); it != isoln_ptr->Get_comps().end(); it++) + { + token = it->first; + Utilities::str_tolower(token); + if (it->second.Get_units().size() == 0) + { + it->second.Set_units(isoln_ptr->Get_units().c_str()); + } + else + { + bool alk = false; + if (strstr(token.c_str(), "alk") == token.c_str()) + alk = true; + std::string token1 = it->second.Get_units(); + if (check_units(token1, alk, true, isoln_ptr->Get_units().c_str(), true) == CParser::PARSER_ERROR) + { + input_error++; + } + else + { + it->second.Set_units(token1.c_str()); + } + } + if (it->second.Get_pe_reaction().size() == 0) + { + it->second.Set_pe_reaction(isoln_ptr->Get_default_pe()); + } + } + Rxn_solution_map[n_user] = temp_solution; + Rxn_new_solution.insert(n_user); + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_species(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read data for aqueous species, parse equations + */ + int i; + int association; + struct species *s_ptr; + struct elt_list *next_elt; + char *ptr, token[MAX_LENGTH]; + //bool vm_read = false; + int return_value, opt, opt_save; + char *next_char; + const char *opt_list[] = { + "no_check", /* 0 */ + "check", /* 1 */ + "gamma", /* 2 */ + "mb", /* 3 */ + "mass_balance", /* 4 */ + "log_k", /* 5 */ + "logk", /* 6 */ + "delta_h", /* 7 */ + "deltah", /* 8 */ + "analytical_expression", /* 9 */ + "a_e", /* 10 */ + "ae", /* 11 */ + "mole_balance", /* 12 */ + "llnl_gamma", /* 13 */ + "co2_llnl_gamma", /* 14 */ + "activity_water", /* 15 */ + "add_logk", /* 16 */ + "add_log_k", /* 17 */ + "add_constant", /* 18 */ + "dw", /* 19 */ + "erm_ddl", /* 20 */ +/* VP: Density Start */ + "millero", /* 21 */ +/* VP: Density End */ + "vm", /* 22, parms for molar volume, a1..a4 and w_ref, I terms */ + "viscosity" /* 23, b and d parms for viscosity, (b1 + b2 * exp(-b3 * tc)) * c + (d1 * exp(-d2 * tc)) * c ^ d3 */ + }; + int count_opt_list = 24; + association = TRUE; + s_ptr = NULL; +/* + * Read eqn from file and call parser + */ + opt_save = OPTION_DEFAULT; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + //vm_read = false; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SOLUTION_SPECIES keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* no_check */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->check_equation = FALSE; + break; + case 1: /* check */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->check_equation = TRUE; + break; + case 2: /* gamma data */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->gflag = 2; /* Wateq D-H */ + i = sscanf(next_char, SCANFORMAT SCANFORMAT, &s_ptr->dha, + &s_ptr->dhb); + if (i < 2) + { + error_string = sformatf( "Expecting 2 activity-" + "coefficient parameters, a and b."); + warning_msg(error_string); + } + opt_save = OPTION_DEFAULT; + break; + case 3: /* mb */ + case 4: /* mass_balance */ + case 12: /* mole_balance */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + count_elts = 0; + paren_count = 0; + copy_token(token, &next_char, &i); + s_ptr->mole_balance = string_hsave(token); + ptr = token; + s_ptr->next_secondary = + (struct elt_list *) free_check_null(s_ptr->next_secondary); + get_secondary_in_species(&ptr, 1.0); + s_ptr->next_secondary = elt_list_save(); +/* debug + for (i = 0; i < count_elts; i++) { + output_msg(sformatf("%s\t%f\n", elt_list[i].elt->name, + elt_list[i].coef)); + } + */ + opt_save = OPTION_DEFAULT; + break; + case 5: /* log_k */ + case 6: /* logk */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_log_k_only(next_char, &s_ptr->logk[0]); + opt_save = OPTION_DEFAULT; + break; + case 7: /* delta_h */ + case 8: /* deltah */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_delta_h_only(next_char, &s_ptr->logk[1], + &s_ptr->original_units); + opt_save = OPTION_DEFAULT; + break; + case 9: /* analytical_expression */ + case 10: /* a_e */ + case 11: /* ae */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_analytical_expression_only(next_char, &(s_ptr->logk[T_A1])); + opt_save = OPTION_DEFAULT; + break; + case 13: /* llnl_gamma */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->gflag = 7; /* llnl D-H */ + i = sscanf(next_char, SCANFORMAT, &s_ptr->dha); + if (i < 1) + { + error_string = sformatf( + "Expecting activity-coefficient parameter, a."); + warning_msg(error_string); + } + opt_save = OPTION_DEFAULT; + break; + case 14: /* co2_llnl_gamma */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->gflag = 8; /* llnl CO2 D-H */ + opt_save = OPTION_DEFAULT; + break; + case 15: /* activity water */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->gflag = 9; /* activity_water/55.5 for HDO, D2O, H2[O18], etc */ + opt_save = OPTION_DEFAULT; + break; + case 16: /* add_logk */ + case 17: /* add_log_k */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + if (s_ptr->count_add_logk == 0) + { + s_ptr->add_logk = + (struct name_coef *) + PHRQ_malloc(sizeof(struct name_coef)); + if (s_ptr->add_logk == NULL) + malloc_error(); + } + else + { + s_ptr->add_logk = + (struct name_coef *) PHRQ_realloc(s_ptr->add_logk, + (size_t) ((s_ptr-> + count_add_logk + + + 1) * + sizeof + (struct + name_coef))); + if (s_ptr->add_logk == NULL) + malloc_error(); + } + /* read name */ + if (copy_token(token, &next_char, &i) == EMPTY) + { + input_error++; + error_string = sformatf( + "Expected the name of a NAMED_EXPRESSION."); + error_msg(error_string, CONTINUE); + break; + } + s_ptr->add_logk[s_ptr->count_add_logk].name = string_hsave(token); + /* read coef */ + i = sscanf(next_char, SCANFORMAT, + &s_ptr->add_logk[s_ptr->count_add_logk].coef); + if (i <= 0) + { + s_ptr->add_logk[s_ptr->count_add_logk].coef = 1; + } + s_ptr->count_add_logk++; + opt_save = OPTION_DEFAULT; + break; + case 18: /* add_constant */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + if (s_ptr->count_add_logk == 0) + { + s_ptr->add_logk = + (struct name_coef *) + PHRQ_malloc(sizeof(struct name_coef)); + if (s_ptr->add_logk == NULL) + malloc_error(); + } + else + { + s_ptr->add_logk = + (struct name_coef *) PHRQ_realloc(s_ptr->add_logk, + (size_t) ((s_ptr-> + count_add_logk + + + 1) * + sizeof + (struct + name_coef))); + if (s_ptr->add_logk == NULL) + malloc_error(); + } + i = sscanf(next_char, SCANFORMAT, + &s_ptr->add_logk[s_ptr->count_add_logk].coef); + if (i <= 0) + { + input_error++; + error_string = sformatf( + "Expected the constant to add for log_K definition."); + error_msg(error_string, CONTINUE); + break; + } + /* set name */ + s_ptr->add_logk[s_ptr->count_add_logk].name = + string_hsave("XconstantX"); + /* read coef */ + s_ptr->count_add_logk++; + opt_save = OPTION_DEFAULT; + break; + case 19: /* tracer diffusion coefficient */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->dw_t = 0; s_ptr->dw_a = 0; s_ptr->dw_a2 = 0; s_ptr->dw_a_visc = 0; + i = sscanf(next_char, SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT SCANFORMAT, &s_ptr->dw, &s_ptr->dw_t, + &s_ptr->dw_a, &s_ptr->dw_a2, &s_ptr->dw_a_visc); + s_ptr->dw_corr = s_ptr->dw; + opt_save = OPTION_DEFAULT; + break; + case 20: /* enrichment factor in the DDL */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + i = sscanf(next_char, SCANFORMAT, &s_ptr->erm_ddl); + if (s_ptr->erm_ddl < 0) + { + error_string = sformatf( "Expecting enrichment factor > 0, " + "resetting to erm_ddl = 1."); + warning_msg(error_string); + s_ptr->erm_ddl = 1.0; + } + opt_save = OPTION_DEFAULT; + break; +/* VP: Density Start */ + case 21: /* Millero a, b, c, d, e and f coefficients */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + print_density = read_millero_abcdef (next_char, &(s_ptr->millero[0])); + //if (!vm_read) + //{ + ///* copy millero parms into logk, for calculating pressure dependency... */ + // for (i = 0; i < 7; i++) + // s_ptr->logk[vm0 + i] = s_ptr->millero[i]; + // s_ptr->logk[vm0 + i] = 0; + //} + opt_save = OPTION_DEFAULT; + break; +/* VP: Density End */ + case 22: /* vm, supcrt parms + Ionic strength terms */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_aq_species_vm_parms(next_char, &s_ptr->logk[vma1]); + //vm_read = true; + print_density = OK; + opt_save = OPTION_DEFAULT; + break; + case 23: /* viscosity parms for the Jones-Dole eqn */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_viscosity_parms(next_char, &s_ptr->Jones_Dole[0]); + print_viscosity = OK; + opt_save = OPTION_DEFAULT; + break; + case OPTION_DEFAULT: +/* + * Get space for species information and parse equation + */ + s_ptr = NULL; + if (parse_eq(line, &next_elt, association) == ERROR) + { + parse_error++; + error_msg("Parsing equation.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + } +/* + * Get pointer to each species in the reaction, store new species if necessary + */ + trxn.token[0].s = + s_store(trxn.token[0].name, trxn.token[0].z, TRUE); + for (i = 1; i < count_trxn; i++) + { + trxn.token[i].s = + s_store(trxn.token[i].name, trxn.token[i].z, FALSE); + } +/* + * Save element list and carbon, hydrogen, and oxygen in species + */ + trxn.token[0].s->next_elt = next_elt; + trxn.token[0].s->next_secondary = NULL; + for (; next_elt->elt != NULL; next_elt++) + { + if (strcmp(next_elt->elt->name, "C") == 0) + { + trxn.token[0].s->carbon = next_elt->coef; + } + if (strcmp(next_elt->elt->name, "H") == 0) + { + trxn.token[0].s->h = next_elt->coef; + } + if (strcmp(next_elt->elt->name, "O") == 0) + { + trxn.token[0].s->o = next_elt->coef; + } + } +/* + * Malloc space for species reaction + */ + trxn.token[0].s->rxn = rxn_alloc(count_trxn + 1); +/* + * Copy reaction to reaction for species + */ + trxn_copy(trxn.token[0].s->rxn); + s_ptr = trxn.token[0].s; +/* + * Default gamma data + */ + s_ptr->dha = 0.0; + s_ptr->dhb = 0.0; + if (equal(s_ptr->z, 0.0, TOL) == TRUE) + { + s_ptr->gflag = 0; /* Uncharged */ + s_ptr->dhb = 0.1; + } + else + { + s_ptr->gflag = 1; /* Davies */ + } +/* + * Set type for species + */ + if (strcmp(trxn.token[0].s->name, "H+") == 0) + { + s_hplus = trxn.token[0].s; + s_hplus->type = HPLUS; + } + else if (strcmp(trxn.token[0].s->name, "H3O+") == 0) + { + s_h3oplus = trxn.token[0].s; + s_h3oplus->type = HPLUS; + } + else if (strcmp(trxn.token[0].s->name, "e-") == 0) + { + s_eminus = trxn.token[0].s; + s_eminus->type = EMINUS; + s_eminus->gflag = 3; /* Always 1 */ + } + else if (strcmp(trxn.token[0].s->name, "H2O") == 0) + { + s_h2o = trxn.token[0].s; + s_h2o->type = H2O; + s_h2o->gflag = 3; /* Always 1 */ + } + else if (strstr(trxn.token[0].s->name, "(s)") != NULL) + { + trxn.token[0].s->type = SOLID; + } + else if (strcmp(trxn.token[0].s->name, "H2") == 0) + { + trxn.token[0].s->type = AQ; + s_h2 = trxn.token[0].s; + } + else if (strcmp(trxn.token[0].s->name, "O2") == 0) + { + trxn.token[0].s->type = AQ; + s_o2 = trxn.token[0].s; + } + else + { + trxn.token[0].s->type = AQ; + } + opt_save = OPTION_DEFAULT; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_use(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads solution, mix, irreversible reaction, and pure phases to use + * in reaction calculation + */ + int i, l, n_user, return_value; + char *ptr; + char token[MAX_LENGTH], token1[MAX_LENGTH];; +/* + * Read "use" + */ + ptr = line; + copy_token(token, &ptr, &l); +/* + * Read keyword + */ + copy_token(token, &ptr, &l); + check_key(token); + if (next_keyword != Keywords::KEY_SOLUTION && + next_keyword != Keywords::KEY_MIX && + next_keyword != Keywords::KEY_KINETICS && + next_keyword != Keywords::KEY_REACTION && + next_keyword != Keywords::KEY_REACTION_TEMPERATURE && + next_keyword != Keywords::KEY_REACTION_PRESSURE && + next_keyword != Keywords::KEY_EQUILIBRIUM_PHASES && + next_keyword != Keywords::KEY_EXCHANGE && + next_keyword != Keywords::KEY_SURFACE && + next_keyword != Keywords::KEY_GAS_PHASE && + next_keyword != Keywords::KEY_SOLID_SOLUTIONS) + { + input_error++; + error_msg("Unknown item in USE keyword", CONTINUE); + error_msg(line_save, CONTINUE); + check_line("End of use", FALSE, TRUE, TRUE, TRUE); + /* empty, eof, keyword, print */ + return (ERROR); + } +/* + * Read number + */ + strcpy(token1, token); + for (;;) + { + i = copy_token(token, &ptr, &l); + if (i == DIGIT) + { + sscanf(token, "%d", &n_user); + if (n_user < 0) + { + error_msg("Number must be a positive integer.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + if (strstr(token, "-") != NULL) + { + error_string = sformatf( + "USE does not accept a range of numbers, %s.", token); + warning_msg(error_string); + error_string = sformatf( + "Only %s %d will be used in the batch-reaction calculation.", + token1, n_user); + warning_msg(error_string); + error_string = sformatf( + "NOTE--USE is not needed for ADVECTION and TRANSPORT calculations."); + warning_msg(error_string); + + } + break; + } + else if (i == EMPTY) + { + error_string = sformatf( "No number given, 1 assumed."); + warning_msg(error_string); + n_user = 1; + break; + } + else if (token[0] == 'N' || token[0] == 'n') + { + n_user = -2; + break; + } + } + switch (next_keyword) + { + case Keywords::KEY_SOLUTION: /* Solution */ + use.Set_n_solution_user(n_user); + if (n_user >= 0) + { + use.Set_solution_in(true); + } + else + { + use.Set_solution_in(false); + } + break; + case Keywords::KEY_EQUILIBRIUM_PHASES: /* Pure phases */ + use.Set_n_pp_assemblage_user(n_user); + if (n_user >= 0) + { + use.Set_pp_assemblage_in(true); + } + else + { + use.Set_pp_assemblage_in(false); + } + break; + case Keywords::KEY_REACTION: /* Reaction */ + use.Set_n_reaction_user(n_user); + if (n_user >= 0) + { + use.Set_reaction_in(true); + } + else + { + use.Set_reaction_in(false); + } + break; + case Keywords::KEY_MIX: /* Mix */ + use.Set_n_mix_user(n_user); + if (n_user >= 0) + { + use.Set_mix_in(true); + } + else + { + use.Set_mix_in(false); + } + break; + case Keywords::KEY_EXCHANGE: /* Ex */ + use.Set_n_exchange_user(n_user); + if (n_user >= 0) + { + use.Set_exchange_in(true); + } + else + { + use.Set_exchange_in(false); + } + break; + case Keywords::KEY_SURFACE: /* Surface */ + use.Set_n_surface_user(n_user); + if (n_user >= 0) + { + use.Set_surface_in(true); + } + else + { + use.Set_surface_in(false); + } + break; + case Keywords::KEY_REACTION_TEMPERATURE: /* Temperature */ + use.Set_n_temperature_user(n_user); + if (n_user >= 0) + { + use.Set_temperature_in(true); + } + else + { + use.Set_temperature_in(false); + } + break; + case Keywords::KEY_REACTION_PRESSURE: /* pressure */ + use.Set_n_pressure_user(n_user); + if (n_user >= 0) + { + use.Set_pressure_in(true); + } + else + { + use.Set_pressure_in(false); + } + break; + case Keywords::KEY_GAS_PHASE: /* Gas */ + use.Set_n_gas_phase_user(n_user); + if (n_user >= 0) + { + use.Set_gas_phase_in(true); + } + else + { + use.Set_gas_phase_in(false); + } + break; + case Keywords::KEY_KINETICS: /* Kinetics */ + use.Set_n_kinetics_user(n_user); + if (n_user >= 0) + { + use.Set_kinetics_in(true); + } + else + { + use.Set_kinetics_in(false); + } + break; + case Keywords::KEY_SOLID_SOLUTIONS: /* solid_solutions */ + use.Set_n_ss_assemblage_user(n_user); + if (n_user >= 0) + { + use.Set_ss_assemblage_in(true); + } + else + { + use.Set_ss_assemblage_in(false); + } + break; + default: + input_error++; + error_msg(line_save, CONTINUE); + error_msg("Error in switch for USE.", STOP); + break; + } + return_value = check_line("End of use", FALSE, TRUE, TRUE, TRUE); + /* empty, eof, keyword, print */ + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_surface_species(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Read data for surface species, parse equations + */ + int i, j; + int association; + char token[MAX_LENGTH]; + char *ptr; + LDBLE offset; + + struct species *s_ptr; + struct elt_list *next_elt; + struct rxn_token *token_ptr; + + int return_value, opt, opt_save; + char *next_char; + const char *opt_list[] = { + "no_check", /* 0 */ + "check", /* 1 */ + "mb", /* 2 */ + "mass_balance", /* 3 */ + "log_k", /* 4 */ + "logk", /* 5 */ + "delta_h", /* 6 */ + "deltah", /* 7 */ + "analytical_expression", /* 8 */ + "a_e", /* 9 */ + "ae", /* 10 */ + "mole_balance", /* 11 */ + "offset", /* 12 */ + "add_logk", /* 13 */ + "add_log_k", /* 14 */ + "add_constant", /* 15 */ + "cd_music", /* 16 */ + "music", /* 17 */ + "vm" /* 18 */ + }; + int count_opt_list = 19; + association = TRUE; + /* + * Read eqn from file and call parser + */ + opt_save = OPTION_DEFAULT; + return_value = UNKNOWN; + s_ptr = NULL; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SURFACE_SPECIES keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* no_check */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->check_equation = FALSE; + break; + case 1: /* check */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + s_ptr->check_equation = TRUE; + break; + case 2: /* mb */ + case 3: /* mass_balance */ + case 11: /* mole_balance */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + count_elts = 0; + paren_count = 0; + copy_token(token, &next_char, &i); + s_ptr->mole_balance = string_hsave(token); + ptr = token; + s_ptr->next_secondary = + (struct elt_list *) free_check_null(s_ptr->next_secondary); + get_secondary_in_species(&ptr, 1.0); + s_ptr->next_secondary = elt_list_save(); + /* debug + for (i = 0; i < count_elts; i++) { + output_msg(sformatf("%s\t%f\n", elt_list[i].elt->name, + elt_list[i].coef)); + } + */ + opt_save = OPTION_DEFAULT; + break; + case 4: /* log_k */ + case 5: /* logk */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_log_k_only(next_char, &s_ptr->logk[0]); + opt_save = OPTION_DEFAULT; + break; + case 6: /* delta_h */ + case 7: /* deltah */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_delta_h_only(next_char, &s_ptr->logk[1], + &s_ptr->original_units); + opt_save = OPTION_DEFAULT; + break; + case 8: /* analytical_expression */ + case 9: /* a_e */ + case 10: /* ae */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_analytical_expression_only(next_char, &(s_ptr->logk[T_A1])); + opt_save = OPTION_DEFAULT; + break; + case 12: /* offset */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + if (sscanf(next_char, SCANFORMAT, &offset) != 1) + { + error_msg("No offset for log K given", STOP); + } + s_ptr->logk[0] += offset; + opt_save = OPTION_DEFAULT; + break; + case 13: /* add_logk */ + case 14: /* add_log_k */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + if (s_ptr->count_add_logk == 0) + { + s_ptr->add_logk = + (struct name_coef *) + PHRQ_malloc(sizeof(struct name_coef)); + if (s_ptr->add_logk == NULL) + { + malloc_error(); + return (OK); + } + } + else + { + s_ptr->add_logk = + (struct name_coef *) PHRQ_realloc(s_ptr->add_logk, + (size_t) ((s_ptr-> + count_add_logk + + + 1) * + sizeof + (struct + name_coef))); + if (s_ptr->add_logk == NULL) + { + malloc_error(); + return (OK); + } + } + /* read name */ + if (copy_token(token, &next_char, &i) == EMPTY) + { + input_error++; + error_string = sformatf( + "Expected the name of a NAMED_EXPRESSION."); + error_msg(error_string, CONTINUE); + break; + } + s_ptr->add_logk[s_ptr->count_add_logk].name = string_hsave(token); + /* read coef */ + i = sscanf(next_char, SCANFORMAT, + &s_ptr->add_logk[s_ptr->count_add_logk].coef); + if (i <= 0) + { + s_ptr->add_logk[s_ptr->count_add_logk].coef = 1; + } + s_ptr->count_add_logk++; + opt_save = OPTION_DEFAULT; + break; + case 15: /* add_constant */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + if (s_ptr->count_add_logk == 0) + { + s_ptr->add_logk = + (struct name_coef *) + PHRQ_malloc(sizeof(struct name_coef)); + if (s_ptr->add_logk == NULL) + malloc_error(); + } + else + { + s_ptr->add_logk = + (struct name_coef *) PHRQ_realloc(s_ptr->add_logk, + (size_t) ((s_ptr-> + count_add_logk + + + 1) * + sizeof + (struct + name_coef))); + if (s_ptr->add_logk == NULL) + malloc_error(); + } + i = sscanf(next_char, SCANFORMAT, + &s_ptr->add_logk[s_ptr->count_add_logk].coef); + if (i <= 0) + { + input_error++; + error_string = sformatf( + "Expected the constant to add for log_K definition."); + error_msg(error_string, CONTINUE); + break; + } + /* set name */ + s_ptr->add_logk[s_ptr->count_add_logk].name = + string_hsave("XconstantX"); + /* read coef */ + s_ptr->count_add_logk++; + opt_save = OPTION_DEFAULT; + break; + case 16: /* cd_music */ + case 17: /* music */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + for (j = 0; j < 5; j++) + { + if (copy_token(token, &next_char, &i) == EMPTY) + break; + if (sscanf(token, SCANFORMAT, &s_ptr->cd_music[j]) != 1) + break; + } + s_ptr->dz[0] = s_ptr->cd_music[0] + s_ptr->cd_music[3] * s_ptr->cd_music[4]; + s_ptr->dz[1] = s_ptr->cd_music[1] + (1 - s_ptr->cd_music[3]) * s_ptr->cd_music[4]; + s_ptr->dz[2] = s_ptr->cd_music[2]; + for (j = 0; j < 3; j++) + { + s_ptr->rxn->dz[j] = s_ptr->dz[j]; + } + opt_save = OPTION_DEFAULT; + break; + case 18: /* vm, molar volume */ + if (s_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_vm_only(next_char, &s_ptr->logk[vm0], + &s_ptr->original_deltav_units); + opt_save = OPTION_DEFAULT; + break; + case OPTION_DEFAULT: + /* + * Get surface species information and parse equation + */ + s_ptr = NULL; + if (parse_eq(line, &next_elt, association) == ERROR) + { + parse_error++; + error_msg("Parsing equation.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + } + /* + * Get pointer to each species in the reaction, store new species if necessary + */ + trxn.token[0].s = + s_store(trxn.token[0].name, trxn.token[0].z, TRUE); + for (i = 1; i < count_trxn; i++) + { + trxn.token[i].s = + s_store(trxn.token[i].name, trxn.token[i].z, FALSE); + } + /* + * Save element list and carbon, hydrogen, and oxygen in species + */ + trxn.token[0].s->next_elt = next_elt; + for (; next_elt->elt != NULL; next_elt++) + { + if (strcmp(next_elt->elt->name, "C") == 0) + { + trxn.token[0].s->carbon = next_elt->coef; + } + if (strcmp(next_elt->elt->name, "H") == 0) + { + trxn.token[0].s->h = next_elt->coef; + } + if (strcmp(next_elt->elt->name, "O") == 0) + { + trxn.token[0].s->o = next_elt->coef; + } + } +#ifdef SKIP + // need to do this in tidy in case surface master species not yet defined + /* + * Find coefficient of surface in rxn, store in equiv + */ + trxn.token[0].s->equiv = 0.0; + for (i = 1; i < count_trxn; i++) + { + if (trxn.token[i].s->type == SURF) + { + trxn.token[0].s->equiv = trxn.token[i].coef; + } + } + if (trxn.token[0].s->equiv == 0.0) + trxn.token[0].s->equiv = 1.0; +#endif + /* + * Malloc space for species reaction + */ + trxn.token[0].s->rxn = rxn_alloc(count_trxn + 1); + /* + * Copy reaction to reaction for species + */ + token_ptr = trxn.token[0].s->rxn->token; + for (i = 0; i < count_trxn; i++) + { + token_ptr[i].s = trxn.token[i].s; + token_ptr[i].coef = trxn.token[i].coef; + } + token_ptr[i].s = NULL; + /* + * Set type for species + */ + trxn.token[0].s->type = SURF; + s_ptr = trxn.token[0].s; + /* + * Read gamma data + */ + s_ptr->gflag = 6; + s_ptr->dha = 0.0; + s_ptr->dhb = 0.0; + opt_save = OPTION_DEFAULT; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_surface(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Reads surface data + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int n_user, n_user_end; + LDBLE conc; + char *ptr, *ptr1; + char *description; + std::string token, token1, name; + + int return_value, opt; + char *next_char; + const char *opt_list[] = { + "equilibrate", /* 0 */ + "equil", /* 1 */ + "diff", /* 2 */ + "diffuse_layer", /* 3 */ + "no_edl", /* 4 */ + "no_electrostatic", /* 5 */ + "only_counter_ions", /* 6 */ + "donnan", /* 7 */ + "cd_music", /* 8 */ + "capacitances", /* 9 */ + "sites", /* 10 */ + "sites_units", /* 11 */ + "constant_capacitance", /* 12 */ + "ccm", /* 13 */ + "equilibrium", /* 14 */ + "site_units" /* 15 */ + }; + int count_opt_list = 16; + /* + * kin_surf is for Surfaces, related to kinetically reacting minerals + * they are defined if "sites" is followed by mineral name: + * Surf_wOH Manganite [equilibrium_phases or kinetics] 0.25 4000 + * ^Name mineral ^switch ^prop.factor ^m2/mol + */ + /* + * Read surface number and description + */ + ptr = line; + read_number_description(ptr, &n_user, &n_user_end, &description); + cxxSurface temp_surface; + cxxSurfaceComp *comp_ptr = NULL; + cxxSurfaceCharge *charge_ptr = NULL; + temp_surface.Set_new_def(true); + temp_surface.Set_n_user(n_user); + temp_surface.Set_n_user_end(n_user_end); + temp_surface.Set_description(description); + free_check_null(description); + + if (use.Get_surface_in() == FALSE) + { + use.Set_surface_in(true); + use.Set_n_surface_user(n_user); + } + /* + * Read surface data + */ + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SURFACE keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* equilibrate */ + case 1: /* equil */ + case 14: /* equilibrium */ + for (;;) + { + int i = copy_token(token, &next_char); + if (i == DIGIT) + { + int j; + sscanf(token.c_str(), "%d", &j); + temp_surface.Set_solution_equilibria(true); + temp_surface.Set_n_solution(j); + break; + } + if (i == EMPTY) + { + error_msg + ("Expected a solution number with which to equilibrate surface.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + } + break; + case 2: /* diffuse_layer */ + case 3: + { + temp_surface.Set_thickness(1e-8); + temp_surface.Set_dl_type(cxxSurface::BORKOVEK_DL); + int j = sscanf(next_char, SCANFORMAT, &dummy); + if (j == 1) + { + temp_surface.Set_thickness(dummy); + } + } + break; + case 4: /* no electrostatic */ + case 5: + temp_surface.Set_type(cxxSurface::NO_EDL); + break; + case 6: + temp_surface.Set_only_counter_ions(get_true_false(next_char, TRUE) == TRUE); + break; + case 7: /* donnan for DL conc's */ + { + temp_surface.Set_dl_type(cxxSurface::DONNAN_DL); + LDBLE thickness = 0.0; + for (;;) + { + int i = copy_token(token, &next_char); + if (i == DIGIT) + { + sscanf(token.c_str(), SCANFORMAT, &dummy); + temp_surface.Set_thickness(dummy); + thickness = 1; + continue; + } + else if (i != EMPTY) + { + if (token[0] == 'D' || token[0] == 'd') + { + if (thickness != 0) + { + error_msg + ("You must enter EITHER thickness OR Debye lengths (1/k),\n and relative DDL viscosity, DDL limit.\nCorrect is (for example): -donnan 1e-8 viscosity 0.5\n or (default values): -donnan debye_lengths 1 viscosity 1 limit 0.8", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + int j = copy_token(token1, &next_char); + if (j == DIGIT) + { + sscanf(token1.c_str(), SCANFORMAT, &dummy); + temp_surface.Set_debye_lengths(dummy); + continue; + } + else if (j != EMPTY) + { + error_msg + ("Expected number of Debye lengths (1/k).", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + } + else if (token[0] == 'V' || token[0] == 'v') + { + int j = copy_token(token1, &next_char); + if (j == DIGIT) + { + sscanf(token1.c_str(), SCANFORMAT, &dummy); + temp_surface.Set_DDL_viscosity(dummy); + continue; + } + else if (j != EMPTY) + { + error_msg + ("Expected number for relative DDL viscosity.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + } + else if (token[0] == 'L' || token[0] == 'l') + { + int j = copy_token(token1, &next_char); + if (j == DIGIT) + { + sscanf(token1.c_str(), SCANFORMAT, &dummy); + temp_surface.Set_DDL_limit(dummy); + continue; + } + else if (j != EMPTY) + { + error_msg + ("Expected number for maximum of DDL water as fraction of bulk water.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + } + else + { + error_msg + ("Expected diffuse layer thickness (m) or Debye lengths (1/k) for \n\tcalculating the thickness, and relative DDL viscosity and DDL limit.\nCorrect is (for example): -donnan 1e-8 visc 0.5\n or (default values): -donnan debye_lengths 1 visc 1 lim 0.8", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + } + else + break; + } + } + break; + case 8: /* cd_music */ + temp_surface.Set_type(cxxSurface::CD_MUSIC); + break; + case 9: /* capacitances */ + /* + * Read capacitance for CD_MUSIC + */ + if (charge_ptr == NULL) + { + error_msg + ("Surface component has not been defined for capacitances.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + copy_token(token1, &next_char); + if (sscanf(token1.c_str(), SCANFORMAT, &dummy) == 1) + { + charge_ptr->Set_capacitance0(dummy); + } + copy_token(token1, &next_char); + if (sscanf(token1.c_str(), SCANFORMAT, &dummy) == 1) + { + charge_ptr->Set_capacitance1(dummy); + } + break; + case 10: /* sites */ + case 11: /* sites_units */ + case 15: /* site_units */ + { + int j = copy_token(token1, &next_char); + if (j != EMPTY && (token1[0] == 'A' || token1[0] == 'a')) + { + temp_surface.Set_sites_units(cxxSurface::SITES_ABSOLUTE); + } + else if (j != EMPTY && (token1[0] == 'D' || token1[0] == 'd')) + { + temp_surface.Set_sites_units(cxxSurface::SITES_DENSITY); + } + else + { + error_msg + ("Character string expected to be 'Absolute' or 'Density' to define the units of the first item in the definition of a surface component.\n", + CONTINUE); + input_error++; + break; + } + } + break; + // CCM not implemented yet + case 12: /* constant_capacitance */ + case 13: /* ccm */ + temp_surface.Set_type(cxxSurface::CCM); + copy_token(token1, &next_char); + if (charge_ptr == NULL) + { + error_msg("A surface must be defined before the capacitance is defined.\n", + CONTINUE); + input_error++; + break; + } + if (sscanf(token1.c_str(), SCANFORMAT, &dummy) != 1) + { + error_msg("Expected capacitance for constant_capacitance model.\n", + CONTINUE); + input_error++; + break; + } + else + { + charge_ptr->Set_capacitance0(dummy); + } + + /* constant capacitance model not implemented yet */ + //error_msg("Constant capacitance model not implemented.", CONTINUE); + //input_error++; + + break; + case OPTION_DEFAULT: + /* + * Read surface component + */ + { + ptr = line; + int i = copy_token(token, &ptr); + if (i != UPPER && token[0] != '[') + { + error_msg + ("Expected surface name to begin with a capital letter.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + + cxxSurfaceComp temp_comp; + temp_surface.Get_surface_comps().push_back(temp_comp); + comp_ptr = &(temp_surface.Get_surface_comps().back()); + comp_ptr->Set_formula(token.c_str()); + + i = copy_token(token1, &ptr); + if (i == DIGIT) + { + /* + * Read surface concentration + */ + /* surface concentration is read directly */ + if (sscanf(token1.c_str(), SCANFORMAT, &conc) < 1) + { + error_msg("Expected number of surface sites in moles.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + comp_ptr->Set_moles(conc); + /* + * Read equilibrium phase name or kinetics rate name + */ + } + else if (i != EMPTY) + { + + /* surface conc. is related to mineral or kinetics */ + comp_ptr->Set_phase_name(token1.c_str()); + int j = copy_token(token1, &ptr); + + /* read optional 'equilibrium_phases' or 'kinetics' */ + if (j != DIGIT) + { + if (token1[0] == 'K' || token1[0] == 'k') + { + comp_ptr->Set_rate_name(comp_ptr->Get_phase_name().c_str()); + comp_ptr->Set_phase_name(NULL); + } + else if (token1[0] == 'E' || token1[0] == 'e') + { + comp_ptr->Set_rate_name(NULL); + } + else + { + error_msg + ("Character string expected to be 'equilibrium_phase' or 'kinetics' to relate surface to mineral or kinetic reaction.\n", + CONTINUE); + input_error++; + break; + } + j = copy_token(token1, &ptr); + } + + /* read proportion */ + if (j != DIGIT) + { + error_msg + ("Expected a coefficient to relate surface to mineral or kinetic reaction.\n", + CONTINUE); + input_error++; + break; + } + sscanf(token1.c_str(), SCANFORMAT, &dummy); + comp_ptr->Set_phase_proportion(dummy); + /* real conc must be defined in tidy_model */ + conc = 1.0; + } + else + { + error_msg + ("Expected concentration of surface, mineral name, or kinetic reaction name.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + break; + } + /* + * Accumulate elements in elt_list + */ + count_elts = 0; + paren_count = 0; + char * formula = string_duplicate(token.c_str()); + ptr1 = formula; + get_elts_in_species(&ptr1, conc); + /* + * save formula for adjusting number of exchange sites + */ + ptr1 = formula; + int l; + // name is work space + char * name = string_duplicate(formula); + name[0] = '\0'; + get_token(&ptr1, name, &dummy, &l); + comp_ptr->Set_formula_z(dummy); + cxxNameDouble nd = elt_list_NameDouble(); + comp_ptr->Set_totals(nd); + /* + * Search for charge structure + */ + ptr1 = formula; + get_elt(&ptr1, name, &l); + ptr1 = strchr(name, '_'); + if (ptr1 != NULL) + ptr1[0] = '\0'; + charge_ptr = temp_surface.Find_charge(name); + formula = (char*)free_check_null(formula); + if (charge_ptr == NULL) + { + cxxSurfaceCharge temp_charge; + temp_charge.Set_name(name); + if (comp_ptr->Get_phase_name().size() == 0 + && comp_ptr->Get_rate_name().size() == 0) + { + temp_charge.Set_specific_area(600.0); + temp_charge.Set_grams(0.0); + } + else + { + temp_charge.Set_specific_area(0.0); + temp_charge.Set_grams(1.0); + } + temp_surface.Get_surface_charges().push_back(temp_charge); + charge_ptr = temp_surface.Find_charge(name); + } + comp_ptr->Set_charge_name(name); + name = (char*)free_check_null(name); + /* + * Read surface area (m2/g) + */ + copy_token(token1, &ptr); + if (sscanf(token1.c_str(), SCANFORMAT, &dummy) == 1) + { + charge_ptr->Set_specific_area(dummy); + } + else + { + break; + } + /* + * Read grams of solid (g) + */ + copy_token(token1, &ptr); + if (sscanf(token1.c_str(), SCANFORMAT, &dummy) == 1) + { + charge_ptr->Set_grams(dummy); + } + /* read Dw */ + copy_token(token1, &ptr); + Utilities::str_tolower(token1); + if (strcmp(token1.c_str(), "dw") == 0) + { + int j = copy_token(token1, &ptr); + if (j != DIGIT) + { + error_msg + ("Expected surface diffusion coefficient (m2/s).\n", + CONTINUE); + input_error++; + break; + } + else + { + sscanf(token1.c_str(), SCANFORMAT, &dummy); + comp_ptr->Set_Dw(dummy); + if (dummy > 0) + { + temp_surface.Set_transport(true); + if (temp_surface.Get_related_rate() + || temp_surface.Get_related_phases()) + { + error_msg + ("Can`t transport surfaces related to phases or rates (yet).", + CONTINUE); + input_error++; + } + } + break; + } + } + } + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + + // Sort comps and charge + temp_surface.Sort_comps(); + + /* + * copy Dw > 0 to all comps with the same charge structure, check edl & dif for surface transport + */ + if (temp_surface.Get_transport()) + { + if (temp_surface.Get_type() <= cxxSurface::NO_EDL) + { + input_error++; + error_msg + ("Must use default Dzombak and Morel or -cd_music for surface transport.", + CONTINUE); + } + if (temp_surface.Get_dl_type() <= cxxSurface::NO_DL) + { + input_error++; + error_msg + ("Must use -donnan or -diffuse_layer for surface transport.", + CONTINUE); + } + for (size_t i = 0; i < temp_surface.Get_surface_comps().size(); i++) + { + comp_ptr = &(temp_surface.Get_surface_comps()[i]); + if (comp_ptr->Get_Dw() > 0) + { + std::string name = comp_ptr->Get_charge_name(); + for (size_t j = 0; j < temp_surface.Get_surface_comps().size(); j++) + { + cxxSurfaceComp *comp_ptr1 = &(temp_surface.Get_surface_comps()[j]); + std::string name1 = comp_ptr1->Get_charge_name(); + if (name == name1) + { + comp_ptr1->Set_Dw(comp_ptr->Get_Dw()); + } + } + } + } + } + /* + * Make sure surface area is defined + */ + if (temp_surface.Get_type() == cxxSurface::DDL || temp_surface.Get_type() == cxxSurface::CCM || temp_surface.Get_type() == cxxSurface::CD_MUSIC) + { + for (size_t i = 0; i < temp_surface.Get_surface_charges().size(); i++) + { + charge_ptr = &(temp_surface.Get_surface_charges()[i]); + if (charge_ptr->Get_grams() * + charge_ptr->Get_specific_area() <= 0) + { + error_string = sformatf( "Surface area not defined for %s.\n", + charge_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + } + } + /* + * Logical checks + */ + if (temp_surface.Get_type() == cxxSurface::UNKNOWN_DL) + { + error_string = sformatf( "Unknown surface type.\n"); + error_msg(error_string, CONTINUE); + input_error++; + } + else if (temp_surface.Get_type() == cxxSurface::NO_EDL) + { + if (temp_surface.Get_dl_type() != cxxSurface::NO_DL) + { + error_string = sformatf( + "No electrostatic term calculations do not allow calculation of the diffuse layer composition.\n"); + warning_msg(error_string); + temp_surface.Set_dl_type(cxxSurface::NO_DL); + } + } + else if (temp_surface.Get_type() == cxxSurface::DDL || temp_surface.Get_type() == cxxSurface::CCM) + { + /* all values of dl_type are valid */ + } + else if (temp_surface.Get_type() == cxxSurface::CD_MUSIC) + { + if (temp_surface.Get_dl_type() == cxxSurface::BORKOVEK_DL) + { + error_string = sformatf( + "Borkovec and Westall diffuse layer calculation is not allowed with a CD_MUSIC surface.\n\tUsing Donnan diffuse layer calculation."); + warning_msg(error_string); + temp_surface.Set_dl_type(cxxSurface::DONNAN_DL); + } + if (temp_surface.Get_debye_lengths() > 0) + { + error_msg + ("Variable DDL thickness is not permitted in CD_MUSIC. Fix DDL thickness\n\tfor example (default value): -donnan 1e-8", + CONTINUE); + input_error++; + } + } + temp_surface.Sort_comps(); + Rxn_surface_map[n_user] = temp_surface; + Rxn_new_surface.insert(n_user); + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_surface_master_species(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Reads master species data from data file or input file + */ + int l, return_value; + char *ptr, *ptr1; + LDBLE l_z; + struct species *s_ptr; + char token[MAX_LENGTH], token1[MAX_LENGTH]; + int opt, opt_save; + char *next_char; + const char *opt_list[] = { + "capacitance", /* 0 */ + "cd_music_capacitance" /* 1 */ + }; + int count_opt_list = 0; + opt_save = OPTION_DEFAULT; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SURFACE_MASTER_SPECIES keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case OPTION_DEFAULT: + /* + * Get "element" name with valence, allocate space, store + */ + ptr = line; + /* + * Get element name and save pointer to character string + */ + if (copy_token(token, &ptr, &l) != UPPER && token[0] != '[') + { + parse_error++; + error_msg("Reading element for master species.", CONTINUE); + error_msg(line_save, CONTINUE); + continue; + } + replace("(+", "(", token); + /* + * Delete master if it exists + */ + master_delete(token); + /* + * Increase pointer array, if necessary, and malloc space + */ + if (count_master + 4 >= max_master) + { + space((void **) ((void *) &master), count_master + 4, + &max_master, sizeof(struct master *)); + } + /* + * Save values in master and species structure for surface sites + */ + master[count_master] = master_alloc(); + master[count_master]->type = SURF; + master[count_master]->elt = element_store(token); + if (copy_token(token, &ptr, &l) != UPPER && token[0] != '[') + { + parse_error++; + error_msg("Reading surface master species name.", CONTINUE); + error_msg(line_save, CONTINUE); + continue; + } + s_ptr = s_search(token); + if (s_ptr != NULL) + { + master[count_master]->s = s_ptr; + } + else + { + ptr1 = token; + get_token(&ptr1, token1, &l_z, &l); + master[count_master]->s = s_store(token1, l_z, FALSE); + } + master[count_master]->primary = TRUE; + strcpy(token, master[count_master]->elt->name); + count_master++; + /* + * Save values in master and species structure for surface psi + */ + strcpy(token1, token); + replace("_", " ", token1); + ptr1 = token1; + copy_token(token, &ptr1, &l); + strcat(token, "_psi"); + add_psi_master_species(token); + opt_save = OPTION_DEFAULT; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_psi_master_species(char *token) +/* ---------------------------------------------------------------------- */ +{ + struct species *s_ptr; + struct master *master_ptr; + char *ptr; + char token1[MAX_LENGTH]; + int i, n, plane; + + strcpy(token1, token); + for (plane = SURF_PSI; plane <= SURF_PSI2; plane++) + { + strcpy(token, token1); + switch (plane) + { + case SURF_PSI: + break; + case SURF_PSI1: + strcat(token, "b"); + break; + case SURF_PSI2: + strcat(token, "d"); + break; + } + master_ptr = master_search(token, &n); + if (master_ptr == NULL) + { + master[count_master] = master_alloc(); + master[count_master]->type = plane; + master[count_master]->elt = element_store(token); + s_ptr = s_search(token); + if (s_ptr != NULL) + { + master[count_master]->s = s_ptr; + } + else + { + master[count_master]->s = s_store(token, 0.0, FALSE); + } + count_elts = 0; + paren_count = 0; + ptr = token; + get_elts_in_species(&ptr, 1.0); + master[count_master]->s->next_elt = elt_list_save(); + master[count_master]->s->type = plane; + master[count_master]->primary = TRUE; + master[count_master]->s->rxn = rxn_alloc(3); + /* + * Define reaction for psi + */ + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + master[count_master]->s->rxn->logk[i] = 0.0; + } + master[count_master]->s->rxn->token[0].s = + master[count_master]->s; + master[count_master]->s->rxn->token[0].coef = -1.0; + master[count_master]->s->rxn->token[1].s = + master[count_master]->s; + master[count_master]->s->rxn->token[1].coef = 1.0; + master[count_master]->s->rxn->token[2].s = NULL; + count_master++; + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_title(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads title for simulation + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + char *ptr, *ptr1; + int l, title_x_length, line_length; + int return_value; + char token[MAX_LENGTH]; +/* + * Read anything after keyword + */ + ptr = line; + copy_token(token, &ptr, &l); + ptr1 = ptr; + title_x = (char *) free_check_null(title_x); + if (copy_token(token, &ptr, &l) != EMPTY) + { + title_x = string_duplicate(ptr1); + } + else + { + title_x = (char *) PHRQ_malloc(sizeof(char)); + if (title_x == NULL) + malloc_error(); + title_x[0] = '\0'; + } + +/* + * Read additonal lines + */ + for (;;) + { + return_value = check_line("title", TRUE, TRUE, TRUE, TRUE); + /* empty, eof, keyword, print */ + if (return_value == EOF || return_value == KEYWORD) + break; +/* + * append line to title_x + */ + title_x_length = (int) strlen(title_x); + line_length = (int) strlen(line); + title_x = + (char *) PHRQ_realloc(title_x, + (size_t) (title_x_length + line_length + + 2) * sizeof(char)); + if (title_x == NULL) + malloc_error(); + if (title_x_length > 0) + { + title_x[title_x_length] = '\n'; + title_x[title_x_length + 1] = '\0'; + } + strcat(title_x, line); + } + last_title_x = title_x; + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_advection(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads advection information + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ +/* + * Read advection parameters: + * number of cells; + * number of shifts; + */ + char *ptr; + char *description; + int n_user, n_user_end, i; + + int count_punch, count_print; + int *punch_temp, *print_temp; + int return_value, opt, opt_save; + char *next_char; + const char *opt_list[] = { + "cells", /* 0 */ + "shifts", /* 1 */ + "print", /* 2 */ + "selected_output", /* 3 */ + "punch", /* 4 */ + "print_cells", /* 5 */ + "selected_cells", /* 6 */ + "time_step", /* 7 */ + "timest", /* 8 */ + "output", /* 9 */ + "output_frequency", /* 10 */ + "selected_output_frequency", /* 11 */ + "punch_frequency", /* 12 */ + "print_frequency", /* 13 */ + "punch_cells", /* 14 */ + "initial_time", /* 15 */ + "warning", /* 16 */ + "warnings" /* 17 */ + }; + int count_opt_list = 18; +/* + * Read advection number (not currently used) + */ + ptr = line; + read_number_description(ptr, &n_user, &n_user_end, &description); + description = (char *) free_check_null(description); +/* + * Set use data + */ + use.Set_advect_in(true); + count_ad_cells = 0; + count_ad_shifts = 0; + print_ad_modulus = 1; + punch_ad_modulus = 1; + count_punch = 0; + count_print = 0; + punch_temp = (int *) PHRQ_malloc(sizeof(int)); + if (punch_temp == NULL) + malloc_error(); + print_temp = (int *) PHRQ_malloc(sizeof(int)); + if (print_temp == NULL) + malloc_error(); +/* + * Read lines + */ + opt_save = OPTION_DEFAULT; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_DEFAULT: + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in ADVECTION keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* cells */ + sscanf(next_char, "%d", &count_ad_cells); + opt_save = OPTION_DEFAULT; + break; + case 1: /* shifts */ + sscanf(next_char, "%d", &count_ad_shifts); + opt_save = OPTION_DEFAULT; + break; + case 2: /* print */ + case 5: /* print_cells */ + print_temp = + read_list_ints_range(&next_char, &count_print, TRUE, + print_temp); + opt_save = 2; + break; + case 3: /* selected_output */ + case 11: /* selected_output_frequency */ + case 12: /* punch_frequency */ + sscanf(next_char, "%d", &punch_ad_modulus); + opt_save = OPTION_DEFAULT; + if (punch_ad_modulus <= 0) + { + error_string = sformatf( + "Punch frequency must be greater than 0. Frequency set to 1000."); + warning_msg(error_string); + punch_ad_modulus = 1000; + } + break; + case 4: /* punch */ + case 14: /* punch_cells */ + case 6: /* selected_cells */ + punch_temp = + read_list_ints_range(&next_char, &count_punch, TRUE, + punch_temp); + opt_save = 4; + break; + case 7: /* time_step */ + case 8: /* timest */ + sscanf(next_char, SCANFORMAT, &advection_kin_time); + { + std::string token; + int j = copy_token(token, &next_char); + j = copy_token(token, &next_char); + if (j == UPPER || j == LOWER) + { + advection_kin_time = Utilities::convert_time(advection_kin_time, token, "s"); + } + } + advection_kin_time_defined = TRUE; + opt_save = OPTION_DEFAULT; + break; + case 9: /* output */ + case 10: /* output_frequency */ + case 13: /* print_frequency */ + sscanf(next_char, "%d", &print_ad_modulus); + opt_save = OPTION_DEFAULT; + if (print_ad_modulus <= 0) + { + error_string = sformatf( + "Print frequency must be greater than 0. Frequency set to 1000."); + warning_msg(error_string); + print_ad_modulus = 1000; + } + break; + case 15: /* initial_time */ + char token[MAX_LENGTH]; + int j; + if (copy_token(token, &next_char, &j) == DIGIT) + sscanf(token, SCANFORMAT, &initial_total_time); + { + std::string stdtoken; + j = copy_token(stdtoken, &next_char); + if (j == UPPER || j == LOWER) + { + initial_total_time = Utilities::convert_time(initial_total_time, stdtoken, "s"); + } + } + opt_save = OPTION_DEFAULT; + break; + case 16: /* warning */ + case 17: /* warnings */ + advection_warnings = get_true_false(next_char, TRUE); + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } +/* + * Fill in data for punch + */ + advection_punch = + (int *) PHRQ_realloc(advection_punch, + (size_t) (count_ad_cells + 1) * sizeof(int)); + if (advection_punch == NULL) + malloc_error(); + if (count_punch != 0) + { + for (i = 0; i < count_ad_cells; i++) + advection_punch[i] = FALSE; + for (i = 0; i < count_punch; i++) + { + if (punch_temp[i] > count_ad_cells || punch_temp[i] < 1) + { + error_string = sformatf( + "Cell number for punch is out of range, %d. Request ignored.", + punch_temp[i]); + warning_msg(error_string); + } + else + { + advection_punch[punch_temp[i] - 1] = TRUE; + } + } + } + else + { + for (i = 0; i < count_ad_cells; i++) + advection_punch[i] = TRUE; + } + punch_temp = (int *) free_check_null(punch_temp); +/* + * Fill in data for print + */ + advection_print = + (int *) PHRQ_realloc(advection_print, + (size_t) (count_ad_cells + 1) * sizeof(int)); + if (advection_print == NULL) + malloc_error(); + if (count_print != 0) + { + for (i = 0; i < count_ad_cells; i++) + advection_print[i] = FALSE; + for (i = 0; i < count_print; i++) + { + if (print_temp[i] > count_ad_cells || print_temp[i] < 1) + { + error_string = sformatf( + "Cell number for print is out of range, %d. Request ignored.", + print_temp[i]); + warning_msg(error_string); + } + else + { + advection_print[print_temp[i] - 1] = TRUE; + } + } + } + else + { + for (i = 0; i < count_ad_cells; i++) + advection_print[i] = TRUE; + } + print_temp = (int *) free_check_null(print_temp); + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_debug(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads knobs and debugging info + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int return_value, opt; + char *next_char; + const char *opt_list[] = { + "iterations", /* 0 */ + "tolerance", /* 1 */ + "step_size", /* 2 */ + "pe_step_size", /* 3 */ + "scale_pure_phases", /* 4 */ + "diagonal_scale", /* 5 */ + "debug_model", /* 6 */ + "debug_prep", /* 7 */ + "debug_set", /* 8 */ + "debug_inverse", /* 9 */ + "logfile", /* 10 */ + "log_file", /* 11 */ + "debug_diffuse_layer", /* 12 */ + "delay_mass_water", /* 13 */ + "convergence_tolerance", /* 14 */ + "numerical_derivatives", /* 15 */ + "tries", /* 16 */ + "try", /* 17 */ + "numerical_fixed_volume", /* 18 */ + "force_numerical_fixed_volume", /* 19 */ + "equi_delay", /* 20 */ + "minimum_total", /* 21 */ + "min_total" /* 22 */ + }; + int count_opt_list = 23; +/* + * Read parameters: + * ineq_tol; + * step_size; + * pe_step_size; + * pp_scale; + * diagonal_scale; + */ + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_DEFAULT: + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in KNOBS keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* iterations */ + sscanf(next_char, "%d", &itmax); + break; + case 1: /* tolerance */ + sscanf(next_char, SCANFORMAT, &ineq_tol); + break; + case 2: /* step_size */ + sscanf(next_char, SCANFORMAT, &step_size); + break; + case 3: /* pe_step_size */ + sscanf(next_char, SCANFORMAT, &pe_step_size); + break; + case 4: /* pp_scale */ + sscanf(next_char, SCANFORMAT, &pp_scale); + break; + case 5: /* diagonal_scale */ + diagonal_scale = get_true_false(next_char, TRUE); + break; + case 6: /* debug_model */ + debug_model = get_true_false(next_char, TRUE); + break; + case 7: /* debug_prep */ + debug_prep = get_true_false(next_char, TRUE); + break; + case 8: /* debug_set */ + debug_set = get_true_false(next_char, TRUE); + break; + case 9: /* debug_inverse */ + debug_inverse = get_true_false(next_char, TRUE); + break; + case 10: /* logfile */ + case 11: /* log_file */ + pr.logfile = get_true_false(next_char, TRUE); + if (phast == TRUE) + { + pr.logfile = FALSE; + warning_msg("PHREEQC log file is disabled in PHAST"); + } + phrq_io->Set_log_on(pr.logfile == TRUE); + break; + case 12: /* debug_diffuse_layer */ + debug_diffuse_layer = get_true_false(next_char, TRUE); + break; + case 13: /* delay_mass_water */ + delay_mass_water = get_true_false(next_char, TRUE); + break; + case 14: /* convergence_tolerance */ + { + LDBLE ct; + sscanf(next_char, SCANFORMAT, &ct); + convergence_tolerance = ct; + } + break; + case 15: /* numerical_derivatives */ + numerical_deriv = get_true_false(next_char, TRUE); + break; + case 16: /* tries */ + case 17: /* try */ + sscanf(next_char, "%d", &max_tries); + break; + case 18: /* debug_inverse */ + numerical_fixed_volume = (get_true_false(next_char, TRUE) == TRUE); + break; + case 19: /* debug_inverse */ + force_numerical_fixed_volume = (get_true_false(next_char, TRUE) == TRUE); + break; + case 20: /* equi_delay */ + sscanf(next_char, "%d", &equi_delay); + break; + case 21: /* minimum_total */ + case 22: /* min_total */ + sscanf(next_char, SCANFORMAT, &MIN_TOTAL); + MIN_TOTAL_SS = MIN_TOTAL/100; + MIN_RELATED_SURFACE = MIN_TOTAL*100; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_print(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads printing info + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int return_value, opt, l; + char *next_char; + char token[MAX_LENGTH]; + LDBLE num; + const char *opt_list[] = { + "reset", /* 0 */ + "gas_phase", /* 1 */ + "pure_phase", /* 2 */ + "surface", /* 3 */ + "exchange", /* 4 */ + "totals", /* 5 */ + "eh", /* 6 */ + "species", /* 7 */ + "saturation_indices", /* 8 */ + "si", /* 9 same a 8 */ + "reaction", /* 10 irrev, not used, pr.use */ + "mix", /* 11 */ + "use", /* 12 */ + "selected_output", /* 13 */ + "equilibrium_phases", /* 14 same as 2 */ + "equilibria", /* 15 same as 2 */ + "equilibrium", /* 16 same as 2 */ + "pure", /* 17 same as 2 */ + "other", /* 18 same as 12 */ + "status", /* 19 */ + "inverse", /* 20 */ + "kinetics", /* 21 */ + "dump", /* 22 */ + "user_print", /* 23 */ + "user_pr", /* 24 */ + "solid_solution", /* 25 */ + "solid_solutions", /* 26 */ + "inverse_modeling", /* 27 */ + "headings", /* 28 */ + "heading", /* 29 */ + "user_graph", /* 30 */ + "echo_input", /* 31 */ + "warning", /* 32 */ + "warnings", /* 33 */ + "initial_isotopes", /* 34 */ + "isotope_ratios", /* 35 */ + "isotope_alphas", /* 36 */ + "censor_species", /* 37 */ + "alkalinity", /* 38 */ + "equilibrium_phase", /* 39 */ + "high_precision" /* 40 */ + }; + + int count_opt_list = 41; + int value; +/* + * Read flags: + */ + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_DEFAULT: + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in PRINT keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* reset */ + value = get_true_false(next_char, TRUE); + pr.kinetics = value; + pr.gas_phase = value; + pr.pp_assemblage = value; + pr.surface = value; + pr.exchange = value; + pr.totals = value; + pr.eh = value; + pr.species = value; + pr.saturation_indices = value; + pr.irrev = value; + pr.mix = value; + pr.reaction = value; + pr.use = value; + pr.inverse = value; + pr.user_print = value; + pr.ss_assemblage = value; + pr.headings = value; + pr.initial_isotopes = value; + pr.isotope_ratios = value; + pr.isotope_alphas = value; + pr.echo_input = value; + break; + case 1: /* gas_phase */ + pr.gas_phase = get_true_false(next_char, TRUE); + break; + case 2: /* pure_phase */ + case 14: /* equilibrium_phases */ + case 15: /* equilibria */ + case 16: /* equilibrium */ + case 17: /* pure */ + case 39: /* equilibrium_phase */ + pr.pp_assemblage = get_true_false(next_char, TRUE); + break; + case 3: /* surface */ + pr.surface = get_true_false(next_char, TRUE); + break; + case 4: /* exchange */ + pr.exchange = get_true_false(next_char, TRUE); + break; + case 5: /* totals */ + pr.totals = get_true_false(next_char, TRUE); + break; + case 6: /* eh */ + pr.eh = get_true_false(next_char, TRUE); + break; + case 7: /* species */ + pr.species = get_true_false(next_char, TRUE); + break; + case 8: + case 9: /* saturation_indices */ + pr.saturation_indices = get_true_false(next_char, TRUE); + break; + case 10: /* reaction, not used, pr.use controls */ + pr.irrev = get_true_false(next_char, TRUE); + break; + case 11: /* mix, not used, pr.use controls */ + pr.mix = get_true_false(next_char, TRUE); + break; + case 12: /* use */ + case 18: /* other */ + pr.use = get_true_false(next_char, TRUE); + break; + case 13: /* selected_output */ + pr.punch = get_true_false(next_char, TRUE); + phrq_io->Set_punch_on(pr.punch == TRUE); + break; + case 19: /* status */ + { + int j; + pr.status = get_true_false(next_char, TRUE); + j = copy_token(token, &next_char, &l); + if (j == UPPER || j == LOWER) + { + j = copy_token(token, &next_char, &l); + } + if (j == DIGIT) + { + char * tptr = token; + get_num(&tptr, &num); + num = floor(num); + if (num < 0.0) num = 0.0; + //status_interval = (int) floor(num); + status_interval = (clock_t) num; + } + } + //if (status_interval < 0) + // status_interval = 0; + break; + case 20: /* inverse */ + case 27: /* inverse_modeling */ + pr.inverse = get_true_false(next_char, TRUE); + break; + case 21: /* kinetics */ + pr.kinetics = get_true_false(next_char, TRUE); + break; + case 22: /* dump */ + pr.dump = get_true_false(next_char, TRUE); + phrq_io->Set_dump_on(pr.dump == TRUE); + break; + case 23: /* user_print */ + case 24: /* user_pr */ + pr.user_print = get_true_false(next_char, TRUE); + break; + case 25: /* solid_solution */ + case 26: /* solid_solutions */ + pr.ss_assemblage = get_true_false(next_char, TRUE); + break; + case 28: /* headings */ + case 29: /* heading */ + pr.headings = get_true_false(next_char, TRUE); + break; + case 30: /* user_graph */ + pr.user_graph = get_true_false(next_char, TRUE); + break; + case 31: /* echo_input */ + pr.echo_input = get_true_false(next_char, TRUE); + if (pr.echo_input == 0) + phrq_io->Set_echo_on(false); + else + phrq_io->Set_echo_on(true); //**appt + break; + case 32: /* warning */ + case 33: /* warnings */ + sscanf(next_char, "%d", &pr.warnings); + break; + case 34: /* initial_isotopes */ + pr.initial_isotopes = get_true_false(next_char, TRUE); + break; + case 35: /* isotope_ratios */ + pr.isotope_ratios = get_true_false(next_char, TRUE); + break; + case 36: /* isotope_alphas */ + pr.isotope_alphas = get_true_false(next_char, TRUE); + break; + case 37: /* censor_species */ + if (copy_token(token, &next_char, &l) != EMPTY) + { + sscanf(token, SCANFORMAT, &censor); + } + else + { + censor = 0; + } + break; + case 38: /* alkalinity */ + pr.alkalinity = get_true_false(next_char, TRUE); + break; + case 40: /* high_precision */ + value = get_true_false(next_char, TRUE); + high_precision = (value != FALSE); + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +/* + * Utilitity routines for read + */ +/* ---------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_key(const char *str) +/* ---------------------------------------------------------------------- */ +{ +/* + * Check if string begins with a keyword, returns TRUE or FALSE + * + * Arguments: + * *str is pointer to character string to be checked for keywords + * Returns: + * TRUE, + * FALSE. + */ + char *ptr; + std::string stdtoken; + char * token1; + token1 = string_duplicate(str); + + ptr = token1; + int j = copy_token(stdtoken, &ptr); + Utilities::str_tolower(stdtoken); + std::string key(stdtoken); + + if (j == EMPTY) + { + next_keyword = Keywords::KEY_END; + } + else + { + next_keyword = Keywords::Keyword_search(key); + } + + free_check_null(token1); + if (next_keyword > 0) + { + return TRUE; + } + return (FALSE); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_units(std::string &tot_units, bool alkalinity, bool check_compatibility, + const char *default_units, bool print) +/* ---------------------------------------------------------------------- */ +{ +#define NUNITS (sizeof(units) / sizeof(char *)) +/* + * Check if legitimate units + * Input: + * tot_units character string to check, + * alkalinity TRUE if alkalinity, FALSE if any other total, + * check_compatibility TRUE check alk and default units, FALSE otherwise + * default_units character string of default units (check /L, /kg, etc) + * print TRUE print warning messages + * Output: + * tot_units standard form for unit + */ + const char *units[] = { + "Mol/l", /* 0 */ + "mMol/l", /* 1 */ + "uMol/l", /* 2 */ + "g/l", /* 3 */ + "mg/l", /* 4 */ + "ug/l", /* 5 */ + "Mol/kgs", /* 6 */ + "mMol/kgs", /* 7 */ + "uMol/kgs", /* 8 */ + "g/kgs", /* 9 = ppt */ + "mg/kgs", /* 10 = ppm */ + "ug/kgs", /* 11 = ppb */ + "Mol/kgw", /* 12 = mol/kg H2O */ + "mMol/kgw", /* 13 = mmol/kg H2O */ + "uMol/kgw", /* 14 = umol/kg H2O */ + "g/kgw", /* 15 = mol/kg H2O */ + "mg/kgw", /* 16 = mmol/kg H2O */ + "ug/kgw", /* 17 = umol/kg H2O */ + "eq/l", /* 18 */ + "meq/l", /* 19 */ + "ueq/l", /* 20 */ + "eq/kgs", /* 21 */ + "meq/kgs", /* 22 */ + "ueq/kgs", /* 23 */ + "eq/kgw", /* 24 */ + "meq/kgw", /* 25 */ + "ueq/kgw", /* 26 */ + }; + Utilities::squeeze_white(tot_units); + Utilities::str_tolower(tot_units); + replace("milli", "m", tot_units); + replace("micro", "u", tot_units); + replace("grams", "g", tot_units); + replace("gram", "g", tot_units); + replace("moles", "Mol", tot_units); + replace("mole", "Mol", tot_units); + replace("mol", "Mol", tot_units); + replace("liter", "l", tot_units); + replace("kgh", "kgw", tot_units); + replace("ppt", "g/kgs", tot_units); + replace("ppm", "mg/kgs", tot_units); + replace("ppb", "ug/kgs", tot_units); + replace("equivalents", "eq", tot_units); + replace("equivalent", "eq", tot_units); + replace("equiv", "eq", tot_units); + + size_t pos; + if ((pos = tot_units.find("/l")) != std::string::npos) + { + tot_units = tot_units.substr(0, pos + 2); + } + else if ((pos = tot_units.find("/kgs")) != std::string::npos) + { + tot_units = tot_units.substr(0, pos + 4); + } + else if ((pos = tot_units.find("/kgw")) != std::string::npos) + { + tot_units = tot_units.substr(0, pos + 4); + } +/* + * Check if unit in list + */ + bool found = false; + size_t i; + for (i = 0; i < NUNITS; i++) + { + if (strcmp(tot_units.c_str(), units[i]) == 0) + { + found = true; + break; + } + } + if (!found) + { + if (print) + { + error_string = sformatf( "Unknown unit, %s.", tot_units.c_str()); + error_msg(error_string, CONTINUE); + } + return (ERROR); + } + +/* + * Check if units are compatible with default_units + */ + if (!check_compatibility) + return (OK); +/* + * Special cases for alkalinity + */ + if (alkalinity && strstr(tot_units.c_str(), "Mol") != NULL) + { + if (print == TRUE) + { + error_string = sformatf( + "Alkalinity given in moles, assumed to be equivalents."); + warning_msg(error_string); + } + replace("Mol", "eq", tot_units); + } + if (!alkalinity && strstr(tot_units.c_str(), "eq") != NULL) + { + if (print) + { + error_msg("Only alkalinity can be entered in equivalents.", + CONTINUE); + } + return (ERROR); + } +/* + * See if default_units are compatible with tot_units + */ + if (strstr(default_units, "/l") && strstr(tot_units.c_str(), "/l")) + return (OK); + if (strstr(default_units, "/kgs") && strstr(tot_units.c_str(), "/kgs")) + return (OK); + if (strstr(default_units, "/kgw") && strstr(tot_units.c_str(), "/kgw")) + return (OK); + + std::string string = default_units; + Utilities::replace("kgs", "kg solution", string); + Utilities::replace("kgs", "kg solution", tot_units); + Utilities::replace("kgw", "kg water", string); + Utilities::replace("kgw", "kg water", tot_units); + Utilities::replace("/l", "/L", string); + Utilities::replace("Mol", "mol", string); + Utilities::replace("/l", "/L", tot_units); + Utilities::replace("Mol", "mol", tot_units); + if (print == TRUE) + { + error_string = sformatf( + "Units for master species, %s, are not compatible with default units, %s.", + tot_units.c_str(), string.c_str()); + error_msg(error_string, CONTINUE); + } + return (ERROR); +} +#ifdef SKIP +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_units(char *tot_units, int alkalinity, int check_compatibility, + const char *default_units, int print) +/* ---------------------------------------------------------------------- */ +{ +#define NUNITS (sizeof(units) / sizeof(char *)) +/* + * Check if legitimate units + * Input: + * tot_units character string to check, + * alkalinity TRUE if alkalinity, FALSE if any other total, + * check_compatibility TRUE check alk and default units, FALSE otherwise + * default_units character string of default units (check /L, /kg, etc) + * print TRUE print warning messages + * Output: + * tot_units standard form for unit + */ + int i, found; + char *end; + char string[MAX_LENGTH]; + const char *units[] = { + "Mol/l", /* 0 */ + "mMol/l", /* 1 */ + "uMol/l", /* 2 */ + "g/l", /* 3 */ + "mg/l", /* 4 */ + "ug/l", /* 5 */ + "Mol/kgs", /* 6 */ + "mMol/kgs", /* 7 */ + "uMol/kgs", /* 8 */ + "g/kgs", /* 9 = ppt */ + "mg/kgs", /* 10 = ppm */ + "ug/kgs", /* 11 = ppb */ + "Mol/kgw", /* 12 = mol/kg H2O */ + "mMol/kgw", /* 13 = mmol/kg H2O */ + "uMol/kgw", /* 14 = umol/kg H2O */ + "g/kgw", /* 15 = mol/kg H2O */ + "mg/kgw", /* 16 = mmol/kg H2O */ + "ug/kgw", /* 17 = umol/kg H2O */ + "eq/l", /* 18 */ + "meq/l", /* 19 */ + "ueq/l", /* 20 */ + "eq/kgs", /* 21 */ + "meq/kgs", /* 22 */ + "ueq/kgs", /* 23 */ + "eq/kgw", /* 24 */ + "meq/kgw", /* 25 */ + "ueq/kgw", /* 26 */ + }; + + squeeze_white(tot_units); + str_tolower(tot_units); + replace("milli", "m", tot_units); + replace("micro", "u", tot_units); + replace("grams", "g", tot_units); + replace("gram", "g", tot_units); + replace("moles", "Mol", tot_units); + replace("mole", "Mol", tot_units); + replace("mol", "Mol", tot_units); + replace("liter", "l", tot_units); + replace("kgh", "kgw", tot_units); + replace("ppt", "g/kgs", tot_units); + replace("ppm", "mg/kgs", tot_units); + replace("ppb", "ug/kgs", tot_units); + replace("equivalents", "eq", tot_units); + replace("equivalent", "eq", tot_units); + replace("equiv", "eq", tot_units); + + if ((end = strstr(tot_units, "/l")) != NULL) + { + *(end + 2) = '\0'; + } + if ((end = strstr(tot_units, "/kgs")) != NULL) + { + *(end + 4) = '\0'; + } + if ((end = strstr(tot_units, "/kgw")) != NULL) + { + *(end + 4) = '\0'; + } +/* + * Check if unit in list + */ + found = FALSE; + for (i = 0; i < (int) NUNITS; i++) + { + if (strcmp(tot_units, units[i]) == 0) + { + found = TRUE; + break; + } + } + if (found == FALSE) + { + if (print == TRUE) + { + error_string = sformatf( "Unknown unit, %s.", tot_units); + error_msg(error_string, CONTINUE); + } + return (ERROR); + } + +/* + * Check if units are compatible with default_units + */ + if (check_compatibility == FALSE) + return (OK); +/* + * Special cases for alkalinity + */ + if (alkalinity == TRUE && strstr(tot_units, "Mol") != NULL) + { + if (print == TRUE) + { + error_string = sformatf( + "Alkalinity given in moles, assumed to be equivalents."); + warning_msg(error_string); + } + replace("Mol", "eq", tot_units); + } + if (alkalinity == FALSE && strstr(tot_units, "eq") != NULL) + { + if (print == TRUE) + { + error_msg("Only alkalinity can be entered in equivalents.", + CONTINUE); + } + return (ERROR); + } +/* + * See if default_units are compatible with tot_units + */ + if (strstr(default_units, "/l") && strstr(tot_units, "/l")) + return (OK); + if (strstr(default_units, "/kgs") && strstr(tot_units, "/kgs")) + return (OK); + if (strstr(default_units, "/kgw") && strstr(tot_units, "/kgw")) + return (OK); + + strcpy(string, default_units); + replace("kgs", "kg solution", string); + replace("kgs", "kg solution", tot_units); + replace("kgw", "kg water", string); + replace("kgw", "kg water", tot_units); + replace("/l", "/L", string); + replace("Mol", "mol", string); + replace("/l", "/L", tot_units); + replace("Mol", "mol", tot_units); + if (print == TRUE) + { + error_string = sformatf( + "Units for master species, %s, are not compatible with default units, %s.", + tot_units, string); + error_msg(error_string, CONTINUE); + } + return (ERROR); +} +#endif +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +find_option(const char *item, int *n, const char **list, int count_list, int exact) +/* ---------------------------------------------------------------------- */ +{ +/* + * Compares a string value to match beginning letters of a list of options + * + * Arguments: + * item entry: pointer to string to compare + * n exit: item in list that was matched + * list entry: pointer to list of character values, assumed to + * be lower case + * count_list entry: number of character values in list + * + * Returns: + * OK item matched + * ERROR item not matched + * n -1 item not matched + * i position of match in list + */ + int i; + std::string stdtoken(item); + + Utilities::str_tolower(stdtoken); + for (i = 0; i < count_list; i++) + { + if (exact == TRUE) + { + if (strcmp(list[i], stdtoken.c_str()) == 0) + { + *n = i; + return (OK); + } + } + else + { + if (strstr(list[i], stdtoken.c_str()) == list[i]) + { + *n = i; + return (OK); + } + } + } + *n = -1; + return (ERROR); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_true_false(char *string, int default_value) +/* ---------------------------------------------------------------------- */ +{ +/* + * Returns true unless string starts with "F" or "f" + */ + int l; + char token[MAX_LENGTH]; + char *ptr; + + ptr = string; + + if (copy_token(token, &ptr, &l) == EMPTY) + { + return (default_value); + } + else + { + if (token[0] == 'F' || token[0] == 'f') + { + return (FALSE); + } + } + return (TRUE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_option(const char **opt_list, int count_opt_list, char **next_char) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read a line and check for options + */ + int j; + int opt; + char *opt_ptr; + std::string stdoption; +/* + * Read line + */ + j = check_line("get_option", FALSE, TRUE, TRUE, FALSE); + if (j == EOF) + { + j = OPTION_EOF; + } + else if (j == KEYWORD) + { + j = OPTION_KEYWORD; + } + else if (j == OPTION) + { + opt_ptr = line; + copy_token(stdoption, &opt_ptr); + if (find_option(&(stdoption.c_str()[1]), &opt, opt_list, count_opt_list, FALSE) == OK) + { + j = opt; + replace(stdoption.c_str(), opt_list[j], line_save); + replace(stdoption.c_str(), opt_list[j], line); + opt_ptr = line; + copy_token(stdoption, &opt_ptr); + *next_char = opt_ptr; + if (pr.echo_input == TRUE) + { + if (!reading_database()) + output_msg(sformatf( "\t%s\n", line_save)); + } + } + else + { + if (!reading_database()) + output_msg(sformatf( "\t%s\n", line_save)); + error_msg("Unknown option.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + j = OPTION_ERROR; + *next_char = line; + } + } + else + { + opt_ptr = line; + copy_token(stdoption, &opt_ptr); + if (find_option(&(stdoption.c_str()[0]), &opt, opt_list, count_opt_list, TRUE) == OK) + { + j = opt; + *next_char = opt_ptr; + } + else + { + j = OPTION_DEFAULT; + *next_char = line; + } + if (pr.echo_input == TRUE) + { + if (!reading_database()) + output_msg(sformatf( "\t%s\n", line_save)); + } + } + return (j); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_rates(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads basic code with which to calculate rates + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + char *ptr; + int l, length, line_length, n; + int return_value, opt, opt_save; + char token[MAX_LENGTH]; + struct rate *rate_ptr; + char *description; + int n_user, n_user_end; + char *next_char; + const char *opt_list[] = { + "start", /* 0 */ + "end" /* 1 */ + }; + int count_opt_list = 2; +/* + * Read advection number (not currently used) + */ + n = -1; + ptr = line; + read_number_description(ptr, &n_user, &n_user_end, &description); + description = (char *) free_check_null(description); + opt_save = OPTION_DEFAULT; +/* + * Read lines + */ + return_value = UNKNOWN; + rate_ptr = NULL; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in RATES keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* start */ + opt_save = OPT_1; + break; + case 1: /* end */ + opt_save = OPTION_DEFAULT; + break; + case OPTION_DEFAULT: /* read rate name */ + ptr = line; + copy_token(token, &ptr, &l); + { + const char *name = string_hsave(token); + rate_ptr = rate_search(name, &n); + } + if (rate_ptr == NULL) + { + rates = + (struct rate *) PHRQ_realloc(rates, + (size_t) (count_rates + + 1) * + sizeof(struct rate)); + if (rates == NULL) + malloc_error(); + rate_ptr = &rates[count_rates]; + count_rates++; + } + else + { + rate_free(rate_ptr); + } + rate_ptr->new_def = TRUE; + rate_ptr->commands = (char *) PHRQ_malloc(sizeof(char)); + if (rate_ptr->commands == NULL) + { + malloc_error(); + } + else + { + rate_ptr->commands[0] = '\0'; + rate_ptr->name = string_hsave(token); + rate_ptr->linebase = NULL; + rate_ptr->varbase = NULL; + rate_ptr->loopbase = NULL; + } + opt_save = OPT_1; + break; + case OPT_1: /* read command */ + if (rate_ptr == NULL) + { + input_error++; + error_string = sformatf( "No rate name has been defined."); + error_msg(error_string, CONTINUE); + opt_save = OPT_1; + break; + } + length = (int) strlen(rate_ptr->commands); + line_length = (int) strlen(line); + rate_ptr->commands = + (char *) PHRQ_realloc(rate_ptr->commands, + (size_t) (length + line_length + + 2) * sizeof(char)); + if (rate_ptr->commands == NULL) + { + malloc_error(); + } + else + { + rate_ptr->commands[length] = ';'; + rate_ptr->commands[length + 1] = '\0'; + strcat((rate_ptr->commands), line); + } + opt_save = OPT_1; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } +/* output_msg(sformatf( "%s", rates[0].commands)); + */ + rates_map.clear(); + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_user_print(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads basic code with which to calculate rates + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int length, line_length; + int return_value, opt, opt_save; + char *next_char; + const char *opt_list[] = { + "start", /* 0 */ + "end" /* 1 */ + }; + int count_opt_list = 2; + opt_save = OPTION_DEFAULT; +/* + * Read lines + */ + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in USER_PRINT keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* start */ + opt_save = OPTION_DEFAULT; + break; + case 1: /* end */ + opt_save = OPTION_DEFAULT; + break; + case OPTION_DEFAULT: /* read first command */ + rate_free(user_print); + user_print->new_def = TRUE; + user_print->commands = (char *) PHRQ_malloc(sizeof(char)); + if (user_print->commands == NULL) + malloc_error(); + user_print->commands[0] = '\0'; + user_print->linebase = NULL; + user_print->varbase = NULL; + user_print->loopbase = NULL; + user_print->name = + string_hsave("user defined Basic print routine"); + case OPT_1: /* read command */ + length = (int) strlen(user_print->commands); + line_length = (int) strlen(line); + user_print->commands = + (char *) PHRQ_realloc(user_print->commands, + (size_t) (length + line_length + + 2) * sizeof(char)); + if (user_print->commands == NULL) + malloc_error(); + user_print->commands[length] = ';'; + user_print->commands[length + 1] = '\0'; + strcat((user_print->commands), line); + opt_save = OPT_1; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } +/* output_msg(sformatf( "%s", rates[0].commands)); + */ return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_user_punch(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads basic code with which to calculate rates + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int length, line_length; + int return_value, opt, opt_save; + std::string stdtoken; + char *next_char; + const char *opt_list[] = { + "start", /* 0 */ + "end", /* 1 */ + "heading", /* 2 */ + "headings" /* 3 */ + }; + int count_opt_list = 4; + opt_save = OPTION_DEFAULT; +/* + * Read lines + */ + + int n_user, n_user_end; + char *description; + char *ptr; + ptr = line; + read_number_description(ptr, &n_user, &n_user_end, &description); + + UserPunch temp_user_punch; + temp_user_punch.Set_PhreeqcPtr(this); + temp_user_punch.Set_n_user(n_user); + temp_user_punch.Set_n_user_end(n_user_end); + temp_user_punch.Set_description(description); + free_check_null(description); + + //std::map < int, UserPunch >::iterator up = UserPunch_map.find(n_user); + //if (up != UserPunch_map.end()) + //{ + // UserPunch_map.erase(up); + //} + + // Malloc rate structure + struct rate *r = (struct rate *) PHRQ_malloc(sizeof(struct rate)); + if (r == NULL) malloc_error(); + r->commands = NULL; + r->new_def = TRUE; + r->linebase = NULL; + r->varbase = NULL; + r->loopbase = NULL; + r->name = string_hsave("user defined Basic punch routine"); + + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in USER_PUNCH keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* start */ + opt_save = OPTION_DEFAULT; + break; + case 1: /* end */ + opt_save = OPTION_DEFAULT; + break; + case 2: /* headings */ + case 3: /* heading */ + while (copy_token(stdtoken, &next_char) != EMPTY) + { + temp_user_punch.Get_headings().push_back(stdtoken); + } + break; + case OPTION_DEFAULT: /* read first command */ + { + r->commands = (char *) PHRQ_malloc(sizeof(char)); + if (r->commands == NULL) malloc_error(); + else r->commands[0] = '\0'; + } + //rate_free(user_punch); + //user_punch->new_def = TRUE; + //user_punch->commands = (char *) PHRQ_malloc(sizeof(char)); + //if (user_punch->commands == NULL) + // malloc_error(); + //user_punch->commands[0] = '\0'; + //user_punch->linebase = NULL; + //user_punch->varbase = NULL; + //user_punch->loopbase = NULL; + //user_punch->name = + // string_hsave("user defined Basic punch routine"); + case OPT_1: /* read command */ + length = (int) strlen(r->commands); + line_length = (int) strlen(line); + r->commands = (char *) PHRQ_realloc(r->commands, + (size_t) (length + line_length + 2) * sizeof(char)); + if (r->commands == NULL) + { + malloc_error(); + } + else + { + r->commands[length] = ';'; + r->commands[length + 1] = '\0'; + strcat((r->commands), line); + } + //length = (int) strlen(user_punch->commands); + //line_length = (int) strlen(line); + //user_punch->commands = + // (char *) PHRQ_realloc(user_punch->commands, + // (size_t) (length + line_length + + // 2) * sizeof(char)); + //if (user_punch->commands == NULL) + // malloc_error(); + //user_punch->commands[length] = ';'; + //user_punch->commands[length + 1] = '\0'; + //strcat((user_punch->commands), line); + opt_save = OPT_1; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + UserPunch_map.erase(n_user); + UserPunch_map[n_user] = temp_user_punch; + UserPunch_map[n_user].Set_rate(r); + + return (return_value); +} +#ifdef SKIP +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_user_punch(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads basic code with which to calculate rates + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int length, line_length; + int return_value, opt, opt_save; + std::string stdtoken; + char *next_char; + const char *opt_list[] = { + "start", /* 0 */ + "end", /* 1 */ + "heading", /* 2 */ + "headings" /* 3 */ + }; + int count_opt_list = 4; + + opt_save = OPTION_DEFAULT; +/* + * Read lines + */ + user_punch_count_headings = 0; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in USER_PUNCH keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* start */ + opt_save = OPTION_DEFAULT; + break; + case 1: /* end */ + opt_save = OPTION_DEFAULT; + break; + case 2: /* headings */ + case 3: /* heading */ + while (copy_token(stdtoken, &next_char) != EMPTY) + { + user_punch_headings = + (const char **) PHRQ_realloc(user_punch_headings, + (size_t) + (user_punch_count_headings + + 1) * sizeof(char *)); + if (user_punch_headings == NULL) + malloc_error(); + user_punch_headings[user_punch_count_headings] = + string_hsave(stdtoken.c_str()); + user_punch_count_headings++; + } + break; + case OPTION_DEFAULT: /* read first command */ + rate_free(user_punch); + user_punch->new_def = TRUE; + user_punch->commands = (char *) PHRQ_malloc(sizeof(char)); + if (user_punch->commands == NULL) + malloc_error(); + user_punch->commands[0] = '\0'; + user_punch->linebase = NULL; + user_punch->varbase = NULL; + user_punch->loopbase = NULL; + user_punch->name = + string_hsave("user defined Basic punch routine"); + case OPT_1: /* read command */ + length = (int) strlen(user_punch->commands); + line_length = (int) strlen(line); + user_punch->commands = + (char *) PHRQ_realloc(user_punch->commands, + (size_t) (length + line_length + + 2) * sizeof(char)); + if (user_punch->commands == NULL) + malloc_error(); + user_punch->commands[length] = ';'; + user_punch->commands[length + 1] = '\0'; + strcat((user_punch->commands), line); + opt_save = OPT_1; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} +#endif +#if defined PHREEQ98 +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_user_graph(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads basic code with which to calculate rates + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int l, length, line_length, CurveInfonr = 0; + int return_value, opt, opt_save; + char file_name[MAX_LENGTH]; + char token[MAX_LENGTH]; + char *next_char; + const char *opt_list[] = { + "start", /* 0 */ + "end", /* 1 */ + "heading", /* 2 */ + "headings", /* 3 */ + "chart_title", /* 4 */ + "axis_titles", /* 5 */ + "axis_scale", /* 6 */ + "initial_solutions", /* 7 */ + "plot_concentration_vs", /* 8 */ + "shifts_as_points", /* 9 */ + "grid_offset", /* 10 */ + "connect_simulations", /* 11 */ + "plot_csv_file" /* 12 */ + "plot_tsv_file" /* 13 */ + }; + int count_opt_list = 14; + int i; + opt_save = OPTION_DEFAULT; +/* + * Read lines + */ +#ifdef PHREEQ98 + user_graph_count_headings = 0; +#endif + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) opt = opt_save; + + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in USER_GRAPH keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* start */ + opt_save = OPTION_DEFAULT; + break; + case 1: /* end */ + opt_save = OPTION_DEFAULT; + break; + case 2: /* headings */ + case 3: /* heading */ + while (copy_token(token, &next_char, &l) != EMPTY) + { + user_graph_headings = + (char **) PHRQ_realloc(user_graph_headings, + (size_t) (user_graph_count_headings + + 1) * sizeof(char *)); + if (user_graph_headings == NULL) + malloc_error(); + user_graph_headings[user_graph_count_headings] = + string_hsave(token); + user_graph_count_headings++; + } + break; +/*Modifications of read_user_punch to change the chart's appearance */ + case 4: /* chart title */ + copy_title(token, &next_char, &l); + SetChartTitle(token); + break; + case 5: /* axis titles */ + i = 0; + while (copy_title(token, &next_char, &l) != EMPTY) + { + SetAxisTitles(token, i); + i++; + } + break; + case 6: { /* axis scales */ + char *axis = ""; + int j = 0; + float f_min, f_max; f_min = (float) -9.999; + prev_next_char = next_char; + copy_token(token, &next_char, &l); + str_tolower(token); + if (strstr(token, "x") == token) + axis = "x"; + else if ((strstr(token, "y") == token) && (strstr(token, "y2") != token)) + axis = "y"; + else if ((strstr(token, "s") == token) || (strstr(token, "y2") == token)) + axis = "s"; + else + { + input_error++; + copy_token(token, &prev_next_char, &l); + error_string = sformatf( + "Found '%s', but expect axis type \'x\', \'y\', or \'sy\'.", token); + error_msg(error_string, CONTINUE); + } + while ((j < 4) + && (i = copy_token(token, &next_char, &l)) != EMPTY) + { + str_tolower(token); + if ((i == DIGIT) || (strstr(token, "a") == token)) + SetAxisScale(axis, j, token, FALSE); + else + { + input_error++; + copy_token(token, &prev_next_char, &l); + error_string = sformatf( + "Found '%s', but expect number or 'a(uto)'.", token); + error_msg(error_string, CONTINUE); + } + if (i == DIGIT) + { + if (j == 0) + f_min = (float) atof(token); + else if (j == 1 && fabs(f_min + 9.999) > 1e-3) + { + f_max = (float) atof(token); + if (f_min > f_max) + { + error_string = sformatf( + "Maximum must be larger than minimum of axis_scale, interchanging both for %s axis", axis); + warning_msg(error_string); + SetAxisScale(axis, 0, token, FALSE); + sprintf(token, "%e", f_min); + SetAxisScale(axis, 1, token, FALSE); + } + } + } + j++; /* counter for categories */ + prev_next_char = next_char; + } + if (j == 4) + SetAxisScale(axis, j, 0, + get_true_false(next_char, FALSE)); /* anything else than false turns on log scale */ + } + break; + case 7: + graph_initial_solutions = get_true_false(next_char, FALSE); + break; + case 8: + prev_next_char = next_char; + copy_token(token, &next_char, &l); + str_tolower(token); + if (strstr(token, "x") == token || strstr(token, "d") == token) + chart_type = 0; + else if (strstr(token, "t") == token) + chart_type = 1; + else + { + input_error++; + copy_token(token, &prev_next_char, &l); + error_string = sformatf( + "Found '%s', but expect plot type: (\'x\' or \'dist\') for distance, (\'t\') for time.", + token); + error_msg(error_string, CONTINUE); + } + break; + case 9: + shifts_as_points = get_true_false(next_char, TRUE); + if (shifts_as_points == TRUE) + chart_type = 0; + else + chart_type = 1; + break; + case 10: +#ifdef PHREEQ98 + i = copy_token(token, &next_char, &l); + str_tolower(token); + if (i == DIGIT) + sscanf(token, "%d", &RowOffset); + i = copy_token(token, &next_char, &l); + str_tolower(token); + if (i == DIGIT) + sscanf(token, "%d", &ColumnOffset); +#endif + break; + case 11: + connect_simulations = get_true_false(next_char, TRUE); + break; + case 12: + string_trim(next_char); + strcpy(file_name, next_char); + if (!OpenCSVFile(file_name)) + { + error_string = sformatf( "Can`t open file, %s. Give the full path + name, or copy the file to the working directory.", file_name); + input_error++; + error_msg(error_string, CONTINUE); + } + break; + /* End of modifications */ + case OPTION_DEFAULT: /* read first command */ + rate_free(user_graph); + user_graph->new_def = TRUE; + user_graph->commands = (char *) PHRQ_malloc(sizeof(char)); + if (user_graph->commands == NULL) + malloc_error(); + user_graph->commands[0] = '\0'; + user_graph->linebase = NULL; + user_graph->varbase = NULL; + user_graph->loopbase = NULL; + user_graph->name = + string_hsave("user defined Basic graph routine"); + case OPT_1: /* read command */ + length = strlen(user_graph->commands); + line_length = strlen(line); + user_graph->commands = + (char *) PHRQ_realloc(user_graph->commands, + (size_t) (length + line_length + + 2) * sizeof(char)); + if (user_graph->commands == NULL) + malloc_error(); + user_graph->commands[length] = ';'; + user_graph->commands[length + 1] = '\0'; + strcat((user_graph->commands), line); + opt_save = OPT_1; + break; + } + + if (return_value == EOF || return_value == KEYWORD) + break; + } +#ifdef PHREEQ98 + for (i = 0; i < user_graph_count_headings; i++) + { + GridHeadings(user_graph_headings[i], i); + } +#endif + return (return_value); +} +#endif +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_solid_solutions(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads solid solution data + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int n_user, n_user_end; + char *ptr; + char *description; + std::string token; + + int return_value, opt; + char *next_char; + const char *opt_list[] = { + "component", /* 0 */ + "comp", /* 1 */ + "parms", /* 2 */ + "gugg_nondimensional", /* 3 */ + "gugg_kj", /* 4 */ + "activity_coefficients", /* 5 */ + "distribution_coefficients", /* 6 */ + "miscibility_gap", /* 7 */ + "spinodal_gap", /* 8 */ + "critical_point", /* 9 */ + "alyotropic_point", /* 10 */ + "temp", /* 11 */ + "tempk", /* 12 */ + "tempc", /* 13 */ + "thompson", /* 14 */ + "margules", /* 15 */ + "comp1", /* 16 */ + "comp2" /* 17 */ + }; + int count_opt_list = 18; +/* + * Read ss_assemblage number + */ + ptr = line; + read_number_description(ptr, &n_user, &n_user_end, &description); + cxxSSassemblage temp_ss_assemblage; + temp_ss_assemblage.Set_n_user(n_user); + temp_ss_assemblage.Set_n_user_end(n_user_end); + temp_ss_assemblage.Set_description(description); + free_check_null(description); + temp_ss_assemblage.Set_new_def(true); + + std::vector comps; + cxxSScomp * comp0_ptr = NULL; + cxxSScomp * comp1_ptr = NULL; + cxxSS * ss_ptr = NULL; +/* + * Set use data to first read + */ + if (!use.Get_ss_assemblage_in()) + { + use.Set_ss_assemblage_in(true); + use.Set_n_ss_assemblage_user(n_user); + } +/* + * Read solid solutions + */ + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SOLID_SOLUTIONS keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; +/* + * New component + */ + case 0: /* component */ + case 1: /* comp */ + { + cxxSScomp comp; + /* + * Read phase name of component + */ + ptr = next_char; + copy_token(token, &ptr); + comp.Set_name(token); + /* + * Read moles of component + */ + + if (copy_token(token, &ptr) == EMPTY) + { + comp.Set_moles(NAN); + } + else + { + int j = sscanf(token.c_str(), SCANFORMAT, &dummy); + comp.Set_moles(dummy); + if (j != 1) + { + error_msg("Expected moles of solid solution.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + } + comps.push_back(comp); + } + break; + case 2: /* parms */ + case 3: /* gugg_nondimensional */ + /* + * Read parameters + */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + ptr = next_char; + if (copy_token(token, &ptr) != EMPTY) + { + sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p()[0] = dummy; + } + if (copy_token(token, &ptr) != EMPTY) + { + sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p()[1] = dummy; + } + ss_ptr->Set_input_case(cxxSS::SS_PARM_A0_A1); + break; + case 4: /* gugg_kj */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + ptr = next_char; + if (copy_token(token, &ptr) != EMPTY) + { + sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p()[0] = dummy; + } + if (copy_token(token, &ptr) != EMPTY) + { + sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p()[1] = dummy; + } + ss_ptr->Set_input_case(cxxSS::SS_PARM_DIM_GUGG); + break; + case 5: /* activity coefficients */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + ptr = next_char; + ss_ptr->Get_p().clear(); + for (int i = 0; i < 4; i++) + { + if (copy_token(token, &ptr) != EMPTY) + { + sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p().push_back(dummy); + } + } + if (ss_ptr->Get_p().size() != 4) + { + error_string = sformatf( + "Expected 4 parameters to calculate a0 and a1 from two activity coefficients, assemblage %d, solid solution %s", + n_user, + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + ss_ptr->Set_input_case(cxxSS::SS_PARM_GAMMAS); + break; + case 6: /* distribution coefficients */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + ptr = next_char; + ss_ptr->Get_p().clear(); + for (int i = 0; i < 4; i++) + { + if (copy_token(token, &ptr) != EMPTY) + { + sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p().push_back(dummy); + } + } + if (ss_ptr->Get_p().size() != 4) + { + error_string = sformatf( + "Expected 4 parameters to calculate a0 and a1 from two distribution coefficients, assemblage %d, solid solution %s", + n_user, + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + ss_ptr->Set_input_case(cxxSS::SS_PARM_DIST_COEF); + break; + case 7: /* miscibility_gap */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + ptr = next_char; + ss_ptr->Get_p().clear(); + for (int i = 0; i < 2; i++) + { + if (copy_token(token, &ptr) != EMPTY) + { + sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p().push_back(dummy); + } + } + if (ss_ptr->Get_p().size() != 2) + { + error_string = sformatf( + "Expected 2 miscibility gap fractions of component 2 to calculate a0 and a1, assemblage %d, solid solution %s", + n_user, + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + ss_ptr->Set_input_case(cxxSS::SS_PARM_MISCIBILITY); + break; + case 8: /* spinodal_gap */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + ptr = next_char; + ss_ptr->Get_p().clear(); + for (int i = 0; i < 2; i++) + { + if (copy_token(token, &ptr) != EMPTY) + { + sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p().push_back(dummy); + } + } + if (ss_ptr->Get_p().size() != 2) + { + error_string = sformatf( + "Expected 2 spinodal gap fractions of component 2 to calculate a0 and a1, assemblage %d, solid solution %s", + n_user, + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + ss_ptr->Set_input_case(cxxSS::SS_PARM_SPINODAL); + break; + case 9: /* critical point */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + ptr = next_char; + ss_ptr->Get_p().clear(); + for (int i = 0; i < 2; i++) + { + if (copy_token(token, &ptr) != EMPTY) + { + sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p().push_back(dummy); + } + } + if (ss_ptr->Get_p().size() != 2) + { + error_string = sformatf( + "Expected fraction of component 2 and critical temperature to calculate a0 and a1, assemblage %d, solid solution %s", + n_user, + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + ss_ptr->Set_input_case(cxxSS::SS_PARM_CRITICAL); + break; + case 10: /* alyotropic point */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + ptr = next_char; + ss_ptr->Get_p().clear(); + for (int i = 0; i < 2; i++) + { + if (copy_token(token, &ptr) != EMPTY) + { + sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p().push_back(dummy); + } + } + if (ss_ptr->Get_p().size() != 2) + { + error_string = sformatf( + "Expected fraction of component 2 and sigma pi at alyotropic point to calculate a0 and a1, assemblage %d, solid solution %s", + n_user, + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + ss_ptr->Set_input_case(cxxSS::SS_PARM_ALYOTROPIC); + break; + case 12: /* tempk */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + { + ptr = next_char; + int j = 0; + if (copy_token(token, &ptr) != EMPTY) + { + j = sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Set_tk(dummy); + } + if (j != 1) + { + error_string = sformatf( + "Expected temperature (Kelvin) for parameters, assemblage %d, solid solution %s, using 298.15 K", + n_user, + ss_ptr->Get_name().c_str()); + warning_msg(error_string); + ss_ptr->Set_tk(298.15); + } + } + break; + case 11: /* temp */ + case 13: /* tempc */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + { + ptr = next_char; + int j = 0; + if (copy_token(token, &ptr) != EMPTY) + { + j = sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Set_tk(dummy + 298.15); + } + if (j != 1) + { + error_string = sformatf( + "Expected temperature (Celcius) for parameters, assemblage %d, solid solution %s, using 25 C", + n_user, + ss_ptr->Get_name().c_str()); + warning_msg(error_string); + ss_ptr->Set_tk(298.15); + } + } + break; + case 14: /* Thompson and Waldbaum */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + ptr = next_char; + ss_ptr->Get_p().clear(); + for (int i = 0; i < 2; i++) + { + if (copy_token(token, &ptr) != EMPTY) + { + sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p().push_back(dummy); + } + } + if (ss_ptr->Get_p().size() != 2) + { + error_string = sformatf( + "Expected Wg2 and Wg1 Thompson-Waldbaum parameters to calculate a0 and a1, assemblage %d, solid solution %s", + n_user, + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + ss_ptr->Set_input_case(cxxSS::SS_PARM_WALDBAUM); + break; + case 15: /* Margules */ + if (!ss_ptr) + { + error_msg("Solid solution name has not been defined", CONTINUE); + break; + } + ptr = next_char; + ss_ptr->Get_p().clear(); + for (int i = 0; i < 2; i++) + { + if (copy_token(token, &ptr) != EMPTY) + { + sscanf(token.c_str(), SCANFORMAT, &dummy); + ss_ptr->Get_p().push_back(dummy); + } + } + if (ss_ptr->Get_p().size() != 2) + { + error_string = sformatf( + "Expected alpha2 and alpha3 Margules parameters to calculate a0 and a1, assemblage %d, solid solution %s", + n_user, + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + ss_ptr->Set_input_case(cxxSS::SS_PARM_MARGULES); + break; + case 16: /* comp1 */ + + /* + * Read phase name of component + */ + delete comp0_ptr; + comp0_ptr = new cxxSScomp; + ptr = next_char; + copy_token(token, &ptr); + comp0_ptr->Set_name(token); + /* + * Read moles of component + */ + if (copy_token(token, &ptr) == EMPTY) + { + comp0_ptr->Set_moles(NAN); + } + else + { + int j = sscanf(token.c_str(), SCANFORMAT, &dummy); + comp0_ptr->Set_moles(dummy); + if (j != 1) + { + error_msg("Expected moles of solid solution.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + } + break; + case 17: /* comp2 */ + delete comp1_ptr; + comp1_ptr = new cxxSScomp; + /* + * Read phase name of component + */ + ptr = next_char; + copy_token(token, &ptr); + comp1_ptr->Set_name(token); + /* + * Read moles of component + */ + if (copy_token(token, &ptr) == EMPTY) + { + comp1_ptr->Set_moles(NAN); + } + else + { + int j = sscanf(token.c_str(), SCANFORMAT, &dummy); + comp1_ptr->Set_moles(dummy); + if (j != 1) + { + error_msg("Expected moles of solid solution.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + } + break; +/* + * New solid solution + */ + case OPTION_DEFAULT: + if(ss_ptr) + { + if (comp1_ptr) + { + comps.insert(comps.begin(), *comp1_ptr); + } + if (comp0_ptr) + { + comps.insert(comps.begin(), *comp0_ptr); + } + ss_ptr->Set_ss_comps(comps); + temp_ss_assemblage.Get_SSs()[ss_ptr->Get_name()] = *ss_ptr; + delete ss_ptr; + ss_ptr = NULL; + comps.clear(); + delete comp0_ptr; + delete comp1_ptr; + comp0_ptr = comp1_ptr = NULL; + } + ss_ptr = new cxxSS; + /* + * Read solid solution name + */ + ptr = line; + copy_token(token, &ptr); + ss_ptr->Set_name(token); + ss_ptr->Set_total_moles(0.0); + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + + // add last ss and clean up + if (comp1_ptr) + comps.insert(comps.begin(), *comp1_ptr); + if (comp0_ptr) + comps.insert(comps.begin(), *comp0_ptr); + if (ss_ptr != NULL && comps.size() > 0) + { + ss_ptr->Set_ss_comps(comps); + } + if (ss_ptr != NULL && ss_ptr->Get_name().size() > 0) + { + temp_ss_assemblage.Get_SSs()[ss_ptr->Get_name()] = *ss_ptr; + } + delete ss_ptr; + ss_ptr = NULL; + comps.clear(); + delete comp0_ptr; + delete comp1_ptr; + comp0_ptr = comp1_ptr = NULL; + + // check non ideal ss + std::vector ss_v; + for (size_t i = 0; i < ss_v.size(); i++) + { + if (ss_v[i]->Get_p()[0] != 0.0 || + ss_v[i]->Get_p()[1] != 0.0) + { + if (ss_v[i]->Get_ss_comps().size() != 2) + { + error_string = sformatf( + "Solid solution, %s, is nonideal. Must define exactly two components (-comp1 and -comp2).", + ss_v[i]->Get_name().c_str()); + error_msg(error_string, CONTINUE); + input_error++; + } + } + } + + // Add to map + Rxn_ss_assemblage_map[n_user] = temp_ss_assemblage; + Rxn_new_ss_assemblage.insert(n_user); + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_llnl_aqueous_model_parameters(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads aqueous model parameters + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + int i, count_alloc; + char token[MAX_LENGTH]; + + int return_value, opt; + char *next_char; + const char *opt_list[] = { + "temperatures", /* 0 */ + "temperature", /* 1 */ + "temp", /* 2 */ + "adh", /* 3 */ + "debye_huckel_a", /* 4 */ + "dh_a", /* 5 */ + "bdh", /* 6 */ + "debye_huckel_b", /* 7 */ + "dh_b", /* 8 */ + "bdot", /* 9 */ + "b_dot", /* 10 */ + "c_co2", /* 11 */ + "co2_coefs" /* 12 */ + }; + int count_opt_list = 13; +/* + * Initialize + */ +/* + * Read aqueous model parameters + */ + return_value = UNKNOWN; + opt = get_option(opt_list, count_opt_list, &next_char); + for (;;) + { + next_char = line; + if (opt >= 0) + { + copy_token(token, &next_char, &i); + } + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_DEFAULT: + case OPTION_ERROR: + input_error++; + error_msg + ("Unknown input in LLNL_AQUEOUS_MODEL_PARAMETERS keyword.", + CONTINUE); + error_msg(line_save, CONTINUE); + break; + +/* + * New component + */ + case 0: /* temperatures */ + case 1: /* temperature */ + case 2: /* temp */ + count_alloc = 1; + llnl_count_temp = 0; + i = read_lines_doubles(next_char, &(llnl_temp), + &(llnl_count_temp), &(count_alloc), + opt_list, count_opt_list, &opt); + /* + ptr = next_char; + llnl_temp = read_list_doubles(&ptr, &count); + llnl_count_temp = count; + */ + break; + case 3: /* adh */ + case 4: /* debye_huckel_a */ + case 5: /* dh_a */ + count_alloc = 1; + llnl_count_adh = 0; + i = read_lines_doubles(next_char, &(llnl_adh), &(llnl_count_adh), + &(count_alloc), opt_list, count_opt_list, + &opt); + /* + ptr = next_char; + llnl_adh = read_list_doubles(&ptr, &count); + llnl_count_adh = count; + */ + break; + case 6: /* bdh */ + case 7: /* debye_huckel_b */ + case 8: /* dh_b */ + count_alloc = 1; + llnl_count_bdh = 0; + i = read_lines_doubles(next_char, &(llnl_bdh), &(llnl_count_bdh), + &(count_alloc), opt_list, count_opt_list, + &opt); + /* + ptr = next_char; + llnl_bdh = read_list_doubles(&ptr, &count); + llnl_count_bdh = count; + */ + break; + case 9: /* bdot */ + case 10: /* b_dot */ + count_alloc = 1; + llnl_count_bdot = 0; + i = read_lines_doubles(next_char, &(llnl_bdot), + &(llnl_count_bdot), &(count_alloc), + opt_list, count_opt_list, &opt); + /* + ptr = next_char; + llnl_bdot = read_list_doubles(&ptr, &count); + llnl_count_bdot = count; + */ + break; + case 11: /* c_co2 */ + case 12: /* co2_coefs */ + count_alloc = 1; + llnl_count_co2_coefs = 0; + i = read_lines_doubles(next_char, &(llnl_co2_coefs), + &(llnl_count_co2_coefs), &(count_alloc), + opt_list, count_opt_list, &opt); + /* + ptr = next_char; + llnl_co2_coefs = read_list_doubles(&ptr, &count); + llnl_count_co2_coefs = count; + */ + break; + } + return_value = check_line_return; + if (return_value == EOF || return_value == KEYWORD) + break; + } + /* check consistency */ + if ((llnl_count_temp <= 0) || + (llnl_count_temp != llnl_count_adh) || + (llnl_count_temp != llnl_count_bdh) || + (llnl_count_temp != llnl_count_bdot)) + { + error_msg + ("Must define equal number (>0) of temperatures, dh_a, dh_b, and bdot parameters\nin LLNL_AQUEOUS_MODEL", + CONTINUE); + input_error++; + } + if (llnl_count_co2_coefs != 5) + { + error_msg + ("Must define 5 CO2 activity coefficient parameters in LLNL_AQUEOUS_MODEL", + CONTINUE); + input_error++; + } + for (i = 1; i < llnl_count_temp; i++) + { + if (llnl_temp[i - 1] > llnl_temp[i]) + { + error_msg + ("Temperatures must be in ascending order in LLNL_AQUEOUS_MODEL", + CONTINUE); + input_error++; + } + } + + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_lines_doubles(char *next_char, LDBLE ** d, int *count_d, + int *count_alloc, const char **opt_list, + int count_opt_list, int *opt) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads LDBLEs on line starting at next_char + * and on succeeding lines. Appends to d. + * Stops at KEYWORD, OPTION, and EOF + * + * Input Arguments: + * next_char points to line to read from + * d points to array of LDBLEs, must be malloced + * count_d number of elements in array + * count_alloc number of elements malloced + * + * Output Arguments: + * d points to array of LDBLEs, may have been + * realloced + * count_d updated number of elements in array + * count_alloc updated of elements malloced + * + * Returns: + * KEYWORD + * OPTION + * EOF + * ERROR if any errors reading LDBLEs + */ + + if (read_line_doubles(next_char, d, count_d, count_alloc) == ERROR) + { + return (ERROR); + } + for (;;) + { + *opt = get_option(opt_list, count_opt_list, &next_char); + if (*opt == OPTION_KEYWORD || *opt == OPTION_EOF + || *opt == OPTION_ERROR) + { + break; + } + else if (*opt >= 0) + { + break; + } + next_char = line; + if (read_line_doubles(next_char, d, count_d, count_alloc) == ERROR) + { + return (ERROR); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_line_doubles(char *next_char, LDBLE ** d, int *count_d, int *count_alloc) +/* ---------------------------------------------------------------------- */ +{ + int i, j, l, n; + LDBLE value; + char token[MAX_LENGTH]; + + for (;;) + { + j = copy_token(token, &next_char, &l); + if (j == EMPTY) + { + break; + } + if (j != DIGIT) + { + return (ERROR); + } + if (replace("*", " ", token) == TRUE) + { + if (sscanf(token, "%d" SCANFORMAT, &n, &value) != 2) + { + return (ERROR); + } + } + else + { + sscanf(token, SCANFORMAT, &value); + n = 1; + } + for (;;) + { + if ((*count_d) + n > (*count_alloc)) + { + *count_alloc *= 2; + *d = (LDBLE *) PHRQ_realloc(*d, + (size_t) (*count_alloc) * + sizeof(LDBLE)); + if (*d == NULL) + malloc_error(); + } + else + { + break; + } + } + for (i = 0; i < n; i++) + { + (*d)[(*count_d) + i] = value; + } + *count_d += n; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +next_keyword_or_option(const char **opt_list, int count_opt_list) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads to next keyword or option or eof + * + * Returns: + * KEYWORD + * OPTION + * EOF + */ + int opt; + char *next_char; + + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_EOF) + { /* end of file */ + break; + } + else if (opt == OPTION_KEYWORD) + { /* keyword */ + break; + } + else if (opt >= 0 && opt < count_opt_list) + { + break; + } + else + { + error_msg("Expected a keyword or option.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + } + } + return (opt); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_named_logk(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads K's that can be used to calculate K's for species + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + + int l; + int i, empty; + struct logk *logk_ptr; + char token[MAX_LENGTH]; + + int return_value, opt, opt_save; + char *next_char; + const char *opt_list[] = { + "log_k", /* 0 */ + "logk", /* 1 */ + "delta_h", /* 2 */ + "deltah", /* 3 */ + "analytical_expression", /* 4 */ + "a_e", /* 5 */ + "ae", /* 6 */ + "ln_alpha1000", /* 7 */ + "add_logk", /* 8 */ + "add_log_k", /* 9 */ + "vm" /* 10 */ + }; + int count_opt_list = 11; + logk_ptr = NULL; +/* + * Read name followed by options + */ + opt_save = OPTION_DEFAULT; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + opt_save = OPTION_DEFAULT; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SPECIES keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* log_k */ + case 1: /* logk */ + if (logk_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_log_k_only(next_char, &logk_ptr->log_k[0]); + logk_copy2orig(logk_ptr); + opt_save = OPTION_DEFAULT; + break; + case 2: /* delta_h */ + case 3: /* deltah */ + if (logk_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_delta_h_only(next_char, &logk_ptr->log_k[1], + &logk_ptr->original_units); + logk_copy2orig(logk_ptr); + opt_save = OPTION_DEFAULT; + break; + case 4: /* analytical_expression */ + case 5: /* a_e */ + case 6: /* ae */ + if (logk_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_analytical_expression_only(next_char, &(logk_ptr->log_k[T_A1])); + logk_copy2orig(logk_ptr); + opt_save = OPTION_DEFAULT; + break; + case 7: /* ln_alpha1000 */ + if (logk_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + empty = TRUE; + for (i = T_A1; i <= T_A6; i++) + { + if (logk_ptr->log_k[i] != 0.0) + { + empty = FALSE; + logk_ptr->log_k[i] = 0.0; + } + } + if (empty == FALSE) + { + error_string = sformatf( + "Analytical expression previously defined for %s in NAMED_EXPRESSIONS\nAnalytical expression will be overwritten.", + logk_ptr->name); + warning_msg(error_string); + } + read_analytical_expression_only(next_char, &(logk_ptr->log_k[T_A1])); + for (i = T_A1; i < T_A6; i++) + { + logk_ptr->log_k[i] /= 1000. * LOG_10; + } + logk_copy2orig(logk_ptr); + opt_save = OPTION_DEFAULT; + break; + case 8: /* add_logk */ + case 9: /* add_log_k */ + if (logk_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + if (logk_ptr->count_add_logk == 0) + { + logk_ptr->add_logk = + (struct name_coef *) + PHRQ_malloc(sizeof(struct name_coef)); + if (logk_ptr->add_logk == NULL) + malloc_error(); + } + else + { + logk_ptr->add_logk = + (struct name_coef *) PHRQ_realloc(logk_ptr->add_logk, + (size_t) ((logk_ptr-> + count_add_logk + + + 1) * + sizeof + (struct + name_coef))); + if (logk_ptr->add_logk == NULL) + malloc_error(); + } + /* read name */ + if (copy_token(token, &next_char, &i) == EMPTY) + { + input_error++; + error_string = sformatf( + "Expected the name of a NAMED_EXPRESSION."); + error_msg(error_string, CONTINUE); + break; + } + logk_ptr->add_logk[logk_ptr->count_add_logk].name = + string_hsave(token); + /* read coef */ + i = sscanf(next_char, SCANFORMAT, + &logk_ptr->add_logk[logk_ptr->count_add_logk].coef); + if (i <= 0) + { + logk_ptr->add_logk[logk_ptr->count_add_logk].coef = 1; + } + logk_ptr->count_add_logk++; + opt_save = OPTION_DEFAULT; + break; + case 10: /* vm, molar volume */ + if (logk_ptr == NULL) + { + error_string = sformatf( + "No reaction defined before option, %s.", + opt_list[opt]); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + read_vm_only(next_char, &logk_ptr->log_k[vm0], + &logk_ptr->original_deltav_units); + logk_copy2orig(logk_ptr); + opt_save = OPTION_DEFAULT; + break; + case OPTION_DEFAULT: +/* + * Get space for logk information + */ + logk_ptr = NULL; + copy_token(token, &next_char, &l); + + logk_ptr = logk_store(token, TRUE); +/* + * Get pointer to each species in the reaction, store new species if necessary + */ + opt_save = OPTION_DEFAULT; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_copy(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads solution, + * equilibrium_phases, + * exchange, + * surface, + * solid_solution, + * gas_phase, + * kinetics, + * mix, + * reaction, + * reaction_temperature + * + */ + int i, l, n, n_user, n_user_start, n_user_end, return_value; + char *ptr; + char token[MAX_LENGTH], token1[MAX_LENGTH], nonkeyword[MAX_LENGTH]; +/* + * Read "copy" + */ + ptr = line; + copy_token(token, &ptr, &l); +/* + * Read keyword + */ + copy_token(token, &ptr, &l); + check_key(token); + + switch (next_keyword) + { + case Keywords::KEY_NONE: /* Have not read line with keyword */ + strcpy(nonkeyword, token); + break; + case Keywords::KEY_SOLUTION: /* Solution */ + case Keywords::KEY_EQUILIBRIUM_PHASES: /* Pure phases */ + case Keywords::KEY_REACTION: /* Reaction */ + case Keywords::KEY_MIX: /* Mix */ + case Keywords::KEY_EXCHANGE: /* Ex */ + case Keywords::KEY_SURFACE: /* Surface */ + case Keywords::KEY_REACTION_TEMPERATURE: /* Temperature */ + case Keywords::KEY_REACTION_PRESSURE: /* Pressure */ + case Keywords::KEY_GAS_PHASE: /* Gas */ + case Keywords::KEY_KINETICS: /* Kinetics */ + case Keywords::KEY_SOLID_SOLUTIONS: /* solid_solutions */ + break; + default: + input_error++; + error_msg + ("Expecting keyword solution, mix, kinetics, reaction, reaction_pressure, reaction_temperature, equilibrium_phases, exchange, surface, gas_phase, or solid_solutions, or cell.", + CONTINUE); + error_msg(line_save, CONTINUE); + check_line("End of use", FALSE, TRUE, TRUE, TRUE); + /* empty, eof, keyword, print */ + return (ERROR); + } +/* + * Read source index + */ + strcpy(token1, token); + i = copy_token(token, &ptr, &l); + if (i == DIGIT) + { + sscanf(token, "%d", &n_user); + //if (n_user < 0) + //{ + // error_msg("Source index number must be a positive integer.", + // CONTINUE); + // error_msg(line_save, CONTINUE); + // input_error++; + // return (ERROR); + //} + //if (strstr(token, "-") != NULL) + //{ + // error_msg + // ("COPY does not accept a range of numbers for source index", + // CONTINUE); + // error_msg(line_save, CONTINUE); + // input_error++; + // return (ERROR); + //} + } + else + { + error_msg("Source index number must be an integer.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + return (ERROR); + } +/* + * Read target index or range of indices + */ + i = copy_token(token, &ptr, &l); + if (i == DIGIT) + { + replace("-", " ", &token[1]); + n = sscanf(token, "%d%d", &n_user_start, &n_user_end); + if (n == 1) + { + n_user_end = n_user_start; + } + } + else + { + error_msg("Target index number must be an integer.", + CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + return (ERROR); + } + + switch (next_keyword) + { + case Keywords::KEY_NONE: + str_tolower(nonkeyword); + if (strstr(nonkeyword, "cell") != nonkeyword) + { + error_msg("Unknown input in COPY data block.", CONTINUE); + error_msg(line_save, CONTINUE); + input_error++; + return (ERROR); + } + copier_add(©_solution, n_user, n_user_start, n_user_end); + copier_add(©_pp_assemblage, n_user, n_user_start, n_user_end); + copier_add(©_reaction, n_user, n_user_start, n_user_end); + copier_add(©_mix, n_user, n_user_start, n_user_end); + copier_add(©_exchange, n_user, n_user_start, n_user_end); + copier_add(©_surface, n_user, n_user_start, n_user_end); + copier_add(©_temperature, n_user, n_user_start, n_user_end); + copier_add(©_pressure, n_user, n_user_start, n_user_end); + copier_add(©_gas_phase, n_user, n_user_start, n_user_end); + copier_add(©_kinetics, n_user, n_user_start, n_user_end); + copier_add(©_ss_assemblage, n_user, n_user_start, n_user_end); + break; + case Keywords::KEY_SOLUTION: /* Solution */ + copier_add(©_solution, n_user, n_user_start, n_user_end); + break; + case Keywords::KEY_EQUILIBRIUM_PHASES: /* Pure phases */ + copier_add(©_pp_assemblage, n_user, n_user_start, n_user_end); + break; + case Keywords::KEY_REACTION: /* Reaction */ + copier_add(©_reaction, n_user, n_user_start, n_user_end); + break; + case Keywords::KEY_MIX: /* Mix */ + copier_add(©_mix, n_user, n_user_start, n_user_end); + break; + case Keywords::KEY_EXCHANGE: /* Ex */ + copier_add(©_exchange, n_user, n_user_start, n_user_end); + break; + case Keywords::KEY_SURFACE: /* Surface */ + copier_add(©_surface, n_user, n_user_start, n_user_end); + break; + case Keywords::KEY_REACTION_TEMPERATURE: /* Temperature */ + copier_add(©_temperature, n_user, n_user_start, n_user_end); + break; + case Keywords::KEY_REACTION_PRESSURE: /* Pressure */ + copier_add(©_pressure, n_user, n_user_start, n_user_end); + break; + case Keywords::KEY_GAS_PHASE: /* Gas */ + copier_add(©_gas_phase, n_user, n_user_start, n_user_end); + break; + case Keywords::KEY_KINETICS: /* Kinetics */ + copier_add(©_kinetics, n_user, n_user_start, n_user_end); + break; + case Keywords::KEY_SOLID_SOLUTIONS: /* solid_solutions */ + copier_add(©_ss_assemblage, n_user, n_user_start, n_user_end); + break; + default: + error_msg("Error in switch for READ_COPY.", STOP); + break; + } + return_value = check_line("End of COPY", FALSE, TRUE, TRUE, TRUE); + /* empty, eof, keyword, print */ + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_reaction_pressure(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads REACTION_PRESSURE data block + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + assert(!reading_database()); + + // Make instance, set n_user, n_user_end, description + cxxPressure atm(this->phrq_io); + char *ptr = line; + char *description; + int n_user, n_user_end; + read_number_description(ptr, &n_user, &n_user_end, &description); + atm.Set_n_user(n_user); + atm.Set_n_user_end(n_user); + atm.Set_description(description); + free_check_null(description); + + /* + * Make parser + */ + CParser parser(this->phrq_io); + if (pr.echo_input == FALSE) parser.set_echo_file(CParser::EO_NONE); + atm.read(parser); + if (atm.Get_base_error_count() == 0) + { + Rxn_pressure_map[n_user] = atm; + } + + if (use.Get_pressure_in() == FALSE) + { + use.Set_pressure_in(true); + use.Set_n_pressure_user(atm.Get_n_user()); + } + + // Make copies if necessary + if (n_user_end > n_user) + { + int i; + for (i = n_user + 1; i <= n_user_end; i++) + { + Utilities::Rxn_copy(Rxn_pressure_map, n_user, i); + } + } + + return cleanup_after_parser(parser); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_reaction_pressure_raw(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads REACTION_PRESSURE_RAW data block + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + assert(!reading_database()); + + cxxPressure atm(this->phrq_io); + /* + * Make parser + */ + CParser parser(this->phrq_io); + if (pr.echo_input == FALSE) parser.set_echo_file(CParser::EO_NONE); + atm.read_raw(parser); + + // Store + if (atm.Get_base_error_count() == 0) + { + Rxn_pressure_map[atm.Get_n_user()] = atm; + } + + // Make copies if necessary + + Utilities::Rxn_copies(Rxn_pressure_map, atm.Get_n_user(), atm.Get_n_user_end()); + + + return cleanup_after_parser(parser); +} +int Phreeqc:: +cleanup_after_parser(CParser &parser) +{ + // check_key sets next_keyword + if (parser.get_m_line_type() == PHRQ_io::LT_EOF) + { + strcpy(line, ""); + strcpy(line_save, ""); + next_keyword = Keywords::KEY_END; + return(TRUE); + } + int return_value = check_key(parser.line().c_str()); + + // copy parser line to line and line_save + // make sure there is enough space + size_t l1 = strlen(parser.line().c_str()) + 1; + size_t l2 = strlen(parser.line_save().c_str()) + 1; + size_t l = (l1 > l2) ? l1 : l2; + if (l >= (size_t) max_line) + { + max_line = (int) l * 2; + line_save = (char *) PHRQ_realloc(line_save, + (size_t) max_line * sizeof(char)); + if (line_save == NULL) + malloc_error(); + line = (char *) PHRQ_realloc(line, (size_t) max_line * sizeof(char)); + if (line == NULL) + malloc_error(); + } + strcpy(line, parser.line().c_str()); + strcpy(line_save, parser.line_save().c_str()); + return return_value; +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_temperature(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads REACTION_TEMPERATURE data block + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + assert(!reading_database()); + + // Make instance, set n_user, n_user_end, description + cxxTemperature t_react(this->phrq_io); + char *ptr = line; + char *description; + int n_user, n_user_end; + read_number_description(ptr, &n_user, &n_user_end, &description); + t_react.Set_n_user(n_user); + t_react.Set_n_user_end(n_user); + t_react.Set_description(description); + free_check_null(description); + + /* + * Make parser + */ + CParser parser(this->phrq_io); + if (pr.echo_input == FALSE) parser.set_echo_file(CParser::EO_NONE); + t_react.read(parser); + if (t_react.Get_base_error_count() == 0) + { + Rxn_temperature_map[n_user] = t_react; + } + + if (use.Get_temperature_in() == FALSE) + { + use.Set_temperature_in(true); + use.Set_n_temperature_user(t_react.Get_n_user()); + } + + // Make copies if necessary + if (n_user_end > n_user) + { + int i; + for (i = n_user + 1; i <= n_user_end; i++) + { + Utilities::Rxn_copy(Rxn_temperature_map, n_user, i); + } + } + + return cleanup_after_parser(parser); +} diff --git a/phreeqcpp/readtr.cpp b/phreeqcpp/readtr.cpp new file mode 100644 index 00000000..f63d76d6 --- /dev/null +++ b/phreeqcpp/readtr.cpp @@ -0,0 +1,1405 @@ +#include /* std::cout std::cerr */ +#include +#include +#include "StorageBin.h" +#include "SS.h" +#ifndef boolean +typedef unsigned char boolean; +#endif +#include "Phreeqc.h" +#include "phqalloc.h" +#include "Utils.h" + + +#define OPTION_EOF -1 +#define OPTION_KEYWORD -2 +#define OPTION_ERROR -3 +#define OPTION_DEFAULT -4 +#define OPTION_DEFAULT2 -5 + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_transport(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Reads advection and column information + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + char *ptr; + int i, j, l; + int count_length, count_disp, count_punch, count_print, count_por; + int count_length_alloc, count_disp_alloc, count_por_alloc; + char token[MAX_LENGTH]; + char *description; + int n_user, n_user_end; + LDBLE *length, *disp, *pors; + int *punch_temp, *print_temp; + int return_value, opt, opt_save; + char *next_char, *next_char_save; + char file_name[MAX_LENGTH]; + + const char *opt_list[] = { + "cells", /* 0 */ + "shifts", /* 1 */ + "print", /* 2 */ + "selected_output", /* 3 */ + "bcond", /* 4 */ + "timest", /* 5 */ + "diffc", /* 6 */ + "tempr", /* 7 */ + "length", /* 8 */ + "disp", /* 9 */ + "punch", /* 10 */ + "stagnant", /* 11 */ + "bc", /* 12 */ + "boundary_conditions", /* 13 */ + "time_step", /* 14 */ + "temp_retardation_factor", /* 15 */ + "diffusion_coefficient", /* 16 */ + "dispersivity", /* 17 */ + "direction", /* 18 */ + "temperature_retardation_factor", /* 19 */ + "print_cells", /* 20 */ + "selected_cells", /* 21 */ + "flow_direction", /* 22 */ + "flow", /* 23 */ + "lengths", /* 24 */ + "dispersivities", /* 25 */ + "dump", /* 26 */ + "output", /* 27 */ + "output_frequency", /* 28 */ + "selected_output_frequency", /* 29 */ + "punch_cells", /* 30 */ + "dump_frequency", /* 31 */ + "dump_restart", /* 32 */ + "punch_frequency", /* 33 */ + "print_frequency", /* 34 */ + "correct_disp", /* 35 */ + "initial_time", /* 36 */ + "warning", /* 37 */ + "warnings", /* 38 */ + "thermal_diffusion", /* 39 */ + "multi_d", /* 40 */ + "interlayer_d", /* 41 */ + "porosities", /* 42 */ + "porosity", /* 43 */ + "fix_current", /* 44 */ + "current" /* 45 */ + }; + int count_opt_list = 46; + + strcpy(file_name, "phreeqc.dmp"); + /* + * Initialize + */ + simul_tr++; + if (simul_tr == 1) + { + correct_disp = FALSE; + old_cells = 0; + max_cells = 0; + all_cells = 0; + } + else + old_cells = count_cells; + count_length = count_disp = count_punch = count_print = count_por = 0; + + length = (LDBLE *)PHRQ_malloc(sizeof(LDBLE)); + if (length == NULL) + malloc_error(); + + disp = (LDBLE *)PHRQ_malloc(sizeof(LDBLE)); + if (disp == NULL) + malloc_error(); + + pors = (LDBLE *)PHRQ_malloc(sizeof(LDBLE)); + if (pors == NULL) + malloc_error(); + + punch_temp = (int *)PHRQ_malloc(sizeof(int)); + if (punch_temp == NULL) + malloc_error(); + + print_temp = (int *)PHRQ_malloc(sizeof(int)); + if (print_temp == NULL) + malloc_error(); + + count_length_alloc = count_disp_alloc = count_por_alloc = 1; + transport_start = 1; + /* + * Read transport number (not currently used) + */ + ptr = line; + read_number_description(ptr, &n_user, &n_user_end, &description); + description = (char *)free_check_null(description); + /* + * Set use data to last read + */ + use.Set_trans_in(true); + /* + * Read lines + */ + opt_save = OPTION_DEFAULT; + return_value = UNKNOWN; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + opt = opt_save; + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + case OPTION_DEFAULT: + input_error++; + error_msg("Unknown input in TRANSPORT keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* cells */ + sscanf(next_char, "%d", &count_cells); + opt_save = OPTION_DEFAULT; + break; + case 1: /* shifts */ + if (copy_token(token, &next_char, &l) == DIGIT) + sscanf(token, "%d", &count_shifts); + else + { + warning_msg + ("Expected the number of shifts. One shift is assumed."); + count_shifts = 1; + } + j = copy_token(token, &next_char, &l); + if (j != EMPTY) + { + if (j == DIGIT) + sscanf(token, "%d", &ishift); + else + { + input_error++; + error_msg + ("Expected shift direction, -1, 0, 1. Use -direction instead.", + CONTINUE); + ishift = 1; + } + } + opt_save = OPTION_DEFAULT; + break; + case 2: /* print */ + case 20: /* print_cells */ + print_temp = + read_list_ints_range(&next_char, &count_print, FALSE, + print_temp); + opt_save = 2; + break; + case 3: /* selected_output */ + case 29: /* selected_output_frequency */ + case 33: /* punch_frequency */ + sscanf(next_char, "%d", &punch_modulus); + opt_save = OPTION_DEFAULT; + if (punch_modulus <= 0) + { + error_string = sformatf( + "Punch frequency must be greater than 0. Frequency set to 1000."); + warning_msg(error_string); + punch_modulus = 1000; + } + + break; + case 4: /* bcond */ + case 12: /* bc */ + case 13: /* boundary_conditions */ + /* first cell boundary condition */ + i = copy_token(token, &next_char, &l); + str_tolower(token); + if (i == DIGIT) + { + sscanf(token, "%d", &bcon_first); + if (bcon_first < 1 || bcon_first > 3) + { + input_error++; + error_msg + ("Expected boundary condition to be 'constant' (1), 'closed' (2) , or 'flux' (3).", + CONTINUE); + } + } + else if (i == EMPTY) + bcon_first = 3; + else if (strstr(token, "co") == token) + bcon_first = 1; + else if (strstr(token, "cl") == token) + bcon_first = 2; + else if (strstr(token, "f") == token) + bcon_first = 3; + else + { + input_error++; + error_msg + ("Expected boundary condition to be 'constant', 'closed', or 'flux'.", + CONTINUE); + } + + /* last cell boundary condition */ + i = copy_token(token, &next_char, &l); + str_tolower(token); + if (i == DIGIT) + { + sscanf(token, "%d", &bcon_last); + if (bcon_last < 1 || bcon_last > 3) + { + input_error++; + error_msg + ("Expected boundary condition to be 'constant' (1), 'closed' (2) , or 'flux' (3).", + CONTINUE); + } + } + else if (i == EMPTY) + bcon_last = 3; + else if (strstr(token, "co") == token) + bcon_last = 1; + else if (strstr(token, "cl") == token) + bcon_last = 2; + else if (strstr(token, "f") == token) + bcon_last = 3; + else + { + input_error++; + error_msg + ("Expected boundary condition to be 'constant', 'closed', or 'flux'.", + CONTINUE); + } + opt_save = OPTION_DEFAULT; + break; + case 5: /* timest */ + case 14: /* time_step */ + if (copy_token(token, &next_char, &l) == DIGIT) + sscanf(token, SCANFORMAT, ×t); + { + std::string stdtoken; + j = copy_token(stdtoken, &next_char); + if (j == UPPER || j == LOWER) + { + timest = Utilities::convert_time(timest, stdtoken, "s"); + j = copy_token(stdtoken, &next_char); + } + if (j == DIGIT) + { + sscanf(stdtoken.c_str(), SCANFORMAT, &mcd_substeps); + } + } + //if (copy_token(token, &next_char, &l) == DIGIT) + // sscanf(token, SCANFORMAT, &mcd_substeps); + if (mcd_substeps < 1) + { + mcd_substeps = 1.0; + warning_msg("Substep factor in MCD must be >= 1.0\n" + "mcd_substeps = 1.0 assumed."); + } + opt_save = OPTION_DEFAULT; + break; + case 6: /* diffc */ + case 16: /* diffusion_coefficient */ + sscanf(next_char, SCANFORMAT, &diffc); + opt_save = OPTION_DEFAULT; + break; + case 7: /* tempr */ + case 15: /* temp_retardation_factor */ + case 19: /* temperature_retardation_factor */ + case 39: /* thermal_diffusion */ + if (copy_token(token, &next_char, &l) == DIGIT) + sscanf(token, SCANFORMAT, &tempr); + if (tempr < 1) + { + tempr = 1; + warning_msg + ("Temperature retardation factor < 1 is not possible.\n" + "Temperature retardation factor = 1 assumed."); + } + j = copy_token(token, &next_char, &l); + if (j == DIGIT) + sscanf(token, SCANFORMAT, &heat_diffc); + opt_save = OPTION_DEFAULT; + break; + case 8: /* length */ + case 24: /* lengths */ + if (read_line_LDBLEs + (next_char, &length, &count_length, + &count_length_alloc) == ERROR) + { + input_error++; + error_msg("Reading lengths in TRANSPORT keyword.\n", + CONTINUE); + } + opt_save = 8; + break; + case 9: /* disp */ + case 17: /* dispersivity */ + case 25: /* dispersivities */ + if (read_line_LDBLEs + (next_char, &disp, &count_disp, &count_disp_alloc) == ERROR) + { + input_error++; + error_msg("Reading dispersivities in TRANSPORT keyword.\n", + CONTINUE); + } + opt_save = 9; + break; + case 10: /* punch */ + case 21: /* selected_cells */ + case 30: /* punch_cells */ + punch_temp = + read_list_ints_range(&next_char, &count_punch, FALSE, + punch_temp); + opt_save = 10; + break; + case 11: /* stagnant */ + if (copy_token(token, &next_char, &l) != EMPTY) + { + /* exchange factor */ + if (sscanf(token, "%d", &(stag_data->count_stag)) != 1) + { + input_error++; + error_string = sformatf( + "Expecting number of stagnant layers."); + error_msg(error_string, CONTINUE); + break; + } + + /* exchange factor */ + j = copy_token(token, &next_char, &l); + if (j != EMPTY) + { + if (sscanf(token, SCANFORMAT, &(stag_data->exch_f)) != 1) + { + input_error++; + error_string = sformatf( + "Expecting exchange factor for stagnant layers."); + error_msg(error_string, CONTINUE); + break; + } + copy_token(token, &next_char, &l); + if (sscanf(token, SCANFORMAT, &(stag_data->th_m)) != 1) + { + input_error++; + error_string = sformatf( + "Expecting porosity in the mobile zone."); + error_msg(error_string, CONTINUE); + break; + } + copy_token(token, &next_char, &l); + if (sscanf(token, SCANFORMAT, &(stag_data->th_im)) != 1) + { + input_error++; + error_string = sformatf( + "Expecting porosity in the immobile zone."); + error_msg(error_string, CONTINUE); + break; + } + } + } + opt_save = OPTION_DEFAULT; + break; + case 18: /* direction */ + case 22: /* flow_direction */ + case 23: /* flow */ + copy_token(token, &next_char, &l); + str_tolower(token); + if (strstr(token, "f") == token) + ishift = 1; + else if (strstr(token, "b") == token) + ishift = -1; + else if (strstr(token, "d") == token) + ishift = 0; + else if (strstr(token, "n") == token) + ishift = 0; + else + { + input_error++; + error_msg + ("Expected flow direction to be 'forward', 'back', or 'no_flow'.", + CONTINUE); + } + opt_save = OPTION_DEFAULT; + break; + case 26: /* dump */ + dump_in = TRUE; + next_char_save = next_char; + if (copy_token(file_name, &next_char, &l) == EMPTY) + strcpy(file_name, "phreeqc.dmp"); + else + { + string_trim(next_char_save); + strcpy(file_name, next_char_save); + } + opt_save = OPTION_DEFAULT; + break; + case 27: /* output */ + case 28: /* output_frequency */ + case 34: /* print_frequency */ + sscanf(next_char, "%d", &print_modulus); + opt_save = OPTION_DEFAULT; + if (print_modulus <= 0) + { + error_string = sformatf( + "Print frequency must be greater than 0. Frequency set to 1000."); + warning_msg(error_string); + print_modulus = 1000; + } + break; + case 31: /* dump_frequency */ + dump_in = TRUE; + if (copy_token(token, &next_char, &l) == DIGIT) + sscanf(token, "%d", &dump_modulus); + else + { + warning_msg("Expected integer value for dump_frequency."); + dump_modulus = 0; + } + opt_save = OPTION_DEFAULT; + break; + case 32: /* dump_restart */ + dump_in = TRUE; + if (copy_token(token, &next_char, &l) == DIGIT) + sscanf(token, "%d", &transport_start); + else + { + warning_msg + ("Expected shift number to start calculations, 1 will be used."); + transport_start = 1; + } + opt_save = OPTION_DEFAULT; + break; + case 35: /* correct_dispersion */ + correct_disp = get_true_false(next_char, TRUE); + opt_save = OPTION_DEFAULT; + break; + case 36: /* initial_time */ + if (copy_token(token, &next_char, &l) == DIGIT) + sscanf(token, SCANFORMAT, &initial_total_time); + { + std::string stdtoken; + j = copy_token(stdtoken, &next_char); + if (j == UPPER || j == LOWER) + { + initial_total_time = Utilities::convert_time(initial_total_time, stdtoken, "s"); + } + } + opt_save = OPTION_DEFAULT; + break; + case 37: /* warning */ + case 38: /* warnings */ + transport_warnings = get_true_false(next_char, TRUE); + break; + case 40: /* multicomponent diffusion */ + copy_token(token, &next_char, &l); + str_tolower(token); + if (strstr(token, "f") == token) + multi_Dflag = 0; + else if (strstr(token, "t") == token) + multi_Dflag = 1; + else + { + input_error++; + error_msg + ("Expected multicomponent diffusion flag: 'true' or 'false'.", + CONTINUE); + } + default_Dw = 1e-9; + multi_Dpor = 0.3; + multi_Dpor_lim = 0.0; + multi_Dn = 1.0; + correct_Dw = 0; + if (copy_token(token, &next_char, &l) == EMPTY) + break; + else + { + /* default species diffusion coeff */ + if (sscanf(token, SCANFORMAT, &default_Dw) != 1) + { + input_error++; + error_string = sformatf( + "Expected default species diffusion coefficient in water at 25oC, m2/s."); + error_msg(error_string, CONTINUE); + break; + } + } + if (copy_token(token, &next_char, &l) == EMPTY) + break; + else + { + /* porosity */ + if (sscanf(token, SCANFORMAT, &multi_Dpor) != 1) + { + input_error++; + error_string = sformatf( + "Expected porosity to calculate diffusion coefficient."); + error_msg(error_string, CONTINUE); + break; + } + } + if (copy_token(token, &next_char, &l) == EMPTY) + break; + else + { + /* porosity */ + if (sscanf(token, SCANFORMAT, &multi_Dpor_lim) != 1) + { + input_error++; + error_string = sformatf( + "Expected porosity limit for diffusive transport."); + error_msg(error_string, CONTINUE); + break; + } + } + if (copy_token(token, &next_char, &l) == EMPTY) + break; + else + { + if (sscanf(token, SCANFORMAT, &multi_Dn) != 1) + { + input_error++; + error_string = sformatf( + "Expected exponent for porosity reduction of diffusion coefficient (Dp = Dw * (por)^n)."); + error_msg(error_string, CONTINUE); + break; + } + } + if (copy_token(token, &next_char, &l) == EMPTY) + break; + else + { + str_tolower(token); + if (strstr(token, "f") == token) + correct_Dw = 0; + else if (strstr(token, "t") == token) + correct_Dw = 1; + else + { + input_error++; + error_msg + ("Expected 'true' or 'false' for correcting Dw's as in Specific Conductance.", + CONTINUE); + } + } + opt_save = OPTION_DEFAULT; + break; + case 41: /* interlayer diffusion */ + copy_token(token, &next_char, &l); + str_tolower(token); + if (strstr(token, "f") == token) + interlayer_Dflag = 0; + else if (strstr(token, "t") == token) + interlayer_Dflag = 1; + else + { + input_error++; + error_msg + ("Expected interlayer diffusion flag: 'true' or 'false'.", + CONTINUE); + } + interlayer_Dpor = 0.1; + interlayer_Dpor_lim = 0.0; + interlayer_tortf = 100.0; + if (copy_token(token, &next_char, &l) == EMPTY) + break; + else + { + /* porosity */ + if (sscanf(token, SCANFORMAT, &interlayer_Dpor) != 1) + { + input_error++; + error_string = sformatf("Expected interlayer porosity."); + error_msg(error_string, CONTINUE); + break; + } + } + if (copy_token(token, &next_char, &l) == EMPTY) + break; + else + { + /* porosity limit */ + if (sscanf(token, SCANFORMAT, &interlayer_Dpor_lim) != 1) + { + input_error++; + error_string = sformatf( + "Expected interlayer porosity limit for diffusive transport."); + error_msg(error_string, CONTINUE); + break; + } + } + if (copy_token(token, &next_char, &l) == EMPTY) + break; + else + { + if (sscanf(token, SCANFORMAT, &interlayer_tortf) != 1) + { + input_error++; + error_string = sformatf( + "Expected interlayer tortuosity factor (Dp = Dw /t_f)."); + error_msg(error_string, CONTINUE); + break; + } + } + opt_save = OPTION_DEFAULT; + break; + case 42: /* porosities */ + case 43: /* porosity */ + if (read_line_LDBLEs + (next_char, &pors, &count_por, + &count_por_alloc) == ERROR) + { + input_error++; + error_msg("Reading porosities in TRANSPORT keyword.\n", + CONTINUE); + } + opt_save = 42; + break; + case 44: /* fix_current */ + case 45: /* current */ + if (copy_token(token, &next_char, &l) == DIGIT) + { + sscanf(token, SCANFORMAT, &fix_current); +// fix_current = fabs(fix_current); + } + else + { + warning_msg("Expected the fixed value for the current (Ampere)."); + fix_current = 0.0; + } + opt_save = OPTION_DEFAULT; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + /* + * Determine number of cells + */ + max_cells = count_cells; + if (count_length > max_cells) + max_cells = count_length; + if (count_disp > max_cells) + max_cells = count_disp; + //if (count_por > max_cells) + // max_cells = count_por; + if (max_cells > count_cells) + { + if (max_cells == count_length) + { + sprintf(token, + "Number of cells is increased to number of 'lengths' %d.", + count_length); + warning_msg(token); + } + else if (max_cells == count_disp) + { + sprintf(token, + "Number of cells is increased to number of dispersivities %d.", + count_disp); + warning_msg(token); + } + //else + //{ + // sprintf(token, + // "Number of cells is increased to number of porosities %d.", + // count_por); + // warning_msg(token); + //} + } + /* + * Allocate space for cell_data + */ + int all_cells_now = max_cells * (1 + stag_data->count_stag) + 2; + space((void **)((void *)&cell_data), all_cells_now, &cell_data_max_cells, + sizeof(struct cell_data)); + + // initialize new cells + if (all_cells_now > all_cells) + { + for (int i = all_cells; i < all_cells_now; i++) + { + cell_data[i].length = 1.0; + cell_data[i].mid_cell_x = 1.0; + cell_data[i].disp = 1.0; + cell_data[i].temp = 25.0; + cell_data[i].por = 0.3; + cell_data[i].por_il = 0.01; + cell_data[i].potV = 0; + cell_data[i].punch = FALSE; + cell_data[i].print = FALSE; + } + all_cells = all_cells_now; + } + + /* + * Fill in data for lengths + */ + if (count_length == 0) + { + if (old_cells < max_cells) + { + error_string = sformatf( + "No cell-lengths were read; length = 1 m assumed."); + warning_msg(error_string); + for (i = 1; i <= max_cells; i++) + cell_data[i].length = 1.0; + } + } + else + { + for (i = 1; i <= count_length; i++) + { + cell_data[i].length = length[i - 1]; + } + if (max_cells > count_length) + { + error_string = sformatf( + "Cell-lengths were read for %d cells. Last value is used till cell %d.", + count_length, max_cells); + warning_msg(error_string); + for (i = count_length; i <= max_cells; i++) + cell_data[i + 1].length = length[count_length - 1]; + } + } + cell_data[0].mid_cell_x = 0; + cell_data[1].mid_cell_x = cell_data[1].length / 2; + for (i = 2; i <= max_cells; i++) + { + cell_data[i].mid_cell_x = cell_data[i - 1].mid_cell_x + + (cell_data[i - 1].length + cell_data[i].length) / 2; + } + cell_data[max_cells + 1].mid_cell_x = + cell_data[max_cells].mid_cell_x + cell_data[max_cells].length / 2; + /* + * Fill in data for dispersivities + */ + if (count_disp == 0) + { + if (old_cells < max_cells) + { + error_string = sformatf( + "No dispersivities were read; disp = 0 assumed."); + warning_msg(error_string); + for (i = 1; i <= max_cells; i++) + cell_data[i].disp = 0.0; + } + } + else + { + for (i = 1; i <= count_disp; i++) + cell_data[i].disp = disp[i - 1]; + if (max_cells > count_disp) + { + error_string = sformatf( + "Dispersivities were read for %d cells. Last value is used till cell %d.", + count_disp, max_cells); + warning_msg(error_string); + for (i = count_disp; i <= max_cells; i++) + cell_data[i + 1].disp = disp[count_disp - 1]; + } + } + /* + * Fill in data for porosities + */ + if (count_por == 0) + { + if (old_cells < all_cells && multi_Dflag && simul_tr == 1) + { + multi_Dpor = (multi_Dpor < 1e-10 ? 1e-10 : multi_Dpor); + if (multi_Dpor > 1e-10) + error_string = sformatf( + "No porosities were read; used the value %8.2e from -multi_D.", multi_Dpor); + else + error_string = sformatf( + "No porosities were read; set to minimal value of 1e-10 for -multi_D."); + warning_msg(error_string); + for (i = 1; i < all_cells; i++) + cell_data[i].por = multi_Dpor; + } + } + else + { + if ((stag_data->exch_f > 0) && (stag_data->count_stag == 1)) + { + error_string = sformatf( + "Mobile porosities were read, but mobile/immobile porosity was also defined in -stagnant. Using the values from -stagnant for mobile/immobile exchange and tortuosity factors."); + warning_msg(error_string); + for (i = 1; i <= max_cells; i++) + cell_data[i].por = stag_data->th_m; + for (i++; i <= 2 * max_cells + 1; i++) + cell_data[i].por = stag_data->th_im; + } + else + { + for (i = 1; i <= count_por; i++) + cell_data[i].por = pors[i - 1]; + if (max_cells > count_por) + { + error_string = sformatf( + "Porosities were read for %d cells. Last value is used till cell %d.", + count_por, all_cells - 1); + warning_msg(error_string); + for (i = count_por; i < all_cells; i++) + cell_data[i + 1].por = pors[count_por - 1]; + } + } + } + if (interlayer_Dflag && !multi_Dflag) + { + input_error++; + error_string = sformatf( + "-multi_D must be defined, when -interlayer_D true."); + error_msg(error_string, CONTINUE); + + } + for (i = 0; i < all_cells; i++) + { + interlayer_Dpor = (interlayer_Dpor < 1e-10 ? 1e-10 : interlayer_Dpor); + cell_data[i].por_il = interlayer_Dpor; + } + count_cells = max_cells; + /* + * Account for stagnant cells + */ + if (stag_data->count_stag > 0) + { + max_cells = count_cells * (1 + stag_data->count_stag) + 2; + for (i = 1; i <= count_cells; i++) + { + for (l = 1; l <= stag_data->count_stag; l++) + cell_data[i + 1 + l * count_cells].mid_cell_x = + cell_data[i].mid_cell_x; + } + } + /* + * Fill in data for punch + */ + if (count_punch != 0) + { + for (i = 0; i < all_cells; i++) + cell_data[i].punch = FALSE; + for (i = 0; i < count_punch; i++) + { + if (punch_temp[i] > all_cells - 1 || punch_temp[i] < 0) + { + error_string = sformatf( + "Cell number for punch is out of range, %d. Request ignored.", + punch_temp[i]); + warning_msg(error_string); + } + else + cell_data[punch_temp[i]].punch = TRUE; + } + } + else if (simul_tr == 1) + for (i = 0; i < all_cells; i++) + cell_data[i].punch = TRUE; + /* + * Fill in data for print + */ + if (count_print != 0) + { + for (i = 0; i < all_cells; i++) + cell_data[i].print = FALSE; + for (i = 0; i < count_print; i++) + { + if (print_temp[i] > all_cells - 1 || print_temp[i] < 0) + { + error_string = sformatf( + "Cell number for print is out of range, %d. Request ignored.", + print_temp[i]); + warning_msg(error_string); + } + else + cell_data[print_temp[i]].print = TRUE; + } + } + else if (simul_tr == 1) + for (i = 0; i < all_cells; i++) + cell_data[i].print = TRUE; +//#define OLD_POROSITY +#if defined(OLD_POROSITY) + /* + * Fill in porosities + */ + if (interlayer_Dflag && !multi_Dflag) + { + input_error++; + error_string = sformatf( + "-multi_D must be defined, when -interlayer_D true."); + error_msg(error_string, CONTINUE); + + } + for (i = 0; i < max_cells; i++) + { + multi_Dpor = (multi_Dpor < 1e-10 ? 1e-10 : multi_Dpor); //Fix for Jenkins !!!!!!!!!!!! + //if (cell_data[i].por < 0) + { + cell_data[i].por = multi_Dpor; //Fix for Jenkins !!!!!!!!!!!! + } + interlayer_Dpor = (interlayer_Dpor < 1e-10 ? 1e-10 : interlayer_Dpor); + cell_data[i].por_il = interlayer_Dpor; + } +#endif +/* + * Calculate dump_modulus + */ + if (dump_in == TRUE) + { + if (dump_modulus == 0) + { + warning_msg + ("Expected dump_modulus. Value of 'shifts/2' will be used."); + dump_modulus = count_shifts / 2; + if (dump_modulus == 0) + dump_modulus = 1; + } + if (transport_start > count_shifts) + { + input_error++; + error_string = sformatf( + "Starting shift for transport, %d, is greater than number of shifts, %d.", + transport_start, count_shifts); + error_msg(error_string, CONTINUE); + } + } + /* + * Check boundary conditions + */ + if ((ishift != 0) && ((bcon_first == 2) || (bcon_last == 2))) + { + warning_msg + ("Boundary condition = 'closed' not possible with advective transport.\n\t Boundary condition = 'flux' assumed."); + if (bcon_first == 2) + bcon_first = 3; + if (bcon_last == 2) + bcon_last = 3; + } + /* + * Retain data from previous run + */ + if (simul_tr > 1) + { + if ((count_length == 0) && (count_disp == 0) && (count_por == 0)) + dup_print("Column data retained from former run", TRUE); + } + /* + * Check heat_diffc + */ + if (heat_diffc < 0) + heat_diffc = diffc; + else if (stag_data->count_stag == 1) + { + if (stag_data->exch_f > 0) + { + if (diffc <= 0 && heat_diffc > 0) + { + input_error++; + error_string = sformatf( + "Must enter diffusion coefficient (-diffc) when modeling thermal diffusion."); + error_msg(error_string, CONTINUE); + } + else if (heat_diffc > diffc) + { + error_string = sformatf( + "Thermal diffusion is calculated assuming exchange factor was for\n\t effective (non-thermal) diffusion coefficient = %e.", + (double)diffc); + warning_msg(error_string); + } + } + else + { + if (heat_diffc > diffc) + { + input_error++; + error_string = sformatf( + "Must enter value for mobile/stagnant exchange factor when modeling thermal diffusion."); + error_msg(error_string, CONTINUE); + } + } + } + else if (stag_data->count_stag > 1 && heat_diffc > diffc) + { + input_error++; + error_string = sformatf( + "Only one stagnant layer permitted (-stag) when modeling thermal diffusion."); + error_msg(error_string, CONTINUE); + } + /* + * free storage for length, disp, punch + */ + length = (LDBLE *)free_check_null(length); + disp = (LDBLE *)free_check_null(disp); + pors = (LDBLE *)free_check_null(pors); + punch_temp = (int *)free_check_null(punch_temp); + print_temp = (int *)free_check_null(print_temp); + + if (dump_in == TRUE) + { + dump_file_name_cpp.clear(); + dump_file_name_cpp.append(file_name); + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_line_LDBLEs(char *next_char, LDBLE ** d, int *count_d, int *count_alloc) +/* ---------------------------------------------------------------------- */ +{ + int i, j, l, n; + LDBLE value; + char token[MAX_LENGTH]; + + for (;;) + { + j = copy_token(token, &next_char, &l); + if (j == EMPTY) + break; + if (j != DIGIT) + return (ERROR); + if (replace("*", " ", token) == TRUE) + { + if (sscanf(token, "%d" SCANFORMAT, &n, &value) != 2) + return (ERROR); + } + else + { + sscanf(token, SCANFORMAT, &value); + n = 1; + } + for (;;) + { + if ((*count_d) + n > (*count_alloc)) + { + *count_alloc *= 2; + *d = (LDBLE *)PHRQ_realloc(*d, (size_t)(*count_alloc) * sizeof(LDBLE)); + if (*d == NULL) + malloc_error(); + } + else + break; + } + for (i = 0; i < n; i++) + (*d)[(*count_d) + i] = value; + *count_d += n; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +dump_cpp(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * dumps solution compositions to file + */ + + int l; + + if (dump_in == FALSE || pr.dump == FALSE) + return (OK); + + cxxStorageBin phreeqcBin(phrq_io); + phreeqc2cxxStorageBin(phreeqcBin); + + std::ofstream fs(dump_file_name_cpp.c_str()); + if (!fs.is_open()) + { + error_string = sformatf("Can`t open file, %s.", dump_file_name_cpp.c_str()); + input_error++; + error_msg(error_string, CONTINUE); + return (OK); + } + + fs << "# Dumpfile" << "\n" << "# Transport simulation " << simul_tr << " Shift " << transport_step << "\n" << "#" << "\n"; + phreeqcBin.dump_raw(fs, 0); + fs << "END" << "\n"; + + char token[MAX_LENGTH]; + sprintf(token, "KNOBS\n"); + fs << token; + sprintf(token, "\t-iter%15d\n", itmax); + fs << token; + sprintf(token, "\t-tol %15.3e\n", (double)ineq_tol); + fs << token; + sprintf(token, "\t-step%15.3e\n", (double)step_size); + fs << token; + sprintf(token, "\t-pe_s%15.3e\n", (double)pe_step_size); + fs << token; + sprintf(token, "\t-diag "); + fs << token; + if (diagonal_scale == TRUE) + { + sprintf(token, "true\n"); + fs << token; + } + else + { + sprintf(token, "false\n"); + fs << token; + } + std::map < int, SelectedOutput >::iterator so_it = SelectedOutput_map.begin(); + for (; so_it != SelectedOutput_map.end(); so_it++) + { + current_selected_output = &(so_it->second); + + sprintf(token, "SELECTED_OUTPUT %d\n", current_selected_output->Get_n_user()); + fs << token; + //sprintf(token, "\t-file %-15s\n", "sel_o$$$.prn"); + //fs << token; + fs << "\t-file " << "sel_o$$$" << current_selected_output->Get_n_user() << ".prn\n"; + //if (punch.count_totals != 0) + if (current_selected_output->Get_totals().size() > 0) + { + sprintf(token, "\t-tot "); + fs << token; + for (size_t i = 0; i < current_selected_output->Get_totals().size(); i++) + { + sprintf(token, " %s", current_selected_output->Get_totals()[i].first.c_str()); + fs << token; + } + sprintf(token, "\n"); + fs << token; + } + if (current_selected_output->Get_molalities().size() > 0) + { + sprintf(token, "\t-mol "); + fs << token; + for (size_t i = 0; i < current_selected_output->Get_molalities().size(); i++) + { + sprintf(token, " %s", current_selected_output->Get_molalities()[i].first.c_str()); + fs << token; + } + sprintf(token, "\n"); + fs << token; + } + if (current_selected_output->Get_activities().size() > 0) + { + sprintf(token, "\t-act "); + fs << token; + for (size_t i = 0; i < current_selected_output->Get_activities().size(); i++) + { + sprintf(token, " %s", current_selected_output->Get_activities()[i].first.c_str()); + fs << token; + } + sprintf(token, "\n"); + fs << token; + } + if (current_selected_output->Get_pure_phases().size() > 0) + { + sprintf(token, "\t-equ "); + fs << token; + for (size_t i = 0; i < current_selected_output->Get_pure_phases().size(); i++) + { + sprintf(token, " %s", current_selected_output->Get_pure_phases()[i].first.c_str()); + fs << token; + } + sprintf(token, "\n"); + fs << token; + } + if (current_selected_output->Get_si().size() > 0) + { + sprintf(token, "\t-si "); + fs << token; + for (size_t i = 0; i < current_selected_output->Get_si().size(); i++) + { + sprintf(token, " %s", current_selected_output->Get_si()[i].first.c_str()); + fs << token; + } + sprintf(token, "\n"); + fs << token; + } + if (current_selected_output->Get_gases().size() > 0) + { + sprintf(token, "\t-gas "); + fs << token; + for (size_t i = 0; i < current_selected_output->Get_gases().size(); i++) + { + sprintf(token, " %s", current_selected_output->Get_gases()[i].first.c_str()); + fs << token; + } + sprintf(token, "\n"); + fs << token; + } + if (current_selected_output->Get_s_s().size() > 0) + { + sprintf(token, "\t-solid_solutions "); + fs << token; + for (size_t i = 0; i < current_selected_output->Get_s_s().size(); i++) + { + sprintf(token, " %s", current_selected_output->Get_s_s()[i].first.c_str()); + fs << token; + } + sprintf(token, "\n"); + fs << token; + } + if (current_selected_output->Get_kinetics().size() > 0) + { + sprintf(token, "\t-kin "); + fs << token; + for (size_t i = 0; i < current_selected_output->Get_kinetics().size(); i++) + { + sprintf(token, " %s", current_selected_output->Get_kinetics()[i].first.c_str()); + fs << token; + } + sprintf(token, "\n"); + fs << token; + } + } + sprintf(token, "TRANSPORT\n"); + fs << token; + sprintf(token, "\t-cells %6d\n", count_cells); + fs << token; + sprintf(token, "\t-shifts%6d%6d\n", count_shifts, ishift); + fs << token; + sprintf(token, "\t-output_frequency %6d\n", print_modulus); + fs << token; + sprintf(token, "\t-selected_output_frequency %6d\n", + punch_modulus); + fs << token; + sprintf(token, "\t-bcon %6d%6d\n", bcon_first, bcon_last); + fs << token; + sprintf(token, "\t-timest %13.5e\n", (double)timest); + fs << token; + if (!high_precision) + { + sprintf(token, "\t-diffc %13.5e\n", (double)diffc); + fs << token; + } + else + { + sprintf(token, "\t-diffc %20.12e\n", (double)diffc); + fs << token; + } + sprintf(token, "\t-tempr %13.5e\n", (double)tempr); + fs << token; + if (correct_disp == TRUE) + { + sprintf(token, "\t-correct_disp %s\n", "True"); + fs << token; + } + else + { + sprintf(token, "\t-correct_disp %s\n", "False"); + fs << token; + } + sprintf(token, "\t-length\n"); + fs << token; + for (int i = 1; i <= count_cells; i++) + { + sprintf(token, "%12.3e", (double)cell_data[i].length); + fs << token; + if (i > 0 && (i % 8) == 0) + { + sprintf(token, "\n"); + fs << token; + } + } + sprintf(token, "\n"); + fs << token; + sprintf(token, "\t-disp\n"); + fs << token; + for (int i = 1; i <= count_cells; i++) + { + if (!high_precision) + { + sprintf(token, "%12.3e", (double)cell_data[i].disp); + fs << token; + } + else + { + sprintf(token, "%20.12e", (double)cell_data[i].disp); + fs << token; + } + if (i > 0 && (i % 8) == 0) + { + sprintf(token, "\n"); + fs << token; + } + } + sprintf(token, "\n"); + fs << token; + sprintf(token, "\t-punch_cells"); + fs << token; + l = 0; + for (int i = 0; i < all_cells; i++) + { + if (cell_data[i].punch != TRUE) + continue; + sprintf(token, " %d", i); + fs << token; + l++; + if ((l % 20) == 0) + { + sprintf(token, "\n"); + fs << token; + } + } + sprintf(token, "\n"); + fs << token; + sprintf(token, "\t-print_cells"); + fs << token; + l = 0; + for (int i = 0; i < all_cells; i++) + { + if (cell_data[i].print != TRUE) + continue; + sprintf(token, " %d", i); + fs << token; + l++; + if ((l % 20) == 0) + { + sprintf(token, "\n"); + fs << token; + } + } + sprintf(token, "\n"); + fs << token; + sprintf(token, "\t-dump $$$.dmp\n"); + fs << token; + sprintf(token, "\t-dump_frequency %d\n", dump_modulus); + fs << token; + sprintf(token, "\t-dump_restart %d\n", transport_step + 1); + fs << token; + +#if defined MULTICHART + // user graphs + chart_handler.dump(fs, 0); +#endif + + sprintf(token, "END\n"); + fs << token; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +dump(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * dumps solution compositions to file + */ + if (dump_in == FALSE || pr.dump == FALSE) + return (OK); + + dump_cpp(); + return OK; + +} diff --git a/phreeqcpp/runner.cpp b/phreeqcpp/runner.cpp new file mode 100644 index 00000000..d8e78cb8 --- /dev/null +++ b/phreeqcpp/runner.cpp @@ -0,0 +1,149 @@ +#include "runner.h" +#include "Parser.h" +#include "NA.h" +#include "Utils.h" +runner::runner(PHRQ_io *io) +: +PHRQ_base(io) +{ + this->time_step = NA; + this->start_time = NA; + this->run_cells = false; + +} +runner::runner(CParser & parser, PHRQ_io *io) +: +PHRQ_base(io) +{ + this->time_step = NA; + this->start_time = NA; + this->run_cells = false; + this->Read(parser); +} + +runner::~runner(void) +{ +} +bool runner::Read(CParser & parser) +{ + + bool return_value(true); + + std::istream::pos_type ptr; + std::istream::pos_type next_char; + std::string token; + int opt_save; + + this->Get_cells().Set_defined(true); + opt_save = CParser::OPT_DEFAULT; + + StorageBinListItem item; + for (;;) + { + int opt; + opt = parser.get_option(vopts, next_char); + if (opt == CParser::OPT_DEFAULT) + { + opt = opt_save; + } + else + { + opt_save = opt; + } + + // Process other identifiers + std::set < int >::iterator it; + switch (opt) + { + case CParser::OPT_EOF: + break; + case CParser::OPT_KEYWORD: + break; + + case 0: + case 1: + for (;;) + { + CParser::TOKEN_TYPE j = parser.copy_token(token, next_char); + if (j == CParser::TT_DIGIT) + { + item.Augment(token); + } + else if (j == CParser::TT_EMPTY) + { + item.Augment(token); + break; + } + else + { + parser.error_msg("Expected single number or range of numbers.", + PHRQ_io::OT_CONTINUE); + } + } + break; + case 2: //start_time + if (!(parser.get_iss() >> this->start_time)) + { + parser.error_msg("Expected start_time for RUN_CELLS.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + break; + } + { + std::string token; + if (parser.get_iss() >> token) + { + token = trim(token); + this->start_time = Utilities::convert_time(this->start_time, token, "s"); + } + } + break; + case 3: //time_step + case 4: //time_steps + case 5: //step + case 6: //steps + if (!(parser.get_iss() >> this->time_step)) + { + parser.error_msg("Expected time_step for RUN_CELLS.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + break; + } + { + std::string token; + if (parser.get_iss() >> token) + { + token = trim(token); + this->time_step = Utilities::convert_time(this->time_step, token, "s"); + } + } + break; + default: + case CParser::OPT_DEFAULT: + case CParser::OPT_ERROR: + opt = CParser::OPT_EOF; + parser.error_msg("Unknown input reading RUN_CELLS definition.", + PHRQ_io::OT_CONTINUE); + parser.error_msg(parser.line().c_str(), PHRQ_io::OT_CONTINUE); + return_value = false; + break; + } + if (opt == CParser::OPT_EOF || opt == CParser::OPT_KEYWORD) + break; + } + if (item.Get_numbers().size() > 0) + { + this->cells = item; + } + return(return_value); +} +const std::vector< std::string >::value_type temp_vopts[] = { + std::vector< std::string >::value_type("cell"), // 0 + std::vector< std::string >::value_type("cells"), // 1 + std::vector< std::string >::value_type("start_time"), // 2 + std::vector< std::string >::value_type("time_step"), // 3 + std::vector< std::string >::value_type("time_steps"), // 4 + std::vector< std::string >::value_type("step"), // 5 + std::vector< std::string >::value_type("steps") // 6 +}; +const std::vector< std::string > runner::vopts(temp_vopts, temp_vopts + sizeof temp_vopts / sizeof temp_vopts[0]); diff --git a/phreeqcpp/runner.h b/phreeqcpp/runner.h new file mode 100644 index 00000000..f492a7fd --- /dev/null +++ b/phreeqcpp/runner.h @@ -0,0 +1,33 @@ +#if !defined(RUNNER_H_INCLUDED) +#define RUNNER_H_INCLUDED +#include // std::set +#include // std::string + +#include "phrqtype.h" +#include "StorageBinList.h" +#include "PHRQ_base.h" +class CParser; + +class runner: public PHRQ_base +{ +public: + runner(PHRQ_io *io=NULL); + runner(CParser & parser, PHRQ_io *io=NULL); + virtual ~runner(void); + bool Read(CParser & parser); + StorageBinListItem & Get_cells(void) { return(this->cells); }; + LDBLE Get_time_step() { return(this->time_step); }; + LDBLE Get_start_time() { return(this->start_time); }; + void Set_time_step(LDBLE ts) { this->time_step = ts; }; + void Set_start_time(LDBLE st) { this->start_time = st; }; + bool Get_run_cells() { return(this->run_cells); }; + void Set_run_cells(bool tf) { this->run_cells = tf; }; + +protected: + LDBLE time_step; + LDBLE start_time; + StorageBinListItem cells; + bool run_cells; + const static std::vector < std::string > vopts; +}; +#endif // !defined(RUNNER_H_INCLUDED) diff --git a/phreeqcpp/sit.cpp b/phreeqcpp/sit.cpp new file mode 100644 index 00000000..26c8d9aa --- /dev/null +++ b/phreeqcpp/sit.cpp @@ -0,0 +1,1708 @@ +#include "Phreeqc.h" +#include "phqalloc.h" +#include "Exchange.h" +#include "Solution.h" + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +sit_init(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Initialization for SIT + */ + sit_model = FALSE; + max_sit_param = 100; + count_sit_param = 0; + space((void **) ((void *) &sit_params), INIT, &max_sit_param, + sizeof(struct pitz_param *)); + OTEMP = -100.; + OPRESS = -100.; + return OK; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +sit_tidy(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Make lists of species for cations, anions, neutral + */ + int i, j; + /* + * Ensure new parameters are calculated + */ + OTEMP = -100.; + OPRESS = -100.; + /* + * allocate pointers to species structures + */ + if (spec != NULL) spec = (struct species **) free_check_null(spec); + spec = (struct species **) PHRQ_malloc((size_t) (3 * count_s * sizeof(struct species *))); + if (spec == NULL) malloc_error(); + for (i = 0; i < 3 * count_s; i++) spec[i] = NULL; + + cations = spec; + neutrals = &(spec[count_s]); + anions = &(spec[2 * count_s]); + sit_MAXCATIONS = count_s; + sit_FIRSTANION = 2 * count_s; + sit_MAXNEUTRAL = count_s; + sit_count_cations = 0; + sit_count_anions = 0; + sit_count_neutrals = 0; + if (itmax < 200) itmax = 200; + /* + * allocate other arrays for SIT + */ + if (sit_IPRSNT != NULL) sit_IPRSNT = (int *) free_check_null(sit_IPRSNT); + sit_IPRSNT = (int *) PHRQ_malloc((size_t) (3 * count_s * sizeof(int))); + if (sit_IPRSNT == NULL) malloc_error(); + if (sit_M != NULL) sit_M = (LDBLE *) free_check_null(sit_M); + sit_M = (LDBLE *) PHRQ_malloc((size_t) (3 * count_s * sizeof(LDBLE))); + if (sit_M == NULL) malloc_error(); + if (sit_LGAMMA != NULL) sit_LGAMMA = (LDBLE *) free_check_null(sit_LGAMMA); + sit_LGAMMA = (LDBLE *) PHRQ_malloc((size_t) (3 * count_s * sizeof(LDBLE))); + if (sit_LGAMMA == NULL) malloc_error(); + + + for (i = 0; i < count_s; i++) + { + if (s[i] == s_eminus) + continue; + if (s[i] == s_h2o) + continue; + if (s[i]->type == EX || s[i]->type == SURF) + continue; + if (s[i]->z < -.001) + { + anions[sit_count_anions++] = s[i]; + } + else if (s[i]->z > .001) + { + cations[sit_count_cations++] = s[i]; + } + else + { + neutrals[sit_count_neutrals++] = s[i]; + } + } + /* + * no ethetas + */ + /* + * put species numbers in sit_params + */ + for (i = 0; i < count_sit_param; i++) + { + for (j = 0; j < 3; j++) + { + if (sit_params[i]->species[j] == NULL) + continue; + sit_params[i]->ispec[j] = sit_ISPEC(sit_params[i]->species[j]); + if ((j < 2 && sit_params[i]->ispec[j] == -1) || + (j == 3 + && (sit_params[i]->type == TYPE_PSI + || sit_params[i]->type == TYPE_ZETA) + && sit_params[i]->ispec[j] == -1)) + { + input_error++; + error_string = sformatf( + "Species for Pitzer parameter not defined in SOLUTION_SPECIES, %s", + sit_params[i]->species[j]); + error_msg(error_string, CONTINUE); + } + } + } /* remake map */ + { + sit_param_map.clear(); + for (int j = 0; j < count_sit_param; j++) + { + std::set< std::string > header; + for (int i = 0; i < 3; i++) + { + if (sit_params[j]->species[i] != NULL) header.insert(sit_params[j]->species[i]); + } + std::ostringstream key_str; + key_str << sit_params[j]->type << " "; + std::set< std::string >::iterator it = header.begin(); + for(; it != header.end(); ++it) + { + key_str << *it << " "; + } + std::string key = key_str.str().c_str(); + sit_param_map[key] = j; + } + assert ((int) sit_param_map.size() == count_sit_param); + } + if (get_input_errors() > 0) return (ERROR); + return OK; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +sit_ISPEC(const char *name) +/* ---------------------------------------------------------------------- */ +/* + * Find species number in spec for character string species name + */ +{ + int i; + for (i = 0; i < 3 * count_s; i++) + { + if (spec[i] == NULL) + continue; + if (name == spec[i]->name) + { + return (i); + } + } + return (-1); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_sit(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Reads advection information + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + /* + * Read advection parameters: + * number of cells; + * number of shifts; + */ + int n; + struct pitz_param *pzp_ptr; + pitz_param_type pzp_type; + + int return_value, opt, opt_save; + char *next_char; + const char *opt_list[] = { + "epsilon", /* 0 */ + "epsilon1" /* 1 */ + }; + int count_opt_list = 2; + /* + * Read lines + */ + opt_save = OPTION_ERROR; + return_value = UNKNOWN; + n = -1; + pzp_type = TYPE_Other; + pitzer_pe = TRUE; + for (;;) + { + opt = get_option(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT) + { + opt = opt_save; + } + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_DEFAULT: + pzp_ptr = pitz_param_read(line, n); + if (pzp_ptr != NULL) + { + pzp_ptr->type = pzp_type; + sit_param_store(pzp_ptr, false); + } + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SIT keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* epsilon */ + pzp_type = TYPE_SIT_EPSILON; + n = 2; + opt_save = OPTION_DEFAULT; + break; + case 1: /* epsilon1 */ + pzp_type = TYPE_SIT_EPSILON_MU; + n = 2; + opt_save = OPTION_DEFAULT; + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } + sit_model = TRUE; + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_sit_param(struct pitz_param *pz_ptr, LDBLE TK, LDBLE TR) +/* ---------------------------------------------------------------------- */ +{ + LDBLE param; + /* + */ + + if (fabs(TK - TR) < 0.01) + { + param = pz_ptr->a[0]; + } + else + { + param = (pz_ptr->a[0] + + pz_ptr->a[1] * (1.e0 / TK - 1.e0 / TR) + + pz_ptr->a[2] * log(TK / TR) + + pz_ptr->a[3] * (TK - TR) + + pz_ptr->a[4] * (TK * TK - TR * TR)); + } + pz_ptr->p = param; + switch (pz_ptr->type) + { + case TYPE_SIT_EPSILON: + pz_ptr->U.eps = param; + break; + case TYPE_SIT_EPSILON_MU: + pz_ptr->U.eps1 = param; + break; + case TYPE_Other: + default: + error_msg("Should not be TYPE_Other in function calc_sit_param", + STOP); + break; + } + return OK; +} +#ifdef SKIP +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +sit(void) +/* ---------------------------------------------------------------------- */ +{ + int i, i0, i1; + LDBLE param, z0, z1; + LDBLE A, AGAMMA, T; + /* + LDBLE CONV, XI, XX, OSUM, BIGZ, DI, F, XXX, GAMCLM, + CSUM, PHIMAC, OSMOT, BMXP, ETHEAP, CMX, BMX, PHI, + BMXPHI, PHIPHI, AW, A, B; + */ +/* + LDBLE CONV, XI, XX, OSUM, BIGZ, DI, F, XXX, GAMCLM, CSUM, PHIMAC, OSMOT, + B; +*/ + LDBLE XI, XX, OSUM, DI, F, OSMOT, B; + LDBLE I, TK; + /* + C + C INITIALIZE + C + */ + //CONV = 1.0 / log(10.0); + XI = 0.0e0; + XX = 0.0e0; + OSUM = 0.0e0; + /*n + I = *I_X; + TK = *TK_X; + */ + I = mu_x; + TK = tk_x; + /* DH_AB(TK, &A, &B); */ + /* + C + C TRANSFER DATA FROM TO sit_M + C + */ + for (i = 0; i < 3 * count_s; i++) + { + sit_IPRSNT[i] = FALSE; + sit_M[i] = 0.0; + if (spec[i] != NULL && spec[i]->in == TRUE) + { + if (spec[i]->type == EX || + spec[i]->type == SURF || spec[i]->type == SURF_PSI) + continue; + sit_M[i] = under(spec[i]->lm); + if (sit_M[i] > MIN_TOTAL) + sit_IPRSNT[i] = TRUE; + } + } + /* + C + C COMPUTE SIT COEFFICIENTS' TEMPERATURE DEPENDENCE + C + */ + PTEMP_SIT(TK); + for (i = 0; i < 2 * count_s + sit_count_anions; i++) + { + sit_LGAMMA[i] = 0.0; + if (sit_IPRSNT[i] == TRUE) + { + XX = XX + sit_M[i] * fabs(spec[i]->z); + XI = XI + sit_M[i] * spec[i]->z * spec[i]->z; + OSUM = OSUM + sit_M[i]; + } + } + I = XI / 2.0e0; + I = mu_x; // Added equation for MU + DI = sqrt(I); + /* + C + C CALCULATE F & GAMCLM + C + */ + AGAMMA = 3*sit_A0; /* Grenthe p 379 */ + A = AGAMMA / log(10.0); + /* + * F is now for log10 gamma + */ + + B = 1.5; + F = -A * (DI / (1.0e0 + B * DI)); + + + /*OSMOT = -(sit_A0) * pow(I, 1.5e0) / (1.0e0 + B * DI);*/ + T = 1.0 + B*DI; + OSMOT = -2.0*A/(B*B*B)*(T - 2.0*log(T) - 1.0/T); + /* + * Sums for sit_LGAMMA, and OSMOT + * epsilons are tabulated for log10 gamma (not ln gamma) + */ + for (i = 0; i < count_sit_param; i++) + { + i0 = sit_params[i]->ispec[0]; + i1 = sit_params[i]->ispec[1]; + if (sit_IPRSNT[i0] == FALSE || sit_IPRSNT[i1] == FALSE) continue; + z0 = spec[i0]->z; + z1 = spec[i1]->z; + param = sit_params[i]->p; + switch (sit_params[i]->type) + { + case TYPE_SIT_EPSILON: + sit_LGAMMA[i0] += sit_M[i1] * param; + sit_LGAMMA[i1] += sit_M[i0] * param; + if (z0 == 0.0 && z1 == 0.0) + { + OSMOT += sit_M[i0] * sit_M[i1] * param / 2.0; + } + else + { + OSMOT += sit_M[i0] * sit_M[i1] * param; + } + break; + case TYPE_SIT_EPSILON_MU: + sit_LGAMMA[i0] += sit_M[i1] * I * param; + sit_LGAMMA[i1] += sit_M[i0] * I * param; + OSMOT += sit_M[i0] * sit_M[i1] * param; + if (z0 == 0.0 && z1 == 0.0) + { + OSMOT += sit_M[i0] * sit_M[i1] * param * I / 2.0; + } + else + { + OSMOT += sit_M[i0] * sit_M[i1] * param * I; + } + break; + default: + case TYPE_Other: + error_msg("TYPE_Other in pitz_param list.", STOP); + break; + } + } + + /* + * Add F and CSUM terms to sit_LGAMMA + */ + + for (i = 0; i < sit_count_cations; i++) + { + z0 = spec[i]->z; + sit_LGAMMA[i] += z0 * z0 * F; + } + for (i = 2 * count_s; i < 2 * count_s + sit_count_anions; i++) + { + z0 = spec[i]->z; + sit_LGAMMA[i] += z0 * z0 * F; + } + /* + C + C CONVERT TO MACINNES CONVENTION + C + */ + /*COSMOT = 1.0e0 + 2.0e0 * OSMOT / OSUM;*/ + COSMOT = 1.0e0 + OSMOT*log(10.0) / OSUM; + /* + C + C CALCULATE THE ACTIVITY OF WATER + C + */ + AW = exp(-OSUM * COSMOT / 55.50837e0); + /*if (AW > 1.0) AW = 1.0;*/ + /*s_h2o->la=log10(AW); */ + mu_x = I; + for (i = 0; i < 2 * count_s + sit_count_anions; i++) + { + if (sit_IPRSNT[i] == FALSE) continue; + spec[i]->lg_pitzer = sit_LGAMMA[i]; +/* + output_msg(sformatf( "%d %s:\t%e\t%e\t%e\t%e \n", i, spec[i]->name, sit_M[i], spec[i]->la, spec[i]->lg_pitzer, spec[i]->lg)); +*/ + } + return (OK); +} +#endif +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +sit(void) +/* ---------------------------------------------------------------------- */ +{ + int i, i0, i1; + LDBLE param, z0, z1; + LDBLE A, AGAMMA, T; + /* + LDBLE CONV, XI, XX, OSUM, BIGZ, DI, F, XXX, GAMCLM, + CSUM, PHIMAC, OSMOT, BMXP, ETHEAP, CMX, BMX, PHI, + BMXPHI, PHIPHI, AW, A, B; + */ +/* + LDBLE CONV, XI, XX, OSUM, BIGZ, DI, F, XXX, GAMCLM, CSUM, PHIMAC, OSMOT, + B; +*/ + LDBLE XI, XX, OSUM, DI, F, OSMOT, B; + LDBLE I, TK; + /* + C + C INITIALIZE + C + */ + //CONV = 1.0 / log(10.0); + XI = 0.0e0; + XX = 0.0e0; + OSUM = 0.0e0; + /*n + I = *I_X; + TK = *TK_X; + */ + I = mu_x; + TK = tk_x; + /* DH_AB(TK, &A, &B); */ + /* + C + C TRANSFER DATA FROM TO sit_M + C + */ + double log_min = log10(MIN_TOTAL); + for (size_t j = 0; j < s_list.size(); j++) + { + i = s_list[j]; + if (spec[i]->lm > log_min) + { + sit_M[i] = under(spec[i]->lm); + } + else + { + sit_M[i] = 0.0; + } + } + //for (i = 0; i < 3 * count_s; i++) + //{ + // sit_IPRSNT[i] = FALSE; + // sit_M[i] = 0.0; + // if (spec[i] != NULL && spec[i]->in == TRUE) + // { + // if (spec[i]->type == EX || + // spec[i]->type == SURF || spec[i]->type == SURF_PSI) + // continue; + // sit_M[i] = under(spec[i]->lm); + // if (sit_M[i] > MIN_TOTAL) + // sit_IPRSNT[i] = TRUE; + // } + //} + /* + C + C COMPUTE SIT COEFFICIENTS' TEMPERATURE DEPENDENCE + C + */ + PTEMP_SIT(TK); + for (size_t j = 0; j < s_list.size(); j++) + { + int i = s_list[j]; + sit_LGAMMA[i] = 0.0; + XX = XX + sit_M[i] * fabs(spec[i]->z); + XI = XI + sit_M[i] * spec[i]->z * spec[i]->z; + OSUM = OSUM + sit_M[i]; + } + //for (i = 0; i < 2 * count_s + sit_count_anions; i++) + //{ + // sit_LGAMMA[i] = 0.0; + // if (sit_IPRSNT[i] == TRUE) + // { + // XX = XX + sit_M[i] * fabs(spec[i]->z); + // XI = XI + sit_M[i] * spec[i]->z * spec[i]->z; + // OSUM = OSUM + sit_M[i]; + // } + //} + I = XI / 2.0e0; + I = mu_x; // Added equation for MU + DI = sqrt(I); + /* + C + C CALCULATE F & GAMCLM + C + */ + AGAMMA = 3*sit_A0; /* Grenthe p 379 */ + A = AGAMMA / log(10.0); + /* + * F is now for log10 gamma + */ + + B = 1.5; + F = -A * (DI / (1.0e0 + B * DI)); + + + /*OSMOT = -(sit_A0) * pow(I, 1.5e0) / (1.0e0 + B * DI);*/ + T = 1.0 + B*DI; + OSMOT = -2.0*A/(B*B*B)*(T - 2.0*log(T) - 1.0/T); + /* + * Sums for sit_LGAMMA, and OSMOT + * epsilons are tabulated for log10 gamma (not ln gamma) + */ + //for (i = 0; i < count_sit_param; i++) + //{ + for (size_t j = 0; j < param_list.size(); j++) + { + int i = param_list[j]; + i0 = sit_params[i]->ispec[0]; + i1 = sit_params[i]->ispec[1]; + //if (sit_IPRSNT[i0] == FALSE || sit_IPRSNT[i1] == FALSE) continue; + z0 = spec[i0]->z; + z1 = spec[i1]->z; + param = sit_params[i]->p; + switch (sit_params[i]->type) + { + case TYPE_SIT_EPSILON: + sit_LGAMMA[i0] += sit_M[i1] * param; + sit_LGAMMA[i1] += sit_M[i0] * param; + if (z0 == 0.0 && z1 == 0.0) + { + OSMOT += sit_M[i0] * sit_M[i1] * param / 2.0; + } + else + { + OSMOT += sit_M[i0] * sit_M[i1] * param; + } + break; + case TYPE_SIT_EPSILON_MU: + sit_LGAMMA[i0] += sit_M[i1] * I * param; + sit_LGAMMA[i1] += sit_M[i0] * I * param; + OSMOT += sit_M[i0] * sit_M[i1] * param; + if (z0 == 0.0 && z1 == 0.0) + { + OSMOT += sit_M[i0] * sit_M[i1] * param * I / 2.0; + } + else + { + OSMOT += sit_M[i0] * sit_M[i1] * param * I; + } + break; + default: + case TYPE_Other: + error_msg("TYPE_Other in pitz_param list.", STOP); + break; + } + } + + /* + * Add F and CSUM terms to sit_LGAMMA + */ + for (size_t j = 0; j < ion_list.size(); j++) + { + int i = ion_list[j]; + z0 = spec[i]->z; + sit_LGAMMA[i] += z0 * z0 * F; + } + //for (i = 0; i < sit_count_cations; i++) + //{ + // z0 = spec[i]->z; + // sit_LGAMMA[i] += z0 * z0 * F; + //} + //for (i = 2 * count_s; i < 2 * count_s + sit_count_anions; i++) + //{ + // z0 = spec[i]->z; + // sit_LGAMMA[i] += z0 * z0 * F; + //} + /* + C + C CONVERT TO MACINNES CONVENTION + C + */ + /*COSMOT = 1.0e0 + 2.0e0 * OSMOT / OSUM;*/ + COSMOT = 1.0e0 + OSMOT*log(10.0) / OSUM; + /* + C + C CALCULATE THE ACTIVITY OF WATER + C + */ + AW = exp(-OSUM * COSMOT / 55.50837e0); + /*if (AW > 1.0) AW = 1.0;*/ + /*s_h2o->la=log10(AW); */ + mu_x = I; + for (size_t j = 0; j < s_list.size(); j++) + { + int i = s_list[j]; + spec[i]->lg_pitzer = sit_LGAMMA[i]; + } +// for (i = 0; i < 2 * count_s + sit_count_anions; i++) +// { +// if (sit_IPRSNT[i] == FALSE) continue; +// spec[i]->lg_pitzer = sit_LGAMMA[i]; +///* +// output_msg(sformatf( "%d %s:\t%e\t%e\t%e\t%e \n", i, spec[i]->name, sit_M[i], spec[i]->la, spec[i]->lg_pitzer, spec[i]->lg)); +//*/ +// } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +sit_clean_up(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Free all allocated memory, except strings + */ + int i; + + for (i = 0; i < count_sit_param; i++) + { + sit_params[i] = (struct pitz_param *) free_check_null(sit_params[i]); + } + count_sit_param = 0; + sit_params = (struct pitz_param **) free_check_null(sit_params); + sit_param_map.clear(); + sit_LGAMMA = (LDBLE *) free_check_null(sit_LGAMMA); + sit_IPRSNT = (int *) free_check_null(sit_IPRSNT); + spec = (struct species **) free_check_null(spec); + aphi = (struct pitz_param *) free_check_null(aphi); + sit_M = (LDBLE *) free_check_null(sit_M); + + return OK; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_sit(int initial) +/* ---------------------------------------------------------------------- */ +{ +/* + * Sets initial guesses for unknowns if initial == TRUE + * Revises guesses whether initial is true or not + */ + int i; + cxxSolution *solution_ptr; +/* + * Set initial log concentrations to zero + */ + iterations = -1; + solution_ptr = use.Get_solution_ptr(); + for (i = 0; i < count_s_x; i++) + { + s_x[i]->lm = LOG_ZERO_MOLALITY; + s_x[i]->lg_pitzer = 0.0; + } + if (initial == TRUE || set_and_run_attempt > 0) + { + for (i = 0; i < count_s_x; i++) + { + s_x[i]->lg = 0.0; + } + } +/* + * Set master species activities + */ + tc_x = solution_ptr->Get_tc(); + tk_x = tc_x + 273.15; + + patm_x = solution_ptr->Get_patm(); // done in calc_rho_0(tc, pa) + potV_x = solution_ptr->Get_potV(); + +/* + * H+, e-, H2O + */ + mass_water_aq_x = solution_ptr->Get_mass_water(); + mu_x = solution_ptr->Get_mu(); + s_h2o->moles = mass_water_aq_x / gfw_water; + s_h2o->la = log10(solution_ptr->Get_ah2o()); + AW = pow((LDBLE) 10.0E0, s_h2o->la); + s_hplus->la = -solution_ptr->Get_ph(); + s_hplus->lm = s_hplus->la; + s_hplus->moles = exp(s_hplus->lm * LOG_10) * mass_water_aq_x; + s_eminus->la = -solution_ptr->Get_pe(); + if (initial == TRUE) sit_initial_guesses(); + if (dl_type_x != cxxSurface::NO_DL) initial_surface_water(); + sit_revise_guesses(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +sit_initial_guesses(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Make initial guesses for activities of master species and + * ionic strength + */ + int i; + cxxSolution *solution_ptr; + + solution_ptr = use.Get_solution_ptr(); + mu_x = + s_hplus->moles + + exp((solution_ptr->Get_ph() - 14.) * LOG_10) * mass_water_aq_x; + mu_x /= mass_water_aq_x; + s_h2o->la = 0.0; + for (i = 0; i < count_unknowns; i++) + { + if (x[i] == ph_unknown || x[i] == pe_unknown) + continue; + if (x[i]->type < CB) + { + mu_x += + x[i]->moles / mass_water_aq_x * 0.5 * x[i]->master[0]->s->z * + x[i]->master[0]->s->z; + x[i]->master[0]->s->la = log10(x[i]->moles / mass_water_aq_x); + } + else if (x[i]->type == CB) + { + x[i]->master[0]->s->la = + log10(0.001 * x[i]->moles / mass_water_aq_x); + } + else if (x[i]->type == SOLUTION_PHASE_BOUNDARY) + { + x[i]->master[0]->s->la = + log10(0.001 * x[i]->moles / mass_water_aq_x); + } + else if (x[i]->type == EXCH) + { + if (x[i]->moles <= 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else + { + x[i]->master[0]->s->la = log10(x[i]->moles); + } + } + else if (x[i]->type == SURFACE) + { + if (x[i]->moles <= 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else + { + x[i]->master[0]->s->la = log10(0.1 * x[i]->moles); + } + } + else if (x[i]->type == SURFACE_CB) + { + x[i]->master[0]->s->la = 0.0; + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +sit_revise_guesses(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Revise molalities species + */ + int i; + int l_iter, max_iter, repeat, fail; + LDBLE weight, f; + + max_iter = 100; + /* gammas(mu_x); */ + l_iter = 0; + repeat = TRUE; + fail = FALSE; + double d = 2; + double logd = log10(d); + while (repeat == TRUE && fail == FALSE) + { + l_iter++; + if (debug_set == TRUE) + { + output_msg(sformatf( "\nBeginning set iteration %d.\n", + l_iter)); + } + if (l_iter == max_iter + 1) + { + log_msg(sformatf( + "Did not converge in set, iteration %d.\n", + iterations)); + fail = TRUE; + } + if (l_iter > 2 * max_iter) + { + log_msg(sformatf( + "Did not converge with relaxed criteria in set.\n")); + return (OK); + } + molalities(TRUE); + /*pitzer(); */ + /*s_h2o->la = 0.0; */ + /*molalities(TRUE); */ + mb_sums(); + if (state < REACTION) + { + sum_species(); + } + else + { + for (i = 0; i < count_unknowns; i++) + { + x[i]->sum = x[i]->f; + } + } + /*n + if (debug_set == TRUE) { + pr.species = TRUE; + pr.all = TRUE; + print_species(); + } + */ + repeat = FALSE; + for (i = 0; i < count_unknowns; i++) + { + if (x[i] == ph_unknown || x[i] == pe_unknown) + continue; + if (x[i]->type == MB || + /* x[i]->type == ALK || */ + x[i]->type == CB || + x[i]->type == SOLUTION_PHASE_BOUNDARY || + x[i]->type == EXCH || x[i]->type == SURFACE) + { + + if (debug_set == TRUE) + { + output_msg(sformatf( + "\n\t%5s at beginning of set %d: %e\t%e\t%e\n", + x[i]->description, l_iter, (double) x[i]->sum, + (double) x[i]->moles, + (double) x[i]->master[0]->s->la)); + } + if (fabs(x[i]->moles) < 1e-30) + x[i]->moles = 0; + f = fabs(x[i]->sum); + if (f == 0 && x[i]->moles == 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + continue; + } + else if (f == 0) + { + repeat = TRUE; + x[i]->master[0]->s->la += logd; +/*!!!!*/ if (x[i]->master[0]->s->la < -999.) + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else if (f > d * fabs(x[i]->moles) + || f < 1.0/d * fabs(x[i]->moles)) + { + weight = (f < 1.0/d * fabs(x[i]->moles)) ? 0.3 : 1.0; + if (x[i]->moles <= 0) + { + x[i]->master[0]->s->la = MIN_RELATED_LOG_ACTIVITY; + } + else + { + repeat = TRUE; + x[i]->master[0]->s->la += + weight * log10(fabs(x[i]->moles / x[i]->sum)); + } + if (debug_set == TRUE) + { + output_msg(sformatf( + "\t%5s not converged in set %d: %e\t%e\t%e\n", + x[i]->description, l_iter, + (double) x[i]->sum, (double) x[i]->moles, + (double) x[i]->master[0]->s->la)); + } + } + } + else if (x[i]->type == ALK) + { + f = total_co2; + if (fail == TRUE && f < 1.5 * fabs(x[i]->moles)) + { + continue; + } + if (f > 1.5 * fabs(x[i]->moles) + || f < 1.0/d * fabs(x[i]->moles)) + { + repeat = TRUE; + weight = (f < 1.0/d * fabs(x[i]->moles)) ? 0.3 : 1.0; + x[i]->master[0]->s->la += weight * + log10(fabs(x[i]->moles / x[i]->sum)); + if (debug_set == TRUE) + { + output_msg(sformatf( + "%s not converged in set. %e\t%e\t%e\n", + x[i]->description, (double) x[i]->sum, + (double) x[i]->moles, + (double) x[i]->master[0]->s->la)); + } + } + } + } + } + log_msg(sformatf( "Iterations in sit_revise_guesses: %d\n", l_iter)); + /*mu_x = mu_unknown->f * 0.5 / mass_water_aq_x; */ + if (mu_x <= 1e-8) + { + mu_x = 1e-8; + } + /*gammas(mu_x); */ + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +jacobian_sit(void) +/* ---------------------------------------------------------------------- */ +{ + LDBLE *base; + LDBLE d, d1, d2; + int i, j; +Restart: + int pz_max_unknowns = max_unknowns; + //k_temp(tc_x, patm_x); + if (full_pitzer == TRUE) + { + molalities(TRUE); + sit(); + residuals(); + } + base = (LDBLE *) PHRQ_malloc((size_t) count_unknowns * sizeof(LDBLE)); + if (base == NULL) + { + malloc_error(); + return OK; + } + for (i = 0; i < count_unknowns; i++) + { + base[i] = residual[i]; + } + d = 0.0001; + d1 = d * log(10.0); + d2 = 0; + for (i = 0; i < count_unknowns; i++) + { + switch (x[i]->type) + { + case MB: + case ALK: + case CB: + case SOLUTION_PHASE_BOUNDARY: + case EXCH: + case SURFACE: + case SURFACE_CB: + case SURFACE_CB1: + case SURFACE_CB2: + x[i]->master[0]->s->la += d; + d2 = d1; + break; + case AH2O: + x[i]->master[0]->s->la += d; + d2 = d1; + break; + case PITZER_GAMMA: + if (!full_pitzer) + continue; + x[i]->s->lg += d; + d2 = d; + break; + case MH2O: + mass_water_aq_x *= (1.0 + d); + x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + d2 = log(1.0 + d); + break; + case MH: + s_eminus->la += d; + d2 = d1; + break; + /* + if (pitzer_pe == TRUE) + { + s_eminus->la += d; + d2 = d1; + break; + } + else + { + continue; + } + */ + case GAS_MOLES: + if (gas_in == FALSE) + continue; + d2 = d * x[i]->moles; + if (d2 < 1e-14) + d2 = 1e-14; + x[i]->moles += d2; + break; + case MU: + //continue; + d2 = d * mu_x; + mu_x += d2; + //k_temp(tc_x, patm_x); + gammas(mu_x); + break; + case PP: + case SS_MOLES: + continue; + break; + } + molalities(TRUE); + if (max_unknowns > pz_max_unknowns) + { + base = (LDBLE *) free_check_null(base); + gammas_sit(); + jacobian_sums(); + goto Restart; + } + if (full_pitzer == TRUE) + sit(); + mb_sums(); + residuals(); + for (j = 0; j < count_unknowns; j++) + { + array[j * (count_unknowns + 1) + i] = + -(residual[j] - base[j]) / d2; + } + switch (x[i]->type) + { + case MB: + case ALK: + case CB: + case SOLUTION_PHASE_BOUNDARY: + case EXCH: + case SURFACE: + case SURFACE_CB: + case SURFACE_CB1: + case SURFACE_CB2: + case AH2O: + x[i]->master[0]->s->la -= d; + break; + case MH: + s_eminus->la -= d; + if (array[i * (count_unknowns + 1) + i] == 0) + { + array[i * (count_unknowns + 1) + i] = + exp(s_h2->lm * LOG_10) * 2; + } + break; + case PITZER_GAMMA: + x[i]->s->lg -= d; + break; + case MH2O: + mass_water_aq_x /= (1 + d); + x[i]->master[0]->s->moles = mass_water_aq_x / gfw_water; + break; + case MU: + mu_x -= d2; + //k_temp(tc_x, patm_x); + gammas(mu_x); + break; + case GAS_MOLES: + if (gas_in == FALSE) + continue; + x[i]->moles -= d2; + break; + } + } + molalities(TRUE); + if (full_pitzer == TRUE) + sit(); + mb_sums(); + residuals(); + free_check_null(base); + return OK; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +model_sit(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * model is called after the equations have been set up by prep + * and initial guesses have been made in set. + * + * Here is the outline of the calculation sequence: + * residuals--residuals are calculated, if small we are done + * sum_jacobian--jacobian is calculated + * ineq--inequality solver is called + * reset--estimates of unknowns revised, if changes are small solution + * has been found, usually convergence is found in residuals. + * gammas--new activity coefficients + * molalities--calculate molalities + * mb_sums--calculate mass-balance sums + * mb_gases--decide if gas_phase exists + * mb_ss--decide if solid_solutions exists + * switch_bases--check to see if new basis species is needed + * reprep--rewrite equations with new basis species if needed + * sit_revise_guesses--revise unknowns to get initial mole balance + * check_residuals--check convergence one last time + * sum_species--calculate sums of elements from species concentrations + * + * An additional pass through may be needed if unstable phases still exist + * in the phase assemblage. + */ + int l_kode, return_kode; + int r; + int count_infeasible, count_basis_change; + int debug_model_save; + int mass_water_switch_save; + +/* debug_model = TRUE; */ +/* debug_prep = TRUE; */ +/* debug_set = TRUE; */ + /* mass_water_switch == TRUE, mass of water is constant */ + mass_water_switch_save = mass_water_switch; + if (mass_water_switch_save == FALSE && delay_mass_water == TRUE) + { + mass_water_switch = TRUE; + } + debug_model_save = debug_model; + pe_step_size_now = pe_step_size; + step_size_now = step_size; +#ifdef NPP + if (!use.Get_kinetics_in()) status(0, NULL); +#else + status(0, NULL); +#endif + iterations = 0; + gamma_iterations = 0; + count_basis_change = count_infeasible = 0; + stop_program = FALSE; + remove_unstable_phases = FALSE; + if (always_full_pitzer == TRUE) + { + full_pitzer = TRUE; + } + else + { + full_pitzer = FALSE; + } + //sit_make_lists(); + for (;;) + { + mb_gases(); + mb_ss(); + l_kode = 1; + while ((r = residuals()) != CONVERGED + || remove_unstable_phases == TRUE) + { +#if defined(PHREEQCI_GUI) + PhreeqcIWait(this); +#endif + iterations++; + if (iterations > itmax - 1 && debug_model == FALSE + && pr.logfile == TRUE) + { + set_forward_output_to_log(TRUE); + debug_model = TRUE; + } + if (debug_model == TRUE) + { + output_msg(sformatf( + "\nIteration %d\tStep_size = %f\n", iterations, + (double) step_size_now)); + output_msg(sformatf( "\t\tPe_step_size = %f\n\n", + (double) pe_step_size_now)); + } + /* + * Iterations exceeded + */ + if (iterations > itmax) + { + error_string = sformatf( "Maximum iterations exceeded, %d\n", + itmax); + warning_msg(error_string); + stop_program = TRUE; + break; + } + /* + * Calculate jacobian + */ + gammas_sit(); + jacobian_sums(); + jacobian_sit(); + /* + * Full matrix with pure phases + */ + if (r == OK || remove_unstable_phases == TRUE) + { + return_kode = ineq(l_kode); + if (return_kode != OK) + { + if (debug_model == TRUE) + { + output_msg(sformatf( + "Ineq had infeasible solution, " + "kode %d, iteration %d\n", return_kode, + iterations)); + } + log_msg(sformatf( "Ineq had infeasible solution, " + "kode %d, iteration %d\n", return_kode, + iterations)); + count_infeasible++; + } + if (return_kode == 2) + { + ineq(0); + } + reset(); + } + gammas_sit(); + if (full_pitzer == TRUE) + sit(); + if (always_full_pitzer == TRUE) + { + full_pitzer = TRUE; + } + else + { + full_pitzer = FALSE; + } + if (molalities(FALSE) == ERROR) + { + sit_revise_guesses(); + } + if (use.Get_surface_ptr() != NULL && + use.Get_surface_ptr()->Get_dl_type() != cxxSurface::NO_DL && + use.Get_surface_ptr()->Get_related_phases() == TRUE) + initial_surface_water(); + mb_sums(); + mb_gases(); + mb_ss(); +/* + * Switch bases if necessary + */ + if (switch_bases() == TRUE) + { + + count_basis_change++; + count_unknowns -= count_s_x; + reprep(); + full_pitzer = false; + } + /* debug + species_list_sort(); + sum_species(); + print_species(); + print_exchange(); + print_surface(); + */ + if (stop_program == TRUE) + { + break; + } + } +/* + * Check for stop_program + */ + + if (stop_program == TRUE) + { + break; + } + if (check_residuals() == ERROR) + { + stop_program = TRUE; + break; + } + /* remove_unstable_phases is set in check_residuals */ + if (remove_unstable_phases == FALSE && mass_water_switch_save == FALSE + && mass_water_switch == TRUE) + { + log_msg(sformatf( + "\nChanging water switch to FALSE. Iteration %d.\n", + iterations)); + mass_water_switch = FALSE; + continue; + } + gamma_iterations++; + if (gamma_iterations > itmax) + { + error_string = sformatf( "Maximum gamma iterations exceeded, %d\n", + itmax); + warning_msg(error_string); + stop_program = TRUE; + break; + } + if (check_gammas_sit() != TRUE) + { + full_pitzer = TRUE; + continue; + } + if (remove_unstable_phases == FALSE) + break; + if (debug_model == TRUE) + { + output_msg(sformatf( + "\nRemoving unstable phases. Iteration %d.\n", + iterations)); + } + log_msg(sformatf( "\nRemoving unstable phases. Iteration %d.\n", + iterations)); + } + log_msg(sformatf( "\nNumber of infeasible solutions: %d\n", + count_infeasible)); + log_msg(sformatf( "Number of basis changes: %d\n\n", + count_basis_change)); + log_msg(sformatf( "Number of iterations: %d\n", iterations)); + log_msg(sformatf( "Number of gamma iterations: %d\n\n", gamma_iterations)); + debug_model = debug_model_save; + set_forward_output_to_log(FALSE); + if (stop_program == TRUE) + { + return (ERROR); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_gammas_sit(void) +/* ---------------------------------------------------------------------- */ +{ + LDBLE old_mu, tol; + int converge, i; + + old_mu = mu_x; + sit(); + molalities(TRUE); + mb_sums(); + converge = TRUE; + tol = convergence_tolerance * 10.; + for (i = 0; i < count_unknowns; i++) + { + if (x[i]->type != PITZER_GAMMA) + continue; + if (fabs(x[i]->s->lg - x[i]->s->lg_pitzer) > tol) + { + converge = FALSE; + } + } + if (fabs(old_mu - mu_x) > tol) + { + converge = FALSE; + } + if ((pow((LDBLE) 10.0, s_h2o->la) - AW) > tol) + { + converge = FALSE; + } + return converge; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +gammas_sit() +/* ---------------------------------------------------------------------- */ +{ +/* + * Need exchange gammas for pitzer + */ + int i, j; + LDBLE coef; + /* Initialize */ + k_temp(tc_x, patm_x); +/* + * Calculate activity coefficients + */ + for (i = 0; i < count_s_x; i++) + { + switch (s_x[i]->gflag) + { + case 0: /* uncharged */ + case 1: /* Davies */ + case 2: /* Extended D-H, WATEQ D-H */ + case 3: /* Always 1.0 */ + break; + case 4: /* Exchange */ + /* Now calculated in next loop */ + break; + case 5: /* Always 1.0 */ + break; + case 6: /* Surface */ +/* + * Find moles of sites. + * s_x[i]->equiv is stoichiometric coefficient of sites in species + */ + for (j = 1; s_x[i]->rxn_x->token[j].s != NULL; j++) + { + if (s_x[i]->rxn_x->token[j].s->type == SURF) + { + s_x[i]->alk = + s_x[i]->rxn_x->token[j].s->primary->unknown->moles; + break; + } + } + if (s_x[i]->alk > 0) + { + s_x[i]->lg = log10(s_x[i]->equiv / s_x[i]->alk); + s_x[i]->dg = 0.0; + } + else + { + s_x[i]->lg = 0.0; + s_x[i]->dg = 0.0; + } + break; + case 7: /* LLNL */ + break; + case 8: /* LLNL CO2 */ + break; + case 9: /* activity water */ + s_x[i]->lg = log10(exp(s_h2o->la * LOG_10) * gfw_water); + s_x[i]->dg = 0.0; + break; + } +/* + if (mu_unknown != NULL) { + if (fabs(residual[mu_unknown->number]) > 0.1 && + fabs(residual[mu_unknown->number])/mu_x > 0.5) { + s_x[i]->dg = 0.0; + } + } + */ + } + /* + * calculate exchange gammas + */ + + if (use.Get_exchange_ptr() != NULL) + { + for (i = 0; i < count_s_x; i++) + { + switch (s_x[i]->gflag) + { + case 0: /* uncharged */ + case 1: /* Davies */ + case 2: /* Extended D-H, WATEQ D-H */ + case 3: /* Always 1.0 */ + case 5: /* Always 1.0 */ + case 6: /* Surface */ + case 7: /* LLNL */ + case 8: /* LLNL CO2 */ + case 9: /* activity water */ + break; + case 4: /* Exchange */ + + /* + * Find CEC + * z contains valence of cation for exchange species, alk contains cec + */ + /* !!!!! */ + for (j = 1; s_x[i]->rxn_x->token[j].s != NULL; j++) + { + if (s_x[i]->rxn_x->token[j].s->type == EX) + { + s_x[i]->alk = + s_x[i]->rxn_x->token[j].s->primary->unknown-> + moles; + break; + } + } + /* + * Master species is a dummy variable with meaningless activity and mass + */ + s_x[i]->lg = 0.0; + s_x[i]->dg = 0.0; + if (s_x[i]->primary != NULL) + { + break; + } + /* + * All other species + */ + + /* modific 29 july 2005... */ + if (s_x[i]->equiv != 0 && s_x[i]->alk > 0) + { + s_x[i]->lg = log10(fabs(s_x[i]->equiv) / s_x[i]->alk); + } + if (use.Get_exchange_ptr()->Get_pitzer_exchange_gammas()) + { + /* Assume equal gamma's of solute and exchangeable species... */ + for (j = 1; s_x[i]->rxn_x->token[j].s != NULL; j++) + { + if (s_x[i]->rxn_x->token[j].s->type == EX) + continue; + coef = s_x[i]->rxn_x->token[j].coef; + s_x[i]->lg += coef * s_x[i]->rxn_x->token[j].s->lg; + s_x[i]->dg += coef * s_x[i]->rxn_x->token[j].s->dg; + } + } + } + } + } +/* ...end modific 29 july 2005 */ + + return (OK); +} +#ifdef SKIP +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +PTEMP_SIT(LDBLE TK) +/* ---------------------------------------------------------------------- */ +{ +/* +C +C SUBROUTINE TO CALUCLATE TEMPERATURE DEPENDENCE OF PITZER PARAMETER +C +*/ + int i; + LDBLE TR = 298.15; + + if (fabs(TK - OTEMP) < 0.001 && fabs(patm_x - OPRESS) < 0.1) return OK; +/* +C Set DW0 +*/ + DW0 = rho_0 = calc_rho_0(TK - 273.15, patm_x); + VP = patm_x; + for (i = 0; i < count_sit_param; i++) + { + calc_sit_param(sit_params[i], TK, TR); + } + calc_dielectrics(TK - 273.15, patm_x); + sit_A0 = A0; + OTEMP = TK; + OPRESS = patm_x; + return OK; +} +#endif +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +PTEMP_SIT(LDBLE TK) +/* ---------------------------------------------------------------------- */ +{ +/* +C +C SUBROUTINE TO CALUCLATE TEMPERATURE DEPENDENCE OF PITZER PARAMETER +C +*/ + LDBLE TR = 298.15; + + if (fabs(TK - OTEMP) < 0.001 && fabs(patm_x - OPRESS) < 0.1) return OK; +/* +C Set DW0 +*/ + DW0 = rho_0 = calc_rho_0(TK - 273.15, patm_x); + VP = patm_x; + for (size_t j = 0; j < param_list.size(); j++) + { + int i = param_list[j]; + calc_sit_param(sit_params[i], TK, TR); + } + calc_dielectrics(TK - 273.15, patm_x); + sit_A0 = A0; + OTEMP = TK; + OPRESS = patm_x; + return OK; +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +sit_make_lists(void) +/* ---------------------------------------------------------------------- */ +{ + double log_min = log10(MIN_TOTAL); + s_list.clear(); + cation_list.clear(); + neutral_list.clear(); + anion_list.clear(); + ion_list.clear(); + param_list.clear(); + OTEMP = -100.0; + for (int j = 0; j < 3; j++) + { + int min, max; + switch (j) + { + case 0: + min = 0; + max = sit_count_cations; + break; + case 1: + min = count_s; + max = count_s + sit_count_neutrals; + break; + case 2: + min = 2*count_s; + max = 2*count_s + sit_count_anions; + break; + } + for (int i = min; i < max; i++) + { + sit_IPRSNT[i] = FALSE; + sit_M[i] = 0.0; + if (spec[i] != NULL && spec[i]->in == TRUE) + { + if (spec[i]->type == EX || + spec[i]->type == SURF || spec[i]->type == SURF_PSI) + continue; + sit_IPRSNT[i] = TRUE; + s_list.push_back(i); + if (i < count_s) + { + cation_list.push_back(i); + } + if (i >= count_s && i < 2*count_s) + { + neutral_list.push_back(i); + } + if (i >= 2*count_s) + { + anion_list.push_back(i); + } + if (i < count_s || i >= 2*count_s) + { + ion_list.push_back(i); + } + if (spec[i]->lm > log_min) + { + sit_M[i] = under(spec[i]->lm); + } + } + } + } + for (int i = 0; i < count_sit_param; i++) + { + int i0 = sit_params[i]->ispec[0]; + int i1 = sit_params[i]->ispec[1]; + if (sit_IPRSNT[i0] == FALSE || sit_IPRSNT[i1] == FALSE) continue; + param_list.push_back(i); + } +} \ No newline at end of file diff --git a/phreeqcpp/smalldense.cpp b/phreeqcpp/smalldense.cpp new file mode 100644 index 00000000..6cfd7c07 --- /dev/null +++ b/phreeqcpp/smalldense.cpp @@ -0,0 +1,315 @@ +/************************************************************************** + * * + * File : smalldense.c * + * Programmers : Scott D. Cohen and Alan C. Hindmarsh @ LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the implementation file for a generic DENSE linear * + * solver package, intended for small dense matrices. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ + +#include +#include +#include "smalldense.h" +#include "sundialstypes.h" +#include "sundialsmath.h" +/* WARNING don`t include any headers below here */ + +#define ZERO RCONST(0.0) +#define ONE RCONST(1.0) + +/* Implementation */ + +realtype ** +denalloc(integertype n) +{ + integertype j; + realtype **a; + + if (n <= 0) + return (NULL); + + a = (realtype **) malloc(n * sizeof(realtype *)); + if (a == NULL) + return (NULL); + + a[0] = (realtype *) malloc(n * n * sizeof(realtype)); + if (a[0] == NULL) + { + free(a); + return (NULL); + } + + for (j = 1; j < n; j++) + a[j] = a[0] + j * n; + + return (a); +} + +integertype * +denallocpiv(integertype n) +{ + if (n <= 0) + return (NULL); + + return ((integertype *) malloc(n * sizeof(integertype))); +} + +integertype +gefa(realtype ** a, integertype n, integertype * p) +{ + integertype i, j, k, l; + realtype *col_j, *col_k, *diag_k; + realtype temp, mult, a_kj; + booleantype swap; + + /* k = elimination step number */ + + for (k = 0; k < n - 1; k++, p++) + { + + col_k = a[k]; + diag_k = col_k + k; + + /* find l = pivot row number */ + + l = k; + for (i = k + 1; i < n; i++) + if (ABS(col_k[i]) > ABS(col_k[l])) + l = i; + *p = l; + + /* check for zero pivot element */ + + if (col_k[l] == ZERO) + return (k + 1); + + /* swap a(l,k) and a(k,k) if necessary */ + + /*if ( (swap = (l != k) )) { */ + swap = (l != k); + if (swap) + { + temp = col_k[l]; + col_k[l] = *diag_k; + *diag_k = temp; + } + + /* Scale the elements below the diagonal in */ + /* column k by -1.0 / a(k,k). After the above swap, */ + /* a(k,k) holds the pivot element. This scaling */ + /* stores the pivot row multipliers -a(i,k)/a(k,k) */ + /* in a(i,k), i=k+1, ..., n-1. */ + + mult = -ONE / (*diag_k); + for (i = k + 1; i < n; i++) + col_k[i] *= mult; + + /* row_i = row_i - [a(i,k)/a(k,k)] row_k, i=k+1, ..., n-1 */ + /* row k is the pivot row after swapping with row l. */ + /* The computation is done one column at a time, */ + /* column j=k+1, ..., n-1. */ + + for (j = k + 1; j < n; j++) + { + + col_j = a[j]; + a_kj = col_j[l]; + + /* Swap the elements a(k,j) and a(k,l) if l!=k. */ + + if (swap) + { + col_j[l] = col_j[k]; + col_j[k] = a_kj; + } + + /* a(i,j) = a(i,j) - [a(i,k)/a(k,k)]*a(k,j) */ + /* a_kj = a(k,j), col_k[i] = - a(i,k)/a(k,k) */ + + if (a_kj != ZERO) + { + for (i = k + 1; i < n; i++) + col_j[i] += a_kj * col_k[i]; + } + } + } + + /* set the last pivot row to be n-1 and check for a zero pivot */ + + *p = n - 1; + if (a[n - 1][n - 1] == ZERO) + return (n); + + /* return 0 to indicate success */ + + return (0); +} + +void +gesl(realtype ** a, integertype n, integertype * p, realtype * b) +{ + integertype k, l, i; + realtype mult, *col_k; + + /* Solve Ly = Pb, store solution y in b */ + + for (k = 0; k < n - 1; k++) + { + l = p[k]; + mult = b[l]; + if (l != k) + { + b[l] = b[k]; + b[k] = mult; + } + col_k = a[k]; + for (i = k + 1; i < n; i++) + b[i] += mult * col_k[i]; + } + + /* Solve Ux = y, store solution x in b */ + + for (k = n - 1; k >= 0; k--) + { + col_k = a[k]; + b[k] /= col_k[k]; + mult = -b[k]; + for (i = 0; i < k; i++) + b[i] += mult * col_k[i]; + } +} + +void +denzero(realtype ** a, integertype n) +{ + integertype i, j; + realtype *col_j; + + for (j = 0; j < n; j++) + { + col_j = a[j]; + for (i = 0; i < n; i++) + col_j[i] = ZERO; + } +} + +void +dencopy(realtype ** a, realtype ** b, integertype n) +{ + integertype i, j; + realtype *a_col_j, *b_col_j; + + for (j = 0; j < n; j++) + { + a_col_j = a[j]; + b_col_j = b[j]; + for (i = 0; i < n; i++) + b_col_j[i] = a_col_j[i]; + } + +} + +void +denscale(realtype c, realtype ** a, integertype n) +{ + integertype i, j; + realtype *col_j; + + for (j = 0; j < n; j++) + { + col_j = a[j]; + for (i = 0; i < n; i++) + col_j[i] *= c; + } +} + +void +denaddI(realtype ** a, integertype n) +{ + integertype i; + + for (i = 0; i < n; i++) + a[i][i] += ONE; +} + +void +denfreepiv(integertype * p) +{ + free(p); +} + +void +denfree(realtype ** a) +{ + free(a[0]); + free(a); +} + +void +denprint(realtype ** a, integertype n) +{ +#if !defined(R_SO) + integertype i, j; + printf("\n"); + for (i = 0; i < n; i++) + { + for (j = 0; j < n; j++) + { + printf("%10g", (double) a[j][i]); + } + printf("\n"); + } + printf("\n"); +#endif +} diff --git a/phreeqcpp/smalldense.h b/phreeqcpp/smalldense.h new file mode 100644 index 00000000..f7a3e79a --- /dev/null +++ b/phreeqcpp/smalldense.h @@ -0,0 +1,261 @@ +/************************************************************************** + * * + * File : smalldense.h * + * Programmers : Scott D. Cohen and Alan C. Hindmarsh @ LLNL * + * Version of : 26 June 2002 * + *----------------------------------------------------------------- * + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *----------------------------------------------------------------- * + * This is the header file for a generic DENSE linear solver * + * package, intended for small dense matrices. These routines * + * use the type realtype** for dense matrix arguments. * + * * + * These routines begin with "den" (except for the factor and * + * solve routines which are called gefa and gesl, respectively). * + * The underlying matrix storage is described in the * + * documentation for denalloc. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ +#ifndef _smalldense_h +#define _smalldense_h + +#include "sundialstypes.h" + + +/****************************************************************** + * * + * Function : denalloc * + * Usage : realtype **a; * + * a = denalloc(n); * + * if (a == NULL) ... memory request failed * + *----------------------------------------------------------------* + * denalloc(n) allocates storage for an n by n dense matrix. It * + * returns a pointer to the newly allocated storage if * + * successful. If the memory request cannot be satisfied, then * + * denalloc returns NULL. The underlying type of the dense matrix * + * returned is realtype **. If we allocate a dense matrix * + * realtype **a by a = denalloc(n), then a[j][i] references the * + * (i,j)th element of the matrix a, 0 <= i,j <= n-1, and a[j] is * + * a pointer to the first element in the jth column of a. * + * The location a[0] contains a pointer to n^2 contiguous * + * locations which contain the elements of a. * + * * + ******************************************************************/ + + realtype **denalloc(integertype n); + + +/****************************************************************** + * * + * Function : denallocpiv * + * Usage : integertype *pivot; * + * pivot = denallocpiv(n); * + * if (pivot == NULL) ... memory request failed * + *----------------------------------------------------------------* + * denallocpiv(n) allocates an array of n integertype. It returns * + * a pointer to the first element in the array if successful. * + * It returns NULL if the memory request could not be satisfied. * + * * + ******************************************************************/ + + integertype *denallocpiv(integertype n); + + +/****************************************************************** + * * + * Function : gefa * + * Usage : integertype ier; * + * ier = gefa(a,n,p); * + * if (ier > 0) ... zero element encountered during * + * the factorization * + *----------------------------------------------------------------* + * gefa(a,n,p) factors the n by n dense matrix a. It overwrites * + * the elements of a with its LU factors and keeps track of the * + * pivot rows chosen in the pivot array p. * + * * + * A successful LU factorization leaves the matrix a and the * + * pivot array p with the following information: * + * * + * (1) p[k] contains the row number of the pivot element chosen * + * at the beginning of elimination step k, k=0, 1, ..., n-1. * + * * + * (2) If the unique LU factorization of a is given by Pa = LU, * + * where P is a permutation matrix, L is a lower triangular * + * matrix with all 1's on the diagonal, and U is an upper * + * triangular matrix, then the upper triangular part of a * + * (including its diagonal) contains U and the strictly lower * + * triangular part of a contains the multipliers, I-L. * + * * + * gefa returns 0 if successful. Otherwise it encountered a zero * + * diagonal element during the factorization. In this case it * + * returns the column index (numbered from one) at which it * + * encountered the zero. * + * * + ******************************************************************/ + + integertype gefa(realtype ** a, integertype n, integertype * p); + + +/****************************************************************** + * * + * Function : gesl * + * Usage : realtype *b; * + * ier = gefa(a,n,p); * + * if (ier == 0) gesl(a,n,p,b); * + *----------------------------------------------------------------* + * gesl(a,n,p,b) solves the n by n linear system ax = b. It * + * assumes that a has been LU factored and the pivot array p has * + * been set by a successful call to gefa(a,n,p). The solution x * + * is written into the b array. * + * * + ******************************************************************/ + + void gesl(realtype ** a, integertype n, integertype * p, realtype * b); + + +/****************************************************************** + * * + * Function : denzero * + * Usage : denzero(a,n); * + *----------------------------------------------------------------* + * denzero(a,n) sets all the elements of the n by n dense matrix * + * a to be 0.0. * + * * + ******************************************************************/ + + void denzero(realtype ** a, integertype n); + + +/****************************************************************** + * * + * Function : dencopy * + * Usage : dencopy(a,b,n); * + *----------------------------------------------------------------* + * dencopy(a,b,n) copies the n by n dense matrix a into the * + * n by n dense matrix b. * + * * + ******************************************************************/ + + void dencopy(realtype ** a, realtype ** b, integertype n); + + +/****************************************************************** + * * + * Function : denscale * + * Usage : denscale(c,a,n); * + *----------------------------------------------------------------* + * denscale(c,a,n) scales every element in the n by n dense * + * matrix a by c. * + * * + ******************************************************************/ + + void denscale(realtype c, realtype ** a, integertype n); + + +/****************************************************************** + * * + * Function : denaddI * + * Usage : denaddI(a,n); * + *----------------------------------------------------------------* + * denaddI(a,n) increments the n by n dense matrix a by the * + * identity matrix. * + * * + ******************************************************************/ + + void denaddI(realtype ** a, integertype n); + + +/****************************************************************** + * * + * Function : denfreepiv * + * Usage : denfreepiv(p); * + *----------------------------------------------------------------* + * denfreepiv(p) frees the pivot array p allocated by * + * denallocpiv. * + * * + ******************************************************************/ + + void denfreepiv(integertype * p); + + +/****************************************************************** + * * + * Function : denfree * + * Usage : denfree(a); * + *----------------------------------------------------------------* + * denfree(a) frees the dense matrix a allocated by denalloc. * + * * + ******************************************************************/ + + void denfree(realtype ** a); + + +/****************************************************************** + * * + * Function : denprint * + * Usage : denprint(a,n); * + *----------------------------------------------------------------* + * denprint(a,n) prints the n by n dense matrix a to standard * + * output as it would normally appear on paper. It is intended as * + * a debugging tool with small values of n. The elements are * + * printed using the %g option. A blank line is printed before * + * and after the matrix. * + * * + ******************************************************************/ + + void denprint(realtype ** a, integertype n); + + +#endif +/* +#ifdef __cplusplus +} +#endif +*/ diff --git a/phreeqcpp/spread.cpp b/phreeqcpp/spread.cpp new file mode 100644 index 00000000..8720fd56 --- /dev/null +++ b/phreeqcpp/spread.cpp @@ -0,0 +1,1525 @@ +#ifndef boolean +typedef unsigned char boolean; +#endif +#include "Phreeqc.h" +#include "phqalloc.h" +#include "Solution.h" +#include "Utils.h" + +#define STRING 11 +#define NUMBER 12 +#define MIXED 13 +#define EOL 14 + +#define OPTION_EOF -1 +#define OPTION_KEYWORD -2 +#define OPTION_ERROR -3 +#define OPTION_DEFAULT -4 +#define OPT_1 -5 +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +read_solution_spread(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads solution data + * + * Arguments: + * none + * + * Returns: + * KEYWORD if keyword encountered, input_error may be incremented if + * a keyword is encountered in an unexpected position + * EOF if eof encountered while reading mass balance concentrations + * ERROR if error occurred reading data + * + */ + struct spread_row *heading, *row_ptr, *units; + int count, strings, numbers; + int spread_lines; + char *ptr; + struct defaults soln_defaults; + int return_value, opt; + char *next_char; + const char *opt_list[] = { + "temp", /* 0 */ + "temperature", /* 1 */ + "dens", /* 2 */ + "density", /* 3 */ + "units", /* 4 */ + "redox", /* 5 */ + "ph", /* 6 */ + "pe", /* 7 */ + "unit", /* 8 */ + "isotope", /* 9 */ + "water", /* 10 */ + "isotope_uncertainty", /* 11 */ + "uncertainty", /* 12 */ + "uncertainties", /* 13 */ + "pressure", /* 14 */ + "press" /* 15 */ + }; + int count_opt_list = 16; +/* + * Initialize defaults + */ + soln_defaults.temp = 25; + soln_defaults.density = 1.0; + soln_defaults.calc_density = false; + soln_defaults.units = string_hsave("mmol/kgw"); + soln_defaults.redox = string_hsave("pe"); + soln_defaults.ph = 7.0; + soln_defaults.pe = 4.0; + soln_defaults.water = 1.0; + soln_defaults.pressure = 1.0; + +#ifdef PHREEQCI_GUI + free_spread(); +#endif + + + /* fill in soln_defaults.iso */ + soln_defaults.count_iso = count_iso_defaults; + soln_defaults.iso = (struct iso *) PHRQ_malloc((size_t) soln_defaults.count_iso * + sizeof(struct iso)); + if (soln_defaults.iso == NULL) + malloc_error(); + /* all iso[i].name is hsave'd, so no conflicts */ + memcpy(soln_defaults.iso, iso_defaults, + (size_t) soln_defaults.count_iso * sizeof(struct iso)); + + heading = NULL; + units = NULL; + return_value = UNKNOWN; + spread_lines = 0; + CParser parser(this->phrq_io); +/* + * Loop on solutions + */ + for (;;) + { + std::string token, token1; + opt = get_option(opt_list, count_opt_list, &next_char); + if (spread_lines == 0 && opt != OPTION_DEFAULT) + { + row_ptr = string_to_spread_row(line); + ptr = line; + count = numbers = strings = 0; + int j; + while (((j = copy_token(token, &ptr)) != EMPTY)) + { + count++; + if (j == UPPER || j == LOWER) + strings++; + if (j == DIGIT) + numbers++; + } + /* + * Is 2nd token all number + */ + ptr = line; + copy_token(token, &ptr); + j = copy_token(token, &ptr); + bool num = false; + if (j == DIGIT) + { + strtod(token.c_str(), &ptr); + int j1 = copy_token(token1, &ptr); + if (j1 != EMPTY) + { + num = FALSE; + } + else + { + num = TRUE; + } + } + + /* + * Starts with hyphen + */ + ptr = line; + copy_token(token, &ptr); + if (token[0] == '-') + { + opt = opt; + } + else + { + switch (opt) + { + case 0: /* temp */ + case 1: /* temperature */ + case 2: /* dens */ + case 3: /* density */ + case 10: /* water */ + if ((count == 2 || count == 3) && num == TRUE) + { + opt = opt; + } + else + { + opt = OPTION_DEFAULT; + } + break; + case 6: /* ph */ + case 7: /* pe */ + if ((count == 2 || count == 3 || count == 4) + && num == TRUE) + { + opt = opt; + } + else + { + opt = OPTION_DEFAULT; + } + break; + case 5: /* redox */ + case 4: /* units */ + case 8: /* unit */ + if (count == 2) + { + opt = opt; + } + else + { + opt = OPTION_DEFAULT; + } + break; + case 9: /* isotope */ + if (row_ptr->count > 4) + { + opt = OPTION_DEFAULT; + } + else + { + opt = opt; + } + break; + case 11: /* isotope_uncertainty */ + case 12: /* uncertainty */ + case 13: /* uncertainties */ + if (row_ptr->count > 3) + { + opt = OPTION_DEFAULT; + } + else + { + opt = opt; + } + break; + case 14: /* pressure */ + case 15: /* press */ + sscanf(next_char, SCANFORMAT, &(soln_defaults.pressure)); + break; + } + } + spread_row_free(row_ptr); + } + if (opt == OPTION_DEFAULT) + { + if (spread_lines == 0) + { + opt = 100; + } + spread_lines++; + } + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SOLUTION keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case OPTION_DEFAULT: /* solution definition */ + row_ptr = string_to_spread_row(line); + if (spread_lines == 2) + { + numbers = 0; + strings = 0; + //for (int i = 0; i < heading->count; i++) + for (int i = 0; i < row_ptr->count; i++) + { + if (row_ptr->type_vector[i] == STRING) + { + strings++; + } + else if (row_ptr->type_vector[i] == NUMBER) + { + numbers++; + } + } + if (numbers == 0) + { + units = row_ptr; + break; + } + } + spread_row_to_solution(heading, units, row_ptr, soln_defaults); +#ifdef PHREEQCI_GUI + add_row(row_ptr); +#endif + spread_row_free(row_ptr); + break; + case 0: /* temperature */ + case 1: + sscanf(next_char, SCANFORMAT, &(soln_defaults.temp)); + break; + case 2: /* density */ + case 3: + //sscanf(next_char, SCANFORMAT, &(soln_defaults.density)); + { + copy_token(token, &next_char); + if (sscanf(token.c_str(), SCANFORMAT, &dummy) != 1) + { + error_msg("Expecting numeric value for density.", PHRQ_io::OT_CONTINUE); + error_msg(line_save, PHRQ_io::OT_CONTINUE); + input_error++; + } + else + { + soln_defaults.density = dummy; + } + int j = copy_token(token, &next_char); + if (j != EMPTY) + { + if (token[0] != 'c' && token[0] != 'C') + { + error_msg("Only option following density is c[alculate].", PHRQ_io::OT_CONTINUE); + error_msg(line_save, PHRQ_io::OT_CONTINUE); + input_error++; + } + else + { + soln_defaults.calc_density = true; + } + } + } + break; + case 4: /* units */ + case 8: /* unit */ + if (copy_token(token, &next_char) == EMPTY) + break; + { + if (check_units(token, FALSE, FALSE, NULL, TRUE) == OK) + { + soln_defaults.units = string_hsave(token.c_str()); + } + else + { + input_error++; + } + } + break; + case 5: /* redox */ + if (copy_token(token, &next_char) == EMPTY) + break; + if (parser.parse_couple(token) == OK) + { + soln_defaults.redox = string_hsave(token.c_str()); + } + else + { + input_error++; + } + break; + case 6: /* ph */ + copy_token(token, &next_char); + sscanf(token.c_str(), SCANFORMAT, &(soln_defaults.ph)); + if (copy_token(token, &next_char) != EMPTY) + { + warning_msg + ("Not possible to use phase name or saturation index in definition of default pH in SOLUTION_SPREAD."); + } + break; + case 7: /* pe */ + copy_token(token, &next_char); + sscanf(token.c_str(), SCANFORMAT, &(soln_defaults.pe)); + if (copy_token(token, &next_char) != EMPTY) + { + warning_msg + ("Not possible to use phase name or saturation index in definition of default pe in SOLUTION_SPREAD."); + } + break; + case 11: /* isotope_uncertainty */ + case 12: /* uncertainty */ + case 13: /* uncertainties */ + { + if (copy_token(token, &next_char) != DIGIT) + { + input_error++; + error_string = sformatf( "Expected isotope name to" + " begin with an isotopic number."); + error_msg(error_string, CONTINUE); + error_string = sformatf( "In read_solution_spread isotope_uncertainty\n"); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "token: ", token.c_str()); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "next_char: ", next_char); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "line_save: ", line_save); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + continue; + } + int i; + for (i = 0; i < soln_defaults.count_iso; i++) + { + if (strcmp(token.c_str(), soln_defaults.iso[i].name) == 0) + { + break; + } + } + if (i == soln_defaults.count_iso) + { + soln_defaults.iso = + (struct iso *) PHRQ_realloc(soln_defaults.iso, + (size_t) (i + + 1) * + sizeof(struct iso)); + if (soln_defaults.iso == NULL) + { + malloc_error(); + } + else + { + soln_defaults.iso[i].name = string_hsave(token.c_str()); + soln_defaults.iso[i].value = NAN; + soln_defaults.iso[i].uncertainty = NAN; + soln_defaults.count_iso++; + } + } + + /* read and store isotope ratio uncertainty */ + int j; + if ((j = copy_token(token, &next_char)) != EMPTY) + { + if (j != DIGIT) + { + input_error++; + error_string = sformatf( + "Expected numeric value for uncertainty in isotope ratio."); + error_msg(error_string, CONTINUE); + continue; + } + else + { + sscanf(token.c_str(), SCANFORMAT, + &(soln_defaults.iso[i].uncertainty)); + } + } + else + { + soln_defaults.iso[i].uncertainty = NAN; + } + } + break; + case 10: /* water */ + { + int j = copy_token(token, &next_char); + if (j != DIGIT) + { + input_error++; + error_string = sformatf( + "Expected numeric value for mass of water in solution."); + error_msg(error_string, CONTINUE); + } + else + { + sscanf(token.c_str(), SCANFORMAT, &(soln_defaults.water)); + } + } + break; + case 9: /* isotope */ + { + if (copy_token(token, &next_char) != DIGIT) + { + input_error++; + error_string = sformatf( "Expected isotope name to" + " begin with an isotopic number."); + error_msg(error_string, CONTINUE); + error_string = sformatf( "In read_solution_spread isotope\n"); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "token: ", token.c_str()); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "next_char: ", next_char); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "line_save: ", line_save); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + continue; + } + int i; + for (i = 0; i < soln_defaults.count_iso; i++) + { + if (strcmp(token.c_str(), soln_defaults.iso[i].name) == 0) + { + break; + } + } + if (i == soln_defaults.count_iso) + { + soln_defaults.iso = + (struct iso *) PHRQ_realloc(soln_defaults.iso, + (size_t) (i + + 1) * + sizeof(struct iso)); + if (soln_defaults.iso == NULL) + { + malloc_error(); + } + else + { + soln_defaults.iso[i].name = string_hsave(token.c_str()); + soln_defaults.iso[i].value = NAN; + soln_defaults.iso[i].uncertainty = NAN; + soln_defaults.count_iso++; + } + } + /* read and store isotope ratio */ + if (copy_token(token, &next_char) != DIGIT) + { + input_error++; + error_string = sformatf( + "Expected numeric value for default isotope ratio."); + error_msg(error_string, CONTINUE); + break; + } + sscanf(token.c_str(), SCANFORMAT, &(soln_defaults.iso[i].value)); + /* read and store isotope ratio uncertainty */ + int j; + if ((j = copy_token(token, &next_char)) != EMPTY) + { + if (j != DIGIT) + { + input_error++; + error_string = sformatf( + "Expected numeric value for uncertainty in isotope ratio."); + error_msg(error_string, CONTINUE); + continue; + } + else + { + sscanf(token.c_str(), SCANFORMAT, + &(soln_defaults.iso[i].uncertainty)); + } + } + } + break; + case 14: /* pressure */ + sscanf(next_char, SCANFORMAT, &(soln_defaults.pressure)); + break; + case 100: /* read headings */ + heading = string_to_spread_row(line); + { + int i; + for (i = 0; i < heading->count; i++) + { + while (replace(" ", "", heading->char_vector[i]) == TRUE); + while (replace(",", "_", heading->char_vector[i]) == TRUE); + } + } + break; + } + if (return_value == EOF || return_value == KEYWORD) + break; + } +#ifdef PHREEQCI_GUI + if (heading) + { + assert(g_spread_sheet.heading == NULL); + g_spread_sheet.heading = copy_row(heading); + } + if (units) + { + assert(g_spread_sheet.units == NULL); + g_spread_sheet.units = copy_row(units); + } + copy_defaults(&g_spread_sheet.defaults, &soln_defaults); +#endif + spread_row_free(heading); + spread_row_free(units); + + soln_defaults.iso = (struct iso *) free_check_null(soln_defaults.iso); + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +spread_row_to_solution(struct spread_row *heading, struct spread_row *units, + struct spread_row *data, struct defaults defaults) +/* ---------------------------------------------------------------------- */ +{ + Keywords::KEYWORDS next_keyword_save; + int n_user, n_user_end; + std::string description; + std::string token, token1, string; + CParser parser(this->phrq_io); + + int return_value, opt; + char *next_char; + const char *opt_list[] = { + "temp", /* 0 */ + "temperature", /* 1 */ + "dens", /* 2 */ + "density", /* 3 */ + "units", /* 4 */ + "redox", /* 5 */ + "ph", /* 6 */ + "pe", /* 7 */ + "unit", /* 8 */ + "isotope", /* 9 */ + "water", /* 10 */ + "description", /* 11 */ + "desc", /* 12 */ + "descriptor", /* 13 */ + "pressure", /* 14 */ + "press", /* 15 */ + "potential" /* 16 */ + }; + int count_opt_list = 17; + +/* + * look for solution number + */ + n_user = n_user_end = -1; + { + int i; + for (i = 0; i < heading->count; i++) + { + if (strcmp_nocase(heading->char_vector[i], "number") == 0) + { + break; + } + } + if (i == heading->count || data->type_vector[i] == EMPTY + || data->count <= i) + { + n_user = -1; + } + else if (data->type_vector[i] == STRING) + { + input_error++; + error_string = sformatf( + "Expected solution number or number range in 'number' column, found: %s.", + data->char_vector[i]); + error_msg(error_string, CONTINUE); + } + else + { + string = "solution_s "; + string.append( data->char_vector[i] ); + next_keyword_save = next_keyword; + next_keyword = Keywords::KEY_SOLUTION_SPREAD; + cxxNumKeyword nk; + nk.read_number_description(string); + n_user = nk.Get_n_user(); + n_user_end = nk.Get_n_user_end(); + description = nk.Get_description(); + next_keyword = next_keyword_save; + Rxn_new_solution.insert(n_user); + } + } +/* + * set up solution + */ + + cxxSolution temp_solution; + temp_solution.Set_n_user(n_user); + temp_solution.Set_n_user_end(n_user_end); + temp_solution.Set_new_def(true); + temp_solution.Create_initial_data(); + cxxISolution * initial_data_ptr = temp_solution.Get_initial_data(); + if (use.Get_solution_in() == FALSE) + { + use.Set_solution_in(true); + use.Set_n_solution_user(n_user); + } +/* + * Set default ph, temp, density, pe, units + */ + temp_solution.Set_description(description); + temp_solution.Set_tc(defaults.temp); + temp_solution.Set_patm(defaults.pressure); + temp_solution.Set_potV(0); + temp_solution.Set_ph(defaults.ph); + temp_solution.Set_density(defaults.density); + initial_data_ptr->Set_calc_density(defaults.calc_density); + temp_solution.Set_pe(defaults.pe); + temp_solution.Set_mass_water(defaults.water); + temp_solution.Set_ah2o(1.0); + temp_solution.Set_mu(1e-7); + initial_data_ptr->Set_units(defaults.units); + initial_data_ptr->Set_default_pe(defaults.redox); + { + cxxChemRxn temp_chem_reaction; + initial_data_ptr->Get_pe_reactions()[defaults.redox] = temp_chem_reaction; + } +/* + * Read concentration data + */ + return_value = UNKNOWN; + for (int i = 0; i < heading->count; i++) + { + if (strcmp_nocase(heading->char_vector[i], "number") == 0) + continue; + if (strcmp_nocase(heading->char_vector[i], "uncertainty") == 0) + continue; + if (strcmp_nocase(heading->char_vector[i], "uncertainties") == 0) + continue; + if (strcmp_nocase(heading->char_vector[i], "isotope_uncertainty") == + 0) + continue; + /* + * Copy in element name + */ + if (heading->type_vector[i] == EMPTY) + continue; + string = heading->char_vector[i]; + string.append(" "); + /* + * Copy in concentration data + */ + if (i >= data->count || data->type_vector[i] == EMPTY) + continue; + string.append(data->char_vector[i]); + string.append(" "); + /* + * Copy in concentration data + */ + if (units != NULL && i < units->count + && units->type_vector[i] != EMPTY) + { + string.append(units->char_vector[i]); + } +/* + * Parse string just like read_solution input + */ + char * char_string = string_duplicate(string.c_str()); + next_char = char_string; + opt = get_option_string(opt_list, count_opt_list, &next_char); + if (opt == OPTION_DEFAULT && heading->type_vector[i] == NUMBER) + { + opt = 9; + } + switch (opt) + { + case OPTION_EOF: /* end of file */ + return_value = EOF; + break; + case OPTION_KEYWORD: /* keyword */ + return_value = KEYWORD; + break; + case OPTION_ERROR: + input_error++; + error_msg("Unknown input in SOLUTION keyword.", CONTINUE); + error_msg(line_save, CONTINUE); + break; + case 0: /* temperature */ + case 1: + sscanf(next_char, SCANFORMAT, &dummy); + temp_solution.Set_tc(dummy); + break; + case 2: /* density */ + case 3: + { + int j = copy_token(token, &next_char); + sscanf(token.c_str(), SCANFORMAT, &dummy); + temp_solution.Set_density(dummy); + j = copy_token(token, &next_char); + if (j != EMPTY) + { + if (token[0] != 'c' && token[0] != 'C') + { + error_msg("Only option following density is c[alculate].", PHRQ_io::OT_CONTINUE); + error_msg(line_save, PHRQ_io::OT_CONTINUE); + input_error++; + } + else + { + initial_data_ptr->Set_calc_density(true); + } + } + } + break; + case 4: /* units */ + case 8: /* unit */ + if (copy_token(token, &next_char) == EMPTY) + break; + { + if (check_units(token, false, false, initial_data_ptr->Get_units().c_str(), true) == OK) + { + initial_data_ptr->Set_units(token); + } + else + { + input_error++; + } + } + break; + case 5: /* redox */ + if (copy_token(token, &next_char) == EMPTY) + break; + if (parser.parse_couple(token) == OK) + { + const char * pe_str = string_hsave(token.c_str()); + initial_data_ptr->Set_default_pe(pe_str); + cxxChemRxn temp_chem_reaction; + initial_data_ptr->Get_pe_reactions()[token] = temp_chem_reaction; + } + else + { + input_error++; + } + break; + case 6: /* ph */ + { + cxxISolutionComp temp_comp(this->phrq_io); + if (temp_comp.read(char_string, &temp_solution) == CParser::PARSER_ERROR) + { + input_error++; + break; + } + + temp_solution.Set_ph(temp_comp.Get_input_conc()); + if (temp_comp.Get_equation_name().size() == 0) + { + break; + + } + temp_comp.Set_description("H(1)"); + initial_data_ptr->Get_comps()[temp_comp.Get_description()] = temp_comp; + } + break; + case 7: /* pe */ + { + cxxISolutionComp temp_comp(this->phrq_io); + if (temp_comp.read(char_string, &temp_solution) == CParser::PARSER_ERROR) + { + input_error++; + break; + } + temp_solution.Set_pe(temp_comp.Get_input_conc()); + if (temp_comp.Get_equation_name().size() == 0) + { + break; + } + temp_comp.Set_description("E"); + initial_data_ptr->Get_comps()[temp_comp.Get_description()] = temp_comp; + } + break; + case 9: /* isotope */ + { + next_char = char_string; + cxxSolutionIsotope temp_isotope; + if (copy_token(token, &next_char) != CParser::TT_DIGIT) + { + input_error++; + error_string = sformatf( "Expected isotope name to" + " begin with an isotopic number."); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "In spread_row_to_solution isotope\n"); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "token: ", token.c_str()); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "next_char: ", next_char); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "char_string: ", char_string); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + error_string = sformatf( "\t%s\t%s\n", "line_save: ", line_save); + error_msg(error_string, PHRQ_io::OT_CONTINUE); +//struct spread_row +//{ +// int count; +// int empty, string, number; +// char **char_vector; +// LDBLE *d_vector; +// int *type_vector; +//}; + error_string = sformatf("Heading spread_row\n"); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + if (heading) + { + for (int ii = 0; ii < heading->count; ii++) + { + error_string = sformatf("%d\t%s\n",ii,heading->char_vector[ii]); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + } + } + else + { + error_msg("heading is null", PHRQ_io::OT_CONTINUE); + } + + error_string = sformatf("Data spread_row\n"); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + if (data) + { + for (int ii = 0; ii < data->count; ii++) + { + error_string = sformatf("%d\t%s\t%d\n",ii,data->char_vector[ii],data->type_vector[ii]); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + } + } + else + { + error_msg("Data is null", PHRQ_io::OT_CONTINUE); + } + error_string = sformatf("Units spread_row\n"); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + if (units) + { + for (int ii = 0; ii < units->count; ii++) + { + error_string = sformatf("%d\t%s\n",ii,units->char_vector[ii]); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + } + } + else + { + error_msg("Units is null", PHRQ_io::OT_CONTINUE); + } + free_check_null(char_string); + continue; + } + temp_isotope.Set_isotope_name(token.c_str()); + + /* read and save element name */ + { + char *temp_iso_name = string_duplicate(token.c_str()); + char *ptr1 = temp_iso_name; + get_num(&ptr1, &dummy); + temp_isotope.Set_isotope_number(dummy); + if (ptr1[0] == '\0' || isupper((int) ptr1[0]) == FALSE) + { + error_msg("Expecting element name.", PHRQ_io::OT_CONTINUE); + error_msg(line_save, PHRQ_io::OT_CONTINUE); + input_error++; + temp_iso_name = (char*)free_check_null(temp_iso_name); + char_string = (char*)free_check_null(char_string); + return (CParser::PARSER_ERROR); + } + temp_isotope.Set_elt_name(ptr1); + temp_iso_name = (char*)free_check_null(temp_iso_name); + } + /* read and store isotope ratio */ + if (copy_token(token, &next_char) != CParser::TT_DIGIT) + { + input_error++; + error_string = sformatf( + "Expected numeric value for isotope ratio."); + error_msg(error_string, CONTINUE); + free_check_null(char_string); + continue; + } + sscanf(token.c_str(), SCANFORMAT, &dummy); + temp_isotope.Set_ratio(dummy); + temp_isotope.Set_ratio_uncertainty(NAN); + + /* read and store isotope ratio uncertainty */ + int j; + if ((j = copy_token(token, &next_char)) != CParser::TT_EMPTY) + { + if (j != DIGIT) + { + input_error++; + error_string = sformatf( + "Expected numeric value for uncertainty in isotope ratio."); + error_msg(error_string, PHRQ_io::OT_CONTINUE); + free_check_null(char_string); + continue; + } + sscanf(token.c_str(), SCANFORMAT, &dummy); + temp_isotope.Set_ratio_uncertainty(dummy); + } + temp_solution.Get_isotopes()[temp_isotope.Get_isotope_name()] = temp_isotope; + } + break; + case 10: /* water */ + { + //next_char = char_string; + //int j = copy_token(token, &next_char); // read identifier "water" + int j = copy_token(token, &next_char); + if (j == EMPTY) + { + temp_solution.Set_mass_water(1.0); + } + else if (j != DIGIT) + { + input_error++; + error_string = sformatf( + "Expected numeric value for mass of water in solution."); + error_msg(error_string, CONTINUE); + } + else + { + sscanf(token.c_str(), SCANFORMAT, &dummy); + temp_solution.Set_mass_water(dummy); + } + } + break; + case 11: /* description */ + case 12: /* desc */ + case 13: /* descriptor */ + { + temp_solution.Set_description(next_char); + } + break; + case 14: /* pressure */ + case 15: /* press */ + { + if (sscanf(next_char, SCANFORMAT, &dummy) == 1) + { + temp_solution.Set_patm(dummy); + } + } + break; + case 16: /* pote, V */ + { + if (sscanf(next_char, SCANFORMAT, &dummy) == 1) + { + temp_solution.Set_potV(dummy); + } + } + break; + case OPTION_DEFAULT: +/* + * Read concentration + */ + { + next_char = char_string; + if (copy_token(token, &next_char) == LOWER) + { + free_check_null(char_string); + continue; + } + cxxISolutionComp temp_comp(this->phrq_io); + if (temp_comp.read(char_string, &temp_solution) == CParser::PARSER_ERROR) + { +#ifdef SKIP + input_error++; + break; +#endif + } + initial_data_ptr->Get_comps()[temp_comp.Get_description()] = temp_comp; + if (temp_comp.Get_pe_reaction().size() > 0) + { + cxxChemRxn temp_chem_reaction; + initial_data_ptr->Get_pe_reactions()[temp_comp.Get_pe_reaction()] = temp_chem_reaction; + } + } + break; + } + free_check_null(char_string); + if (return_value == EOF || return_value == KEYWORD) + break; + } +/* + * fix up default units and default pe + */ + std::map < std::string, cxxISolutionComp >::iterator it; + for (it = initial_data_ptr->Get_comps().begin(); it != initial_data_ptr->Get_comps().end(); it++) + { + token = it->first; + Utilities::str_tolower(token); + if (it->second.Get_units().size() == 0) + { + it->second.Set_units(initial_data_ptr->Get_units().c_str()); + } + else + { + bool alk = false; + if (strstr(token.c_str(), "alk") == token.c_str()) + alk = true; + std::string token1 = it->second.Get_units(); + if (check_units(token1, alk, true, initial_data_ptr->Get_units().c_str(), true) == CParser::PARSER_ERROR) + { + input_error++; + } + else + { + it->second.Set_units(token1.c_str()); + } + } + if (it->second.Get_pe_reaction().size() == 0) + { + it->second.Set_pe_reaction(initial_data_ptr->Get_default_pe()); + } + } + if (n_user >= 0) + { + Rxn_solution_map[n_user] = temp_solution; + } + else + { + unnumbered_solutions.push_back(temp_solution); + } + return (return_value); +} +/* ---------------------------------------------------------------------- */ +struct spread_row * Phreeqc:: +string_to_spread_row(char *string) +/* ---------------------------------------------------------------------- */ +{ + int j, l; + /* possible memory error if length of line is smaller than previous line */ + char *token; + char *ptr; + struct spread_row *spread_row_ptr = NULL; +/* + * Allocate space + */ + token = (char *) PHRQ_malloc(strlen(line) + 1); + if (token == NULL) + { + malloc_error(); + return spread_row_ptr; + } + spread_row_ptr = + (struct spread_row *) PHRQ_malloc((size_t) sizeof(struct spread_row)); + if (spread_row_ptr == NULL) + { + malloc_error(); + return spread_row_ptr; + } + spread_row_ptr->char_vector = + (char **) PHRQ_malloc((size_t) spread_length * sizeof(char *)); + if (spread_row_ptr->char_vector == NULL) + { + malloc_error(); + return spread_row_ptr; + } + spread_row_ptr->d_vector = + (LDBLE *) PHRQ_malloc((size_t) spread_length * sizeof(LDBLE)); + if (spread_row_ptr->d_vector == NULL) + { + malloc_error(); + return spread_row_ptr; + } + spread_row_ptr->type_vector = + (int *) PHRQ_malloc((size_t) spread_length * sizeof(int)); + if (spread_row_ptr->type_vector == NULL) + { + malloc_error(); + return spread_row_ptr; + } + spread_row_ptr->count = 0; + spread_row_ptr->empty = 0; + spread_row_ptr->string = 0; + spread_row_ptr->number = 0; + ptr = string; +/* + * Split by tabs, reallocate space + */ + for (;;) + { + if (spread_row_ptr->count + 1 > spread_length) + { + spread_length *= 2; + + spread_row_ptr->char_vector = + (char **) PHRQ_realloc(spread_row_ptr->char_vector, + (size_t) spread_length * sizeof(char *)); + if (spread_row_ptr->char_vector == NULL) + { + malloc_error(); + return spread_row_ptr; + } + + spread_row_ptr->d_vector = + (LDBLE *) PHRQ_realloc(spread_row_ptr->d_vector, + (size_t) spread_length * sizeof(LDBLE)); + if (spread_row_ptr->d_vector == NULL) + { + malloc_error(); + return spread_row_ptr; + } + + spread_row_ptr->type_vector = + (int *) PHRQ_realloc(spread_row_ptr->type_vector, + (size_t) spread_length * sizeof(int)); + if (spread_row_ptr->type_vector == NULL) + { + malloc_error(); + return spread_row_ptr; + } + } + j = copy_token_tab(token, &ptr, &l); + if (j == EOL) + break; + spread_row_ptr->char_vector[spread_row_ptr->count] = + string_duplicate(token); + spread_row_ptr->d_vector[spread_row_ptr->count] = NAN; + if (j == EMPTY || l == 0) + { + spread_row_ptr->empty++; + spread_row_ptr->type_vector[spread_row_ptr->count] = EMPTY; + } + else if (j == UPPER || j == LOWER) + { + spread_row_ptr->string++; + spread_row_ptr->type_vector[spread_row_ptr->count] = STRING; + } + else if (j == DIGIT) + { + spread_row_ptr->number++; + spread_row_ptr->d_vector[spread_row_ptr->count] = + strtod(token, NULL); + spread_row_ptr->type_vector[spread_row_ptr->count] = NUMBER; + } + else + { + input_error++; + error_msg("Unknown input in string_to_spread_row keyword.", CONTINUE); + error_string = sformatf("\tcopy_token j: %d, token: %s\n", j, token); + error_msg(error_string, CONTINUE); + error_msg(line_save, CONTINUE); + } + spread_row_ptr->count++; + } +/* + * Clean up and return + */ + if (spread_row_ptr->count == 0) + { + spread_row_ptr->char_vector = + (char **) free_check_null(spread_row_ptr->char_vector); + spread_row_ptr->d_vector = + (LDBLE *) free_check_null(spread_row_ptr->d_vector); + spread_row_ptr->type_vector = + (int *) free_check_null(spread_row_ptr->type_vector); + } + else + { +/* Do not realloc to smaller size, memory error */ +/* + spread_row_ptr->char_vector = + (char **) PHRQ_realloc (spread_row_ptr->char_vector, + (size_t) spread_row_ptr->count * + sizeof (char *)); + if (spread_row_ptr->char_vector == NULL) + malloc_error (); + spread_row_ptr->d_vector = + (LDBLE *) PHRQ_realloc (spread_row_ptr->d_vector, + (size_t) spread_row_ptr->count * + sizeof (LDBLE)); + if (spread_row_ptr->d_vector == NULL) + malloc_error (); + spread_row_ptr->type_vector = + (int *) PHRQ_realloc (spread_row_ptr->type_vector, + (size_t) spread_row_ptr->count * sizeof (int)); + if (spread_row_ptr->type_vector == NULL) + malloc_error (); +*/ + } + token = (char *) free_check_null(token); + return (spread_row_ptr); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +spread_row_free(struct spread_row *spread_row_ptr) +/* ---------------------------------------------------------------------- */ +{ + int i; + + if (spread_row_ptr == NULL) + return (OK); + for (i = 0; i < spread_row_ptr->count; i++) + { + spread_row_ptr->char_vector[i] = + (char *) free_check_null(spread_row_ptr->char_vector[i]); + } + + spread_row_ptr->char_vector = + (char **) free_check_null(spread_row_ptr->char_vector); + spread_row_ptr->d_vector = + (LDBLE *) free_check_null(spread_row_ptr->d_vector); + spread_row_ptr->type_vector = + (int *) free_check_null(spread_row_ptr->type_vector); + spread_row_ptr = (struct spread_row *) free_check_null(spread_row_ptr); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +copy_token_tab(char *token_ptr, char **ptr, int *length) +/* ---------------------------------------------------------------------- */ +{ +/* + * Copies from **ptr to *token_ptr until first tab is encountered. + * + * Arguments: + * *token_ptr output, place to store token + * + * **ptr input, character string to read token from + * output, next position after token + * + * length output, length of token + * + * Returns: + * UPPER, + * LOWER, + * DIGIT, + * EMPTY, + * EOL, + * UNKNOWN. + */ + int i, j, return_value; + char c; +/* + * Strip leading spaces + */ + while ((c = **ptr) == ' ') + (*ptr)++; +/* + * Check what we have + */ + if (isupper((int) c) || c == '[') + { + return_value = UPPER; + } + else if (islower((int) c)) + { + return_value = LOWER; + } + else if (isdigit((int) c) || c == '.' || c == '-') + { + return_value = DIGIT; + } + else if (c == '\0') + { + return_value = EOL; + return (return_value); + } + else if (c == '\t') + { + return_value = EMPTY; + } + else + { + return_value = UNKNOWN; + } +/* + * Begin copying to token + */ + i = 0; + for (;;) + { + c = **ptr; + if (c == '\t') + { + (*ptr)++; + break; + } + else if (c == '\0') + { + break; + } + else + { + token_ptr[i] = c; + (*ptr)++; + i++; + } + } + token_ptr[i] = '\0'; + *length = i; +/* + * Strip trailing spaces + */ + for (j = i - 1; j >= 0; j--) + { + if (j != ' ') + break; + } + if (j != i - 1) + { + token_ptr[j + 1] = '\0'; + *length = j + 1; + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +get_option_string(const char **opt_list, int count_opt_list, char **next_char) +/* ---------------------------------------------------------------------- */ +{ +/* + * Read a line and check for options + */ + int j; + int opt_l, opt; + char *opt_ptr; + char option[MAX_LENGTH]; + + opt_ptr = *next_char; + if (opt_ptr[0] == '-') + { + opt_ptr++; + copy_token(option, &opt_ptr, &opt_l); + if (find_option(&(option[1]), &opt, opt_list, count_opt_list, FALSE) + == OK) + { + j = opt; + *next_char = opt_ptr; + } + else + { + error_msg("Unknown option.", CONTINUE); + error_msg(*next_char, CONTINUE); + input_error++; + j = OPTION_ERROR; + } + } + else + { + copy_token(option, &opt_ptr, &opt_l); + if (find_option(&(option[0]), &opt, opt_list, count_opt_list, TRUE) + == OK) + { + j = opt; + *next_char = opt_ptr; + } + else + { + j = OPTION_DEFAULT; + } + } + return (j); +} + +#ifdef PHREEQCI_GUI +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +free_spread(void) +/* ---------------------------------------------------------------------- */ +{ + int i; + spread_row_free(g_spread_sheet.heading); + spread_row_free(g_spread_sheet.units); + for (i = 0; i < g_spread_sheet.count_rows; i++) + { + spread_row_free(g_spread_sheet.rows[i]); + } + g_spread_sheet.rows = (spread_row**)free_check_null(g_spread_sheet.rows); + + for (i = 0; i < g_spread_sheet.defaults.count_iso; i++) + { + g_spread_sheet.defaults.iso[i].name = + (const char *)free_check_null((void*)g_spread_sheet.defaults.iso[i].name); + } + g_spread_sheet.defaults.iso = + (struct iso*)free_check_null(g_spread_sheet.defaults.iso); + + g_spread_sheet.defaults.redox = + (const char *)free_check_null((void*)g_spread_sheet.defaults.redox); + g_spread_sheet.defaults.units = + (const char *)free_check_null((void*)g_spread_sheet.defaults.units); + + g_spread_sheet.heading = 0; + g_spread_sheet.units = 0; + g_spread_sheet.count_rows = 0; + g_spread_sheet.defaults.count_iso = 0; +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +add_row(struct spread_row *spread_row_ptr) +/* ---------------------------------------------------------------------- */ +{ + g_spread_sheet.rows = + (struct spread_row **) PHRQ_realloc(g_spread_sheet.rows, + sizeof(struct spread_row *) * + (g_spread_sheet.count_rows + 1)); + if (g_spread_sheet.rows == NULL) + { + malloc_error(); + } + else + { + g_spread_sheet.rows[g_spread_sheet.count_rows++] = + copy_row(spread_row_ptr); + } +} + +/* ---------------------------------------------------------------------- */ +struct spread_row * Phreeqc:: +copy_row(struct spread_row *spread_row_ptr) +/* ---------------------------------------------------------------------- */ +{ + int i; + struct spread_row *new_spread_row_ptr; +/* + * Allocate space + */ + new_spread_row_ptr = + (struct spread_row *) PHRQ_malloc((size_t) sizeof(struct spread_row)); + if (new_spread_row_ptr == NULL) + malloc_error(); + new_spread_row_ptr->char_vector = + (char **) PHRQ_malloc((size_t) spread_row_ptr->count * + sizeof(char *)); + if (new_spread_row_ptr->char_vector == NULL) + malloc_error(); + new_spread_row_ptr->d_vector = + (LDBLE *) PHRQ_malloc((size_t) spread_row_ptr->count * sizeof(LDBLE)); + if (new_spread_row_ptr->d_vector == NULL) + malloc_error(); + new_spread_row_ptr->type_vector = + (int *) PHRQ_malloc((size_t) spread_row_ptr->count * sizeof(int)); + if (new_spread_row_ptr->type_vector == NULL) + malloc_error(); + + for (i = 0; i < spread_row_ptr->count; i++) + { + new_spread_row_ptr->char_vector[i] = + string_duplicate(spread_row_ptr->char_vector[i]); + new_spread_row_ptr->d_vector[i] = spread_row_ptr->d_vector[i]; + new_spread_row_ptr->type_vector[i] = spread_row_ptr->type_vector[i]; + } + new_spread_row_ptr->count = spread_row_ptr->count; + new_spread_row_ptr->empty = spread_row_ptr->empty; + new_spread_row_ptr->number = spread_row_ptr->number; + new_spread_row_ptr->string = spread_row_ptr->string; + + return new_spread_row_ptr; +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +copy_defaults(struct defaults *dest_ptr, struct defaults *src_ptr) +/* ---------------------------------------------------------------------- */ +{ + int i; + dest_ptr->count_iso = src_ptr->count_iso; + dest_ptr->density = src_ptr->density; + dest_ptr->iso = + (struct iso *) PHRQ_malloc(sizeof(struct iso) * src_ptr->count_iso); + if (dest_ptr->iso == NULL) + { + malloc_error(); + } + else + { + for (i = 0; i < src_ptr->count_iso; i++) + { + dest_ptr->iso[i] = src_ptr->iso[i]; + dest_ptr->iso[i].name = string_duplicate(src_ptr->iso[i].name); + } + } + + dest_ptr->pe = src_ptr->pe; + dest_ptr->ph = src_ptr->ph; + dest_ptr->redox = string_duplicate(src_ptr->redox); + dest_ptr->temp = src_ptr->temp; + dest_ptr->units = string_duplicate(src_ptr->units); + dest_ptr->water = src_ptr->water; + dest_ptr->pressure = src_ptr->pressure; +} + +#endif diff --git a/phreeqcpp/step.cpp b/phreeqcpp/step.cpp new file mode 100644 index 00000000..4aeda1f3 --- /dev/null +++ b/phreeqcpp/step.cpp @@ -0,0 +1,1480 @@ +#include "Phreeqc.h" +#include "phqalloc.h" + +#include "Utils.h" +#include "StorageBin.h" +#include "Solution.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "SS.h" +#include "NameDouble.h" +#include "Temperature.h" +#include "cxxMix.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "Reaction.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "cxxKinetics.h" + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +step(LDBLE step_fraction) +/* ---------------------------------------------------------------------- */ +{ +/* + * zero global solution, add solution or mixture, add exchange, + * add surface, add gas phase, add solid solutions, + * set temperature, and add reaction. + * Ensure all elements + * included in any of these are present in small amounts. + * Save result as n_user -1. + */ + LDBLE difftemp; + int step_number; + cxxPPassemblage *pp_assemblage_save = NULL; + cxxSSassemblage *ss_assemblage_save = NULL; +/* + * Zero out global solution data + */ + + xsolution_zero(); +/* + * Set reaction to zero + */ + step_x = 0.0; + step_number = reaction_step; +/* + * Mixing or solution + */ + if (use.Get_mix_ptr() != NULL) + { + add_mix(use.Get_mix_ptr()); + } + else if (use.Get_solution_ptr() != NULL) + { + add_solution(use.Get_solution_ptr(), 1.0, 1.0); + cell_no = use.Get_n_solution_user(); + } + else + { + input_error++; + error_msg("Neither mixing nor an initial solution have " + "been defined in reaction step.", STOP); + } +/* + * Reaction + */ + if (use.Get_reaction_ptr() != NULL) + { + add_reaction( use.Get_reaction_ptr(), step_number, step_fraction); + } +/* + * Kinetics + */ + if (use.Get_kinetics_ptr() != NULL) + { + add_kinetics(use.Get_kinetics_ptr()); + } +/* + * Exchange + */ + if (use.Get_exchange_ptr() != NULL) + { + add_exchange(use.Get_exchange_ptr()); + } +/* + * Surface + */ + if (use.Get_surface_ptr() != NULL) + { + add_surface(use.Get_surface_ptr()); + } +/* + * Gases + */ + if (use.Get_gas_phase_ptr() != NULL) + { + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + add_gas_phase(gas_phase_ptr); + } +/* + * Temperature + */ + if (use.Get_temperature_ptr() != NULL) + { + cxxTemperature *t_ptr = use.Get_temperature_ptr(); + tc_x = t_ptr->Temperature_for_step(step_number); + } + if ((state == TRANSPORT) && (transport_step != 0) && + (cell > 0) && (cell != count_cells + 1)) // ** needs potV correction + { + difftemp = tc_x - cell_data[cell].temp; + cell_data[cell].temp += difftemp / tempr; + tc_x = cell_data[cell].temp; + } +/* + * Pressure + */ + if (use.Get_pressure_ptr() != NULL) + { + cxxPressure *p_ptr = use.Get_pressure_ptr(); + patm_x = p_ptr->Pressure_for_step(step_number); + } +/* + * Pure phases and solid solutions are added to avoid + * zero or negative concentrations + */ +/* + * Pure phases + */ + if (use.Get_pp_assemblage_ptr() != NULL) + { + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + pp_assemblage_save = new cxxPPassemblage(*pp_assemblage_ptr); + add_pp_assemblage(pp_assemblage_ptr); + } +/* + * Solid solutions + */ + if (use.Get_ss_assemblage_ptr() != NULL) + { + ss_assemblage_save = new cxxSSassemblage(*use.Get_ss_assemblage_ptr()); + add_ss_assemblage(use.Get_ss_assemblage_ptr()); + } +/* + * Check that elements are available for gas components, + * pure phases, and solid solutions + */ + if (use.Get_gas_phase_ptr() != NULL) + { + cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr(); + gas_phase_check(gas_phase_ptr); + } + if (use.Get_pp_assemblage_ptr() != NULL) + { + cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr(); + pp_assemblage_check(pp_assemblage_ptr); + } + if (use.Get_ss_assemblage_ptr() != NULL) + { + ss_assemblage_check(use.Get_ss_assemblage_ptr()); + } +/* + * Check that element moles are >= zero + */ + if (solution_check() == MASS_BALANCE) + { + /* reset moles and deltas */ + if (use.Get_pp_assemblage_ptr() != NULL) + { + Rxn_pp_assemblage_map[pp_assemblage_save->Get_n_user()] = *pp_assemblage_save; + use.Set_pp_assemblage_ptr(Utilities::Rxn_find(Rxn_pp_assemblage_map, pp_assemblage_save->Get_n_user())); + } + if (use.Get_ss_assemblage_ptr() != NULL) + { + Rxn_ss_assemblage_map[ss_assemblage_save->Get_n_user()] = *ss_assemblage_save; + use.Set_ss_assemblage_ptr(Utilities::Rxn_find(Rxn_ss_assemblage_map, ss_assemblage_save->Get_n_user())); + } + if (pp_assemblage_save != NULL) + { + delete pp_assemblage_save; + pp_assemblage_save = NULL; + } + if (ss_assemblage_save != NULL) + { + delete ss_assemblage_save; + ss_assemblage_save = NULL; + } + return (MASS_BALANCE); + } +/* + * Copy global into solution n_user = -1 + */ + xsolution_save(-1); + step_save_surf(-1); + step_save_exch(-1); +/* + * Clean up temporary space + */ + if (pp_assemblage_save != NULL) + { + delete pp_assemblage_save; + pp_assemblage_save = NULL; + } + if (ss_assemblage_save != NULL) + { + delete ss_assemblage_save; + ss_assemblage_save = NULL; + } + // + // Solution -1 has sum of solution/mix, exchange, surface, gas_phase + // reaction, kinetics + // + // Determine system totals, calculate maximum mineral precipitation + if (use.Get_pp_assemblage_in() || use.Get_ss_assemblage_in()) + { + cxxStorageBin sys_bin(phrq_io); + cxxSolution *sol = Utilities::Rxn_find(Rxn_solution_map, -1); + cxxSolution soln(*sol); + sys_bin.Set_Solution(-1, soln); + if (use.Get_pp_assemblage_in()) + { + sys_bin.Set_PPassemblage(-1, use.Get_pp_assemblage_ptr()); + } + if (use.Get_ss_assemblage_in()) + { + sys_bin.Set_SSassemblage(-1, *use.Get_ss_assemblage_ptr()); + } + sys_bin.Set_System(-1); + sys_bin.Get_System().totalize(this); + cxxNameDouble sys_tots = sys_bin.Get_System().Get_Totals(); + if (use.Get_pp_assemblage_in()) + { + cxxPPassemblage *pp_assemblage_ptr = sys_bin.Get_PPassemblage(-1); + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + int n; + struct phase *p_ptr = phase_bsearch((it->first).c_str(), &n, FALSE); + struct elt_list *e_ptr; + LDBLE min = 1e10; + for (e_ptr = p_ptr->next_elt; e_ptr->elt != NULL; e_ptr++) + { + std::string e(e_ptr->elt->primary->elt->name); + cxxNameDouble::iterator st = sys_tots.find(e.c_str()); + if (st != sys_tots.end()) + { + LDBLE m1 = st->second / e_ptr->coef; + if (m1 < min) min = m1; + } + } + p_ptr->delta_max = min; + } + } + if (use.Get_ss_assemblage_in()) + { + cxxSSassemblage *ss_assemblage_ptr = sys_bin.Get_SSassemblage(-1); + std::vector ss_ptrs = ss_assemblage_ptr->Vectorize(); + for (size_t j = 0; j < ss_ptrs.size(); j++) + { + cxxSS * ss_ptr = ss_ptrs[j]; + for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[k]); + int n; + struct phase *p_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &n, FALSE); + + struct elt_list *e_ptr; + LDBLE min = 1e10; + for (e_ptr = p_ptr->next_elt; e_ptr->elt != NULL; e_ptr++) + { + std::string e(e_ptr->elt->primary->elt->name); + cxxNameDouble::iterator st = sys_tots.find(e.c_str()); + if (st != sys_tots.end()) + { + LDBLE m1 = st->second / e_ptr->coef; + if (m1 < min) + { + min = m1; + } + } + } + p_ptr->delta_max = min; + } + } + } + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +xsolution_zero(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Zero out _x variables, master->totals, and species->la + */ + int i; +/* + * Zero totals in master structures + */ + new_x = FALSE; + + tc_x = 0.0; + patm_x = 0; + potV_x = 0; + ph_x = 0.0; + solution_pe_x = 0.0; + mu_x = 0.0; + ah2o_x = 0.0; + density_x = 0.0; + total_h_x = 0.0; + total_o_x = 0.0; + cb_x = 0.0; + mass_water_aq_x = 0.0; + units_x = moles_per_kilogram_string; + + for (i = 0; i < count_master; i++) + { + master[i]->total = 0.0; + master[i]->total_primary = 0.0; + master[i]->s->la = 0.0; + } + if (pitzer_model == TRUE || sit_model == TRUE) + { + for (i = 0; i < count_s; i++) + { + s[i]->lg = 0.0; + } + } +/* + * Copy pe data (not sure this will be used + */ +/* + pe_data_free (pe_x); + pe_x = pe_data_alloc(); + */ + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_solution(cxxSolution *solution_ptr, LDBLE extensive, LDBLE intensive) +/* ---------------------------------------------------------------------- */ +{ +/* + * Accumulate solution data in master->totals and _x variables. + * + * extensive is multiplication factor for solution + * intensive is fraction of all multiplication factors for all solutions + */ + struct master *master_ptr; + struct species *species_ptr; +/* + * Add solution to global variables + */ + tc_x += solution_ptr->Get_tc() * intensive; + ph_x += solution_ptr->Get_ph() * intensive; + patm_x += solution_ptr->Get_patm() * intensive; + potV_x += solution_ptr->Get_potV() * intensive; + solution_pe_x += solution_ptr->Get_pe() * intensive; + mu_x += solution_ptr->Get_mu() * intensive; + ah2o_x += solution_ptr->Get_ah2o() * intensive; + density_x += solution_ptr->Get_density() * intensive; + + total_h_x += solution_ptr->Get_total_h() * extensive; + total_o_x += solution_ptr->Get_total_o() * extensive; + cb_x += solution_ptr->Get_cb() * extensive; + mass_water_aq_x += solution_ptr->Get_mass_water() * extensive; +/* + * Copy totals data into primary master species + */ + cxxNameDouble::iterator jit = solution_ptr->Get_totals().begin(); + for ( ; jit != solution_ptr->Get_totals().end(); jit++) + { + master_ptr = master_bsearch_primary(jit->first.c_str()); + if (master_ptr != NULL) + { + master_ptr->total += jit->second * extensive; + } + else + { + input_error++; + error_msg(sformatf("Undefined element in solution, %s\n", jit->first.c_str()), CONTINUE); + } + } +/* + * Accumulate initial guesses for activities + */ + jit = solution_ptr->Get_master_activity().begin(); + for ( ; jit != solution_ptr->Get_master_activity().end(); jit++) + { + { + master_ptr = master_bsearch(jit->first.c_str()); + if (master_ptr != NULL) + { + master_ptr->s->la += jit->second * intensive; + } + } + } +/* + * Accumulate initial guesses for log gamma + */ + if (pitzer_model == TRUE || sit_model == TRUE) + { + jit = solution_ptr->Get_species_gamma().begin(); + for ( ; jit != solution_ptr->Get_species_gamma().end(); jit++) + { + species_ptr = s_search(jit->first.c_str()); + if (species_ptr != NULL) + { + species_ptr->lg += jit->second * intensive; + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_exchange(cxxExchange *exchange_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Accumulate exchange data in master->totals and _x variables. + */ + struct master *master_ptr; + + if (exchange_ptr == NULL) + return (OK); +/* + * Add element concentrations on exchanger to master species totals + */ + for (size_t i = 0; i < exchange_ptr->Get_exchange_comps().size(); i++) + { + cxxExchComp comp_ref = exchange_ptr->Get_exchange_comps()[i]; + cxxNameDouble nd(comp_ref.Get_totals()); + cxxNameDouble::iterator it = nd.begin(); + for ( ; it != nd.end(); it++) + { + struct element *elt_ptr = element_store(it->first.c_str()); + LDBLE coef = it->second; + assert(elt_ptr != NULL && elt_ptr->primary != NULL); + master_ptr = elt_ptr->primary; + if (master_ptr->s == s_hplus) + { + total_h_x += coef; + } + else if (master_ptr->s == s_h2o) + { + total_o_x += coef; + } + else + { + master_ptr->total += coef; + } + } + } + if (exchange_ptr->Get_new_def()) + { + for (int i = 0; i < count_master; i++) + { + if (master[i]->type == EX && master[i]->total > 0) + { + master[i]->s->la = log10(0.1 * master[i]->total); + } + } + } + else + { + for (size_t i = 0; i < exchange_ptr->Get_exchange_comps().size(); i++) + { + cxxExchComp &comp_ref = exchange_ptr->Get_exchange_comps()[i]; + cxxNameDouble nd(comp_ref.Get_totals()); + cxxNameDouble::iterator it = nd.begin(); + for ( ; it != nd.end(); it++) + { + struct element *elt_ptr = element_store(it->first.c_str()); + assert(elt_ptr->master); + if (elt_ptr->master->type == EX) + { + elt_ptr->master->s->la = comp_ref.Get_la(); + } + } + cb_x += comp_ref.Get_charge_balance(); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_surface(cxxSurface *surface_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Accumulate surface data in master->totals and _x variables. + */ + if (surface_ptr == NULL) + return (OK); +/* + * Add element concentrations on surface to master species totals + */ + dl_type_x = surface_ptr->Get_dl_type(); + for (size_t i = 0; i < surface_ptr->Get_surface_comps().size(); i++) + { + cxxSurfaceComp *comp_ptr = &(surface_ptr->Get_surface_comps()[i]); + struct element *elt_ptr = element_store(comp_ptr->Get_master_element().c_str()); + if (elt_ptr->master == NULL) + { + error_msg(sformatf("Data not defined for master in SURFACE, %s\n", comp_ptr->Get_formula().c_str()), STOP); + } + struct master *master_i_ptr = elt_ptr->master; + + if (surface_ptr->Get_type() == cxxSurface::NO_EDL) + { + cb_x += comp_ptr->Get_charge_balance(); + } +#ifdef SKIP_MUSIC + if (surface_ptr->type == CD_MUSIC) + { + cb_x += surface_ptr->comps[i].cb; + } +#endif + if (!surface_ptr->Get_new_def()) + { + master_i_ptr->s->la = comp_ptr->Get_la(); + } +/* + * Add surface and specifically sorbed elements + */ + cxxNameDouble::iterator jit; + for (jit = comp_ptr->Get_totals().begin(); jit != comp_ptr->Get_totals().end(); jit++) + { + LDBLE coef = jit->second; + struct element *elt_j_ptr = element_store(jit->first.c_str()); + struct master *master_j_ptr = elt_j_ptr->primary; + if (master_j_ptr == NULL) + { + input_error++; + error_string = sformatf( "Element not defined in database, %s.", + elt_j_ptr->name); + error_msg(error_string, STOP); + } + if (master_j_ptr->s == s_hplus) + { + total_h_x += coef; + } + else if (master_j_ptr->s == s_h2o) + { + total_o_x += coef; + } + else + { + master_j_ptr->total += coef; + } + } + } + if (surface_ptr->Get_type() != cxxSurface::DDL && surface_ptr->Get_type() != cxxSurface::CCM && surface_ptr->Get_type() != cxxSurface::CD_MUSIC) + return (OK); + for (size_t i = 0; i < surface_ptr->Get_surface_charges().size(); i++) + { + cxxSurfaceCharge *charge_ptr = &(surface_ptr->Get_surface_charges()[i]); + if (surface_ptr->Get_type() == cxxSurface::DDL || surface_ptr->Get_type() == cxxSurface::CCM || surface_ptr->Get_type() == cxxSurface::CD_MUSIC) + { + cb_x += charge_ptr->Get_charge_balance(); + } + if (!surface_ptr->Get_new_def()) + { + struct master *master_ptr = surface_get_psi_master(charge_ptr->Get_name().c_str(), SURF_PSI); + master_ptr->s->la = charge_ptr->Get_la_psi(); + } +/* + * Add diffuse layer elements (including water in Debye layer) + */ + if (surface_ptr->Get_dl_type() != cxxSurface::NO_DL && !surface_ptr->Get_new_def()) + { + cxxNameDouble::const_iterator jit; + for (jit = charge_ptr->Get_diffuse_layer_totals().begin(); jit != charge_ptr->Get_diffuse_layer_totals().end(); jit++) + { + LDBLE coef = jit->second; + struct element *elt_j_ptr = element_store(jit->first.c_str()); + struct master * master_j_ptr = elt_j_ptr->master; + if (master_j_ptr->s == s_hplus) + { + total_h_x += coef; + } + else if (master_j_ptr->s == s_h2o) + { + total_o_x += coef; + } + else + { + master_j_ptr->total += coef; + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_mix(cxxMix *mix_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * calls add_solution to accumulate all data in master->totals + * and other variables. + */ + LDBLE sum_fractions, intensive, extensive; + cxxSolution *solution_ptr; + int count_positive; + LDBLE sum_positive; + + if (mix_ptr == NULL) + return (OK); + if (mix_ptr->Get_mixComps().size() == 0) + return (OK); + sum_fractions = 0.0; + sum_positive = 0.0; + count_positive = 0; + std::map::const_iterator it; + for (it = mix_ptr->Get_mixComps().begin(); it != mix_ptr->Get_mixComps().end(); it++) + { + sum_fractions += it->second; + if (it->second > 0) + { + sum_positive += it->second; + count_positive++; + } + } + for (it = mix_ptr->Get_mixComps().begin(); it != mix_ptr->Get_mixComps().end(); it++) + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, it->first); + if (solution_ptr == NULL) + { + error_string = sformatf( "Mix solution not found, %d.", + it->first); + error_msg(error_string, CONTINUE); + input_error++; + continue; + } + extensive = it->second; + intensive = extensive / sum_fractions; + if (count_positive < (int) mix_ptr->Get_mixComps().size()) + { + if (it->second > 0) + { + intensive = extensive / sum_positive; + } + else + { + intensive = 0; + } + } + add_solution(solution_ptr, extensive, intensive); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_pp_assemblage(cxxPPassemblage *pp_assemblage_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Add a small amount of each phase if necessary to insure + * all elements exist in solution. + */ + int i; + LDBLE amount_to_add, total; + char token[MAX_LENGTH]; + char *ptr; + struct master *master_ptr; + + if (check_pp_assemblage(pp_assemblage_ptr) == OK) + return (OK); +/* + * Go through list and generate list of elements and + * coefficient of elements in reaction + */ + count_elts = 0; + paren_count = 0; +/* + * Check that all elements are in solution for phases with greater than zero mass + */ + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + cxxPPassemblageComp * comp_ptr = &(it->second); + if (comp_ptr->Get_precipitate_only()) continue; + int l; + struct phase * phase_ptr = phase_bsearch(it->first.c_str(), &l, FALSE); + count_elts = 0; + paren_count = 0; + amount_to_add = 0.0; + comp_ptr->Set_delta(0.0); + if (comp_ptr->Get_add_formula().size() > 0) + { + strcpy(token, comp_ptr->Get_add_formula().c_str()); + ptr = &(token[0]); + get_elts_in_species(&ptr, 1.0); + } + else + { + strcpy(token, phase_ptr->formula); + add_elt_list(phase_ptr->next_elt, 1.0); + } + if (comp_ptr->Get_moles() > 0.0) + { + for (i = 0; i < count_elts; i++) + { + master_ptr = elt_list[i].elt->primary; + if (master_ptr->s == s_hplus) + { + continue; + } + else if (master_ptr->s == s_h2o) + { + continue; + } + else if (master_ptr->total > MIN_TOTAL) + { + continue; + } + else + { + total = (-master_ptr->total + 1e-10) / elt_list[i].coef; + if (amount_to_add < total) + { + amount_to_add = total; + } + } + } + if (comp_ptr->Get_moles() < amount_to_add) + { + amount_to_add =comp_ptr->Get_moles(); + } + } + if (amount_to_add > 0.0) + { + comp_ptr->Set_moles(comp_ptr->Get_moles() - amount_to_add); + comp_ptr->Set_delta(amount_to_add); +/* + * Add reaction to totals + */ + for (i = 0; i < count_elts; i++) + { + master_ptr = elt_list[i].elt->primary; + if (master_ptr->s == s_hplus) + { + total_h_x += elt_list[i].coef * amount_to_add; + } + else if (master_ptr->s == s_h2o) + { + total_o_x += elt_list[i].coef * amount_to_add; + } + else + { + master_ptr->total += elt_list[i].coef * amount_to_add; + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +check_pp_assemblage(cxxPPassemblage *pp_assemblage_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Check list of all elements in pure_phase assemblage to see + * if all are in model. Return true if all are present, + * Return false if one or more is missing. + */ + struct master *master_ptr; + + cxxNameDouble nd = pp_assemblage_ptr->Get_eltList(); + cxxNameDouble::iterator it; + for (it = nd.begin(); it != nd.end(); it++) + { + struct element *elt_ptr = element_store(it->first.c_str()); + if (elt_ptr == NULL || elt_ptr->primary == NULL) + { + return FALSE; + } + + master_ptr = elt_ptr->primary; + if (master_ptr->s == s_h2o || master_ptr->s == s_hplus) + continue; + if (master_ptr->total > MIN_TOTAL) + continue; + return (FALSE); + } + return (TRUE); +} + /* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_reaction(cxxReaction *reaction_ptr, int step_number, LDBLE step_fraction) +/* ---------------------------------------------------------------------- */ +{ +/* + * Add irreversible reaction + */ + char c; + struct master *master_ptr; +/* + * Calculate and save reaction + */ +/* !!!!! with kinetics reaction, coeff's may change + * and reaction_calc must be called .... + */ + if (reaction_ptr == NULL) + return OK; + + reaction_calc(reaction_ptr); + +/* + * Step size + */ + if (incremental_reactions == FALSE) + { + if (!reaction_ptr->Get_equalIncrements() && reaction_ptr->Get_steps().size()> 0 ) + { + if (step_number > (int) reaction_ptr->Get_steps().size()) + { + step_x = reaction_ptr->Get_steps()[reaction_ptr->Get_steps().size() - 1]; + } + else + { + step_x = reaction_ptr->Get_steps()[step_number - 1]; + } + } + else if (reaction_ptr->Get_equalIncrements() && reaction_ptr->Get_steps().size()> 0) + { + if (step_number > (int) reaction_ptr->Get_reaction_steps()) + { + step_x = reaction_ptr->Get_steps()[0]; + } + else + { + step_x = reaction_ptr->Get_steps()[0] * + ((LDBLE) step_number) / + ((LDBLE) (reaction_ptr->Get_reaction_steps())); + } + } + else + { + step_x = 0.0; + } + } + else + { + /* Incremental reactions */ + if (!reaction_ptr->Get_equalIncrements() && reaction_ptr->Get_steps().size()> 0) + { + if (step_number > (int) reaction_ptr->Get_reaction_steps()) + { + step_x = reaction_ptr->Get_steps()[reaction_ptr->Get_reaction_steps() - 1]; + } + else + { + step_x = reaction_ptr->Get_steps()[step_number - 1]; + } + } + else if (reaction_ptr->Get_equalIncrements() && reaction_ptr->Get_steps().size()> 0) + { + if (step_number > (int) reaction_ptr->Get_reaction_steps()) + { + step_x = 0; + } + else + { + step_x = reaction_ptr->Get_steps()[0] / ((LDBLE) (reaction_ptr->Get_reaction_steps())); + } + } + else + { + step_x = 0.0; + } + } +/* + * Convert units + */ + c = reaction_ptr->Get_units().c_str()[0]; + if (c == 'm') + { + step_x *= 1e-3; + } + else if (c == 'u') + { + step_x *= 1e-6; + } + else if (c == 'n') + { + step_x *= 1e-9; + } +/* + * Add reaction to totals + */ + cxxNameDouble::const_iterator it = reaction_ptr->Get_elementList().begin(); + for ( ; it != reaction_ptr->Get_elementList().end(); it++) + { + struct element * elt_ptr = element_store(it->first.c_str()); + LDBLE coef = it->second; + if (elt_ptr == NULL) + { + assert (false); + } + else + { + master_ptr = elt_ptr->primary; + if (master_ptr == NULL) + { + // error msg has been called in reaction_calc + continue; + } + if (master_ptr->s == s_hplus) + { + total_h_x += coef * step_x * step_fraction; + } + else if (master_ptr->s == s_h2o) + { + total_o_x += coef * step_x * step_fraction; + } + else + { + master_ptr->total += coef * step_x * step_fraction; + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +reaction_calc(cxxReaction *reaction_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through irreversible reaction initially to + * determine a list of elements and amounts in + * the reaction. + */ + int return_value; + LDBLE coef; + char *ptr; + struct phase *phase_ptr; +/* + * Go through list and generate list of elements and + * coefficient of elements in reaction + */ + return_value = OK; + count_elts = 0; + paren_count = 0; + + cxxNameDouble nd(reaction_ptr->Get_reactantList()); + cxxNameDouble::iterator it; + for (it = nd.begin(); it != nd.end(); it++) + { + coef = it->second; + int j; + phase_ptr = phase_bsearch(it->first.c_str(), &j, FALSE); +/* + * Reactant is a pure phase, copy formula into token + */ + if (phase_ptr != NULL) + { + add_elt_list(phase_ptr->next_elt, coef); + } + else + { + char * token = string_duplicate(it->first.c_str()); + ptr = token; + get_elts_in_species(&ptr, coef); + free_check_null(token); + } + } +/* + * Check that all elements are in database + */ + for (int i = 0; i < count_elts; i++) + { + if (elt_list[i].elt->master == NULL) + { + error_string = sformatf( + "Element or phase not defined in database, %s.", + elt_list[i].elt->name); + error_msg(error_string, CONTINUE); + input_error++; + return_value = ERROR; + } + } + reaction_ptr->Set_elementList(elt_list_NameDouble()); + + return (return_value); +} + /* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_gas_phase(cxxGasPhase *gas_phase_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Accumulate gas data in master->totals and _x variables. + */ + int i; + struct master *master_ptr; + + if (gas_phase_ptr == NULL) + return (OK); +/* + * calculate reaction + */ + count_elts = 0; + paren_count = 0; + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + int k; + struct phase *phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str() , &k, FALSE); + if (phase_ptr == NULL) + { + input_error++; + error_msg(sformatf("PHASE not found in database, %s\n", gc_ptr->Get_phase_name().c_str()), CONTINUE); + } + //assert(phase_ptr); + else + { + add_elt_list(phase_ptr->next_elt, gc_ptr->Get_moles()); + } + } +/* + * Sort elements in reaction and combine + */ + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + } +/* + * Add gas elements to totals + */ + for (i = 0; i < count_elts; i++) + { + master_ptr = elt_list[i].elt->primary; + if (master_ptr->s == s_hplus) + { + total_h_x += elt_list[i].coef; + } + else if (master_ptr->s == s_h2o) + { + total_o_x += elt_list[i].coef; + } + else + { + master_ptr->total += elt_list[i].coef; + } + } + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE && fabs(gas_phase_ptr->Get_total_p() - patm_x) > 0.01) + { + patm_x = gas_phase_ptr->Get_total_p(); + k_temp(tc_x, patm_x); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_ss_assemblage(cxxSSassemblage *ss_assemblage_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Accumulate solid_solution data in master->totals and _x variables. + */ + int i, j, k; + LDBLE amount_to_add, total; + struct master *master_ptr; + char *ptr; + + if (ss_assemblage_ptr == NULL) + return (OK); + count_elts = 0; + paren_count = 0; +/* + * Check that all elements are in solution for phases with greater than zero mass + */ + std::vector ss_ptrs = ss_assemblage_ptr->Vectorize(); + for (i = 0; i < (int) ss_ptrs.size(); i++) + { + cxxSS * ss_ptr = ss_ptrs[i]; + count_elts = 0; + paren_count = 0; + for (j = 0; j < (int) ss_ptr->Get_ss_comps().size(); j++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[j]); + int l; + struct phase * phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + + amount_to_add = 0.0; + comp_ptr->Set_delta(0.0); + if (comp_ptr->Get_moles() > 0.0) + { + char * token = string_duplicate(phase_ptr->formula); + ptr = &(token[0]); + get_elts_in_species(&ptr, 1.0); + free_check_null(token); + for (k = 0; k < count_elts; k++) + { + master_ptr = elt_list[k].elt->primary; + if (master_ptr->s == s_hplus) + { + continue; + } + else if (master_ptr->s == s_h2o) + { + continue; + } + else if (master_ptr->total > MIN_TOTAL_SS) + { + continue; + } + else + { + total = + (-master_ptr->total + 1e-10) / elt_list[k].coef; + if (amount_to_add < total) + { + amount_to_add = total; + } + } + } + } + if (comp_ptr->Get_moles() < amount_to_add) + { + amount_to_add = comp_ptr->Get_moles(); + } + if (amount_to_add > 0.0) + { + comp_ptr->Set_moles(comp_ptr->Get_moles() - amount_to_add); + comp_ptr->Set_delta(amount_to_add); +/* + * Add reaction to totals + */ + for (k = 0; k < count_elts; k++) + { + master_ptr = elt_list[k].elt->primary; + if (master_ptr->s == s_hplus) + { + total_h_x += elt_list[k].coef * amount_to_add; + } + else if (master_ptr->s == s_h2o) + { + total_o_x += elt_list[k].coef * amount_to_add; + } + else + { + master_ptr->total += elt_list[k].coef * amount_to_add; + } + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_kinetics(cxxKinetics *kinetics_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Add kinetic reaction + */ + struct master *master_ptr = NULL; +/* + * Add reaction to totals + */ + if (kinetics_ptr->Get_totals().size() == 0) + return (OK); + cxxNameDouble::iterator it = kinetics_ptr->Get_totals().begin(); + for (; it != kinetics_ptr->Get_totals().end(); it++) + { + LDBLE coef = it->second; + struct element *elt_ptr = element_store(it->first.c_str()); + if (elt_ptr == NULL || (master_ptr = elt_ptr->primary) == NULL) + { + input_error++; + error_string = sformatf( + "Element %s in kinetic reaction not found in database.", + it->first.c_str()); + error_msg(error_string, STOP); + } + else + { + if (master_ptr->s == s_hplus) + { + total_h_x += coef; + } + else if (master_ptr->s == s_h2o) + { + total_o_x += coef; + } + else + { + master_ptr->total += coef; + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +gas_phase_check(cxxGasPhase *gas_phase_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Check for missing elements + */ + struct master *master_ptr; + + if (gas_phase_ptr == NULL) + return (OK); +// set gas pressure to reaction_pressure... + if (use.Get_pressure_ptr() != NULL && gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + gas_phase_ptr->Set_total_p(patm_x); + k_temp(tc_x, patm_x); + } + +/* + * Check that all elements are in solution for phases with zero mass + */ + for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++) + { + cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]); + int k; + struct phase *phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str() , &k, FALSE); + count_elts = 0; + paren_count = 0; + if (gc_ptr->Get_moles() <= 0.0) + { + add_elt_list(phase_ptr->next_elt, 1.0); + for (int j = 0; j < count_elts; j++) + { + master_ptr = elt_list[j].elt->primary; + if (master_ptr->s == s_hplus) + { + continue; + } + else if (master_ptr->s == s_h2o) + { + continue; + } + else if (master_ptr->total > MIN_TOTAL) + { + continue; + } + else + { + if (state != ADVECTION && state != TRANSPORT + && state != PHAST) + { + error_string = sformatf( + "Element %s is contained in gas %s (which has 0.0 mass),\nbut is not in solution or other phases.", + elt_list[j].elt->name, + phase_ptr->name); + warning_msg(error_string); + } + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +pp_assemblage_check(cxxPPassemblage *pp_assemblage_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Check for missing elements + */ + std::string token; + char *ptr; + struct master *master_ptr; + + if (check_pp_assemblage(pp_assemblage_ptr) == OK) + return (OK); +/* + * Check that all elements are in solution for phases with zero mass + */ + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + cxxPPassemblageComp * comp_ptr = &(it->second); + int l; + struct phase * phase_ptr = phase_bsearch(it->first.c_str(), &l, FALSE); + count_elts = 0; + paren_count = 0; + if (comp_ptr->Get_moles() <= 0.0) + { + comp_ptr->Set_delta(0.0); + if (comp_ptr->Get_add_formula().size() > 0) + { + token = comp_ptr->Get_add_formula(); + ptr = &(token[0]); + get_elts_in_species(&ptr, 1.0); + } + else + { + token = phase_ptr->formula; + add_elt_list(phase_ptr->next_elt, 1.0); + } + for (int i = 0; i < count_elts; i++) + { + master_ptr = elt_list[i].elt->primary; + if (master_ptr->s == s_hplus) + { + continue; + } + else if (master_ptr->s == s_h2o) + { + continue; + } + else if (master_ptr->total > MIN_TOTAL) + { + continue; + } + else + { + if (state != ADVECTION && state != TRANSPORT + && state != PHAST) + { + error_string = sformatf( + "Element %s is contained in %s (which has 0.0 mass)," + "\t\nbut is not in solution or other phases.", + elt_list[i].elt->name, + phase_ptr->name); + warning_msg(error_string); + } +/* + * Make la's of all master species for the element small, so SI will be small + * and no mass transfer will be calculated + */ + for (int k = 0; k < count_master; k++) + { + if (master[k]->elt->primary == master_ptr) + { + master[k]->s->la = -9999.999; + } + } + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +ss_assemblage_check(cxxSSassemblage *ss_assemblage_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Check for missing elements + */ + int j, k; + struct master *master_ptr; + + if (ss_assemblage_ptr == NULL) + return (OK); +/* + * Check that all elements are in solution for phases with zero mass + */ + std::vector ss_ptrs = ss_assemblage_ptr->Vectorize(); + for (j = 0; j < (int) ss_ptrs.size(); j++) + { + cxxSS * ss_ptr = ss_ptrs[j]; + for (k = 0; k < (int) ss_ptr->Get_ss_comps().size(); k++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[k]); + int l; + struct phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + count_elts = 0; + paren_count = 0; + if (comp_ptr->Get_moles() <= 0.0) + { + add_elt_list(phase_ptr->next_elt, 1.0); + for (l = 0; l < count_elts; l++) + { + master_ptr = elt_list[l].elt->primary; + if (master_ptr->s == s_hplus) + { + continue; + } + else if (master_ptr->s == s_h2o) + { + continue; + } + else if (master_ptr->total > MIN_TOTAL_SS) + { + continue; + } + else + { + if (state != ADVECTION && state != TRANSPORT + && state != PHAST) + { + error_string = sformatf( + "Element %s is contained in solid solution %s (which has 0.0 mass),\nbut is not in solution or other phases.", + elt_list[l].elt->name, + phase_ptr->name); + warning_msg(error_string); + } + } + /* + * Make la's of all master species for the element small, + * so SI will be small + * and no mass transfer will be calculated + */ + for (k = 0; k < count_master; k++) + { + if (master[k]->elt->primary == master_ptr) + { + master[k]->s->la = -9999.999; + } + } + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +solution_check(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Check for missing elements + */ + int i; + struct master *master_ptr; + +/* + * Check that all elements are in solution for phases with zero mass + */ + for (i = 0; i < count_master; i++) + { + master_ptr = master[i]; + if (master_ptr->total <= MIN_TOTAL && master_ptr->total >= -MIN_TOTAL) + { + master_ptr->total = 0; + continue; + } + if (master_ptr->total >= 0.0) + continue; + //if (master_ptr->total > -MIN_TOTAL) + //{ + // master_ptr->total = 0; + // continue; + //} + if (master_ptr->s == s_eminus || master_ptr->s == s_h2o + || master_ptr->s == s_hplus || master_ptr->s == s_h3oplus) + { + master_ptr->total = 0; + continue; + } + /* + sprintf (error_string, + "Element %s has negative moles in solution, %e. \n\tErroneous mole balance occurs as moles are added to produce zero moles.\n\tUsually caused by KINETICS, REACTION, or diffuse layer calculation.\n\tMay be due to large time steps in early part of KINETICS simulation or negative concentrations in the diffuse layer.", + master_ptr->elt->name, (LDBLE) master_ptr->total); + */ + error_string = sformatf( + "Negative moles in solution %d for %s, %e. Recovering...", + cell_no, master_ptr->elt->name, (double) master_ptr->total); + warning_msg(error_string); + return (MASS_BALANCE); + } + return (OK); +} diff --git a/phreeqcpp/structures.cpp b/phreeqcpp/structures.cpp new file mode 100644 index 00000000..67c39f2a --- /dev/null +++ b/phreeqcpp/structures.cpp @@ -0,0 +1,4182 @@ +#include "Utils.h" +#include "Phreeqc.h" +#include + +#include "phqalloc.h" +#include "Temperature.h" +#include "cxxMix.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "Reaction.h" +#include "PPassemblage.h" +#include "Use.h" +#include "SSassemblage.h" +#include "cxxKinetics.h" +#include "Surface.h" +#include "Solution.h" + + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +clean_up(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Free all allocated memory, except strings + */ + int i, j; +#if defined MULTICHART + chart_handler.End_timer(); + output_flush(); +#if 0 + // Wait for charts to end + while (0 != this->chart_handler.Get_active_charts()) + { + System::Threading::Thread::Sleep(60); + } +#endif +#endif + + description_x = (char *) free_check_null(description_x); + isotopes_x.clear(); + moles_per_kilogram_string = + (char *) free_check_null(moles_per_kilogram_string); + pe_string = (char *) free_check_null(pe_string); +/* model */ + last_model.exchange = + (struct master **) free_check_null(last_model.exchange); + last_model.gas_phase = + (struct phase **) free_check_null(last_model.gas_phase); + last_model.pp_assemblage = + (struct phase **) free_check_null(last_model.pp_assemblage); + last_model.ss_assemblage = + (const char **) free_check_null(last_model.ss_assemblage); + last_model.add_formula = + (const char **) free_check_null(last_model.add_formula); + last_model.si = (LDBLE *) free_check_null(last_model.si); + last_model.surface_comp = + (const char **) free_check_null(last_model.surface_comp); + last_model.surface_charge = + (const char **) free_check_null(last_model.surface_charge); + + /* model */ + free_model_allocs(); + +/* species */ + + for (j = 0; j < count_s; j++) + { + s_free(s[j]); + s[j] = (struct species *) free_check_null(s[j]); + } + s = (struct species **) free_check_null(s); + +/* master species */ + + for (j = 0; j < count_master; j++) + { + master_free(master[j]); + } + master = (struct master **) free_check_null(master); + +/* elements */ + + for (j = 0; j < count_elements; j++) + { + elements[j] = (struct element *) free_check_null(elements[j]); + } + elements = (struct element **) free_check_null(elements); + +/* solutions */ + Rxn_solution_map.clear(); + +/* surfaces */ + Rxn_surface_map.clear(); + +/* exchange */ + Rxn_exchange_map.clear(); + +/* pp assemblages */ + Rxn_pp_assemblage_map.clear(); + +/* s_s assemblages */ + Rxn_ss_assemblage_map.clear(); + +/* irreversible reactions */ + Rxn_reaction_map.clear(); + +/* temperature */ + Rxn_temperature_map.clear(); + +/* pressure */ + Rxn_pressure_map.clear(); + +/* unknowns */ + + for (j = 0; j < max_unknowns; j++) + { + unknown_free(x[j]); + } + x = (struct unknown **) free_check_null(x); + +/* mixtures */ + Rxn_mix_map.clear(); + +/* phases */ + + for (j = 0; j < count_phases; j++) + { + phase_free(phases[j]); + phases[j] = (struct phase *) free_check_null(phases[j]); + } + phases = (struct phase **) free_check_null(phases); + +/* inverse */ + for (j = 0; j < count_inverse; j++) + { + inverse_free(&(inverse[j])); + } + inverse = (struct inverse *) free_check_null(inverse); + +/* gases */ + Rxn_gas_phase_map.clear(); + +/* kinetics */ + Rxn_kinetics_map.clear(); + x0_moles = (LDBLE *) free_check_null(x0_moles); + m_temp = (LDBLE *) free_check_null(m_temp); + m_original = (LDBLE *) free_check_null(m_original); + rk_moles = (LDBLE *) free_check_null(rk_moles); + +/* rates */ + for (j = 0; j < count_rates; j++) + { + rate_free(&rates[j]); + } + rates = (struct rate *) free_check_null(rates); + +/* logk hash table */ + for (j = 0; j < count_logk; j++) + { + free_check_null(logk[j]->add_logk); + logk[j] = (struct logk *) free_check_null(logk[j]); + } + logk = (struct logk **) free_check_null(logk); + +/* save_values */ + for (j = 0; j < count_save_values; j++) + { + save_values[j].subscripts = + (int *) free_check_null(save_values[j].subscripts); + } + save_values = (struct save_values *) free_check_null(save_values); + +/* model */ + +/* global solution */ + + pe_x.clear(); + +/* species_list */ + + species_list = (struct species_list *) free_check_null(species_list); + +/* transport data */ + + stag_data = (struct stag_data *) free_check_null(stag_data); + cell_data = (struct cell_data *) free_check_null(cell_data); + +/* punch */ +#ifdef SKIP + punch.totals = (struct name_master *) free_check_null(punch.totals); + punch.molalities = + (struct name_species *) free_check_null(punch.molalities); + punch.activities = + (struct name_species *) free_check_null(punch.activities); + punch.pure_phases = + (struct name_phase *) free_check_null(punch.pure_phases); + punch.si = (struct name_phase *) free_check_null(punch.si); + punch.gases = (struct name_phase *) free_check_null(punch.gases); + punch.s_s = (struct name_phase *) free_check_null(punch.s_s); + punch.kinetics = (struct name_phase *) free_check_null(punch.kinetics); +#endif + advection_punch = (int *) free_check_null(advection_punch); + advection_print = (int *) free_check_null(advection_print); +#ifdef SKIP + punch.isotopes = (struct name_master *) free_check_null(punch.isotopes); + punch.calculate_values = + (struct name_master *) free_check_null(punch.calculate_values); +#endif + SelectedOutput_map.clear(); + UserPunch_map.clear(); + +/* user_print and user_punch */ + rate_free(user_print); + user_print = (struct rate *) free_check_null(user_print); +#ifdef SKIP + rate_free(user_punch); + user_print = (struct rate *) free_check_null(user_print); + + user_punch = (struct rate *) free_check_null(user_punch); + user_punch_headings = (const char **) free_check_null(user_punch_headings); +#endif + + /* + Free llnl aqueous model parameters + */ + llnl_temp = (LDBLE *) free_check_null(llnl_temp); + llnl_adh = (LDBLE *) free_check_null(llnl_adh); + llnl_bdh = (LDBLE *) free_check_null(llnl_bdh); + llnl_bdot = (LDBLE *) free_check_null(llnl_bdot); + llnl_co2_coefs = (LDBLE *) free_check_null(llnl_co2_coefs); + /* + * Copier space + */ + copier_free(©_solution); + copier_free(©_pp_assemblage); + copier_free(©_exchange); + copier_free(©_surface); + copier_free(©_ss_assemblage); + copier_free(©_gas_phase); + copier_free(©_kinetics); + copier_free(©_mix); + copier_free(©_reaction); + copier_free(©_temperature); + copier_free(©_pressure); + +#if defined PHREEQ98 + rate_free(user_graph); + user_graph = (struct rate *) free_check_null(user_graph); + user_graph_headings = (char **) free_check_null(user_graph_headings); +#endif + /* master_isotope */ + for (i = 0; i < count_master_isotope; i++) + { + master_isotope[i] = + (struct master_isotope *) free_check_null(master_isotope[i]); + } + master_isotope = + (struct master_isotope **) free_check_null(master_isotope); + hdestroy_multi(master_isotope_hash_table); + master_isotope_hash_table = NULL; + + /* calculate_value */ + for (i = 0; i < count_calculate_value; i++) + { + calculate_value_free(calculate_value[i]); + calculate_value[i] = + (struct calculate_value *) free_check_null(calculate_value[i]); + } + calculate_value = + (struct calculate_value **) free_check_null(calculate_value); + hdestroy_multi(calculate_value_hash_table); + calculate_value_hash_table = NULL; + + /* isotope_ratio */ + for (i = 0; i < count_isotope_ratio; i++) + { + isotope_ratio[i] = + (struct isotope_ratio *) free_check_null(isotope_ratio[i]); + } + isotope_ratio = (struct isotope_ratio **) free_check_null(isotope_ratio); + hdestroy_multi(isotope_ratio_hash_table); + isotope_ratio_hash_table = NULL; + + /* isotope_alpha */ + for (i = 0; i < count_isotope_alpha; i++) + { + isotope_alpha[i] = + (struct isotope_alpha *) free_check_null(isotope_alpha[i]); + } + isotope_alpha = (struct isotope_alpha **) free_check_null(isotope_alpha); + hdestroy_multi(isotope_alpha_hash_table); + isotope_alpha_hash_table = NULL; + + free_tally_table(); + + /* CVODE memory */ + free_cvode(); + + pitzer_clean_up(); + + sit_clean_up(); + + +/* hash tables */ + + hdestroy_multi(elements_hash_table); + hdestroy_multi(species_hash_table); + hdestroy_multi(logk_hash_table); + hdestroy_multi(phases_hash_table); + + elements_hash_table = NULL; + species_hash_table = NULL; + logk_hash_table = NULL; + phases_hash_table = NULL; + +/* strings */ +#ifdef HASH + strings_hash_clear(); +#else + strings_map_clear(); +#endif + +/* delete basic interpreter */ + basic_free(); + change_surf = (struct Change_Surf *) free_check_null(change_surf); + +/* miscellaneous work space */ + + elt_list = (struct elt_list *) free_check_null(elt_list); + trxn.token = (struct rxn_token_temp *) free_check_null(trxn.token); + mb_unknowns = (struct unknown_list *) free_check_null(mb_unknowns); + line = (char *) free_check_null(line); + line_save = (char *) free_check_null(line_save); + + zeros = (LDBLE *) free_check_null(zeros); + scratch = (LDBLE *) free_check_null(scratch); + x_arg = (LDBLE *) free_check_null(x_arg); + res_arg = (LDBLE *) free_check_null(res_arg); + + normal = (LDBLE *) free_check_null(normal); + ineq_array = (LDBLE *) free_check_null(ineq_array); + back_eq = (int *) free_check_null(back_eq); + zero = (LDBLE *) free_check_null(zero); + res = (LDBLE *) free_check_null(res); + delta1 = (LDBLE *) free_check_null(delta1); + cu = (LDBLE *) free_check_null(cu); + iu = (int *) free_check_null(iu); + is = (int *) free_check_null(is); + +/* x_arg = res_arg = scratch = NULL; */ + x_arg_max = res_arg_max = scratch_max = 0; + + +/* free user database name if defined */ + user_database = (char *) free_check_null(user_database); + //selected_output_file_name = + // (char *) free_check_null(selected_output_file_name); + dump_file_name = (char *) free_check_null(dump_file_name); +#ifdef PHREEQCI_GUI + free_spread(); +#endif + title_x = (char *) free_check_null(title_x); + last_title_x.clear(); + count_elements = 0; + count_master = 0; + count_phases = 0; + count_s = 0; + count_logk = 0; + count_master_isotope = 0; + count_rates = 0; + count_inverse = 0; + count_save_values = 0; + + llnl_count_temp = 0; + llnl_count_adh = 0; + llnl_count_bdh = 0; + llnl_count_bdot = 0; + llnl_count_co2_coefs = 0; + + count_calculate_value = 0; + count_isotope_ratio = 0; + count_isotope_alpha = 0; + + default_data_base = (char *) free_check_null(default_data_base); + sformatf_buffer = (char *) free_check_null(sformatf_buffer); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +reinitialize(void) +/* ---------------------------------------------------------------------- */ +{ +/* solutions */ + Rxn_solution_map.clear(); + +/* surfaces */ + Rxn_surface_map.clear(); + +/* exchange */ + Rxn_exchange_map.clear(); + +/* pp assemblages */ + Rxn_pp_assemblage_map.clear(); + +/* s_s assemblages */ + Rxn_ss_assemblage_map.clear(); + +/* gases */ + Rxn_gas_phase_map.clear(); + +/* kinetics */ + Rxn_kinetics_map.clear(); + +/* irreversible reactions */ + Rxn_reaction_map.clear(); + + // Temperature + Rxn_temperature_map.clear(); + + // Pressure + Rxn_pressure_map.clear(); + return (OK); +} + +/* ********************************************************************** + * + * Routines related to structure "element" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +element_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + const struct element *element_ptr1, *element_ptr2; + element_ptr1 = *(const struct element **) ptr1; + element_ptr2 = *(const struct element **) ptr2; +/* return(strcmp_nocase(element_ptr1->name, element_ptr2->name)); */ + return (strcmp(element_ptr1->name, element_ptr2->name)); + +} + +/* ---------------------------------------------------------------------- */ +struct element * Phreeqc:: +element_store(const char *element) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "element" in the hash table for elements. + * + * If found, pointer to the appropriate element structure is returned. + * + * If the string is not found, a new entry is made at the end of + * the elements array (position count_elements) and count_elements is + * incremented. A new entry is made in the hash table. Pointer to + * the new structure is returned. + * + * Arguments: + * element input, character string to be located or stored. + * + * Returns: + * The address of an elt structure that contains the element data. + */ + int n; + struct element *elts_ptr; + ENTRY item, *found_item; + char token[MAX_LENGTH]; +/* + * Search list + */ + strcpy(token, element); + + item.key = token; + item.data = NULL; + found_item = hsearch_multi(elements_hash_table, item, FIND); + if (found_item != NULL) + { + elts_ptr = (struct element *) (found_item->data); + return (elts_ptr); + } +/* + * Save new elt structure and return pointer to it + */ + /* make sure there is space in elements */ + elements[count_elements] = + (struct element *) PHRQ_malloc((size_t) sizeof(struct element)); + if (elements[count_elements] == NULL) + malloc_error(); + /* set name pointer in elements structure */ + elements[count_elements]->name = string_hsave(token); + /* set return value */ + elements[count_elements]->master = NULL; + elements[count_elements]->primary = NULL; + elements[count_elements]->gfw = 0.0; + n = count_elements++; + if (count_elements >= max_elements) + { + space((void **) ((void *) &elements), count_elements, &max_elements, + sizeof(struct element *)); + } +/* + * Update hash table + */ + item.key = elements[n]->name; + item.data = (void *) elements[n]; + found_item = hsearch_multi(elements_hash_table, item, ENTER); + if (found_item == NULL) + { + error_string = sformatf( "Hash table error in element_store."); + error_msg(error_string, CONTINUE); + } + return (elements[n]); +} + +/* ********************************************************************** + * + * Routines related to structure "elt_list" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +elt_list_combine(void) +/* ---------------------------------------------------------------------- */ +/* + * Function goes through the list of elements pointed to by elt_list + * and combines the coefficients of elements that are the same. + * Assumes elt_list has been sorted by element name. + */ +{ + int i, j; + + if (count_elts < 1) + { + output_msg("elt_list_combine: How did this happen?\n"); + return (ERROR); + } + if (count_elts == 1) + { + return (OK); + } + j = 0; + for (i = 1; i < count_elts; i++) + { + if (elt_list[i].elt == elt_list[j].elt) + { + elt_list[j].coef += elt_list[i].coef; + } + else + { + j++; + if (i != j) + { + elt_list[j].elt = elt_list[i].elt; + elt_list[j].coef = elt_list[i].coef; + } + } + } + count_elts = j + 1; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +elt_list_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + const struct elt_list *a, *b; + + a = (const struct elt_list *) ptr1; + b = (const struct elt_list *) ptr2; + return (strncmp(a->elt->name, b->elt->name, MAX_LENGTH)); +} + +/* ---------------------------------------------------------------------- */ +struct elt_list * Phreeqc:: +elt_list_dup(struct elt_list *elt_list_ptr_old) +/* ---------------------------------------------------------------------- */ +{ +/* + * Duplicates the elt_list structure pointed to by elt_list_ptr_old. + */ + int i, count_totals; + struct elt_list *elt_list_ptr_new; +/* + * Count totals data and copy + */ + if (elt_list_ptr_old == NULL) + return (NULL); + for (i = 0; elt_list_ptr_old[i].elt != NULL; i++); + count_totals = i; +/* + * Malloc space and store element data + */ + elt_list_ptr_new = + (struct elt_list *) PHRQ_malloc((size_t) (count_totals + 1) * + sizeof(struct elt_list)); + if (elt_list_ptr_new == NULL) + malloc_error(); + memcpy(elt_list_ptr_new, elt_list_ptr_old, + (size_t) (count_totals + 1) * sizeof(struct elt_list)); + return (elt_list_ptr_new); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +elt_list_print(struct elt_list *elt_list_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Duplicates the elt_list structure pointed to by elt_list_ptr_old. + */ + int i; +/* + * Debug print for element list + */ + if (elt_list_ptr == NULL) + return (ERROR); + output_msg(sformatf( "Elt_list\n")); + for (i = 0; elt_list_ptr[i].elt != NULL; i++) + { + output_msg(sformatf( "\t%s\t%e\n", elt_list_ptr[i].elt->name, + (double) elt_list_ptr[i].coef)); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +cxxNameDouble Phreeqc:: +elt_list_NameDouble(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Takes data from work space elt_list, makes NameDouble + */ + cxxNameDouble nd; + for(int i = 0; i < count_elts; i++) + { + nd.add(elt_list[i].elt->name, elt_list[i].coef); + } + return (nd); +} +/* ---------------------------------------------------------------------- */ +struct elt_list * Phreeqc:: +elt_list_save(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Takes data from work space elt_list, allocates a new elt_list structure, + * copies data from work space to new structure, and returns pointer to + * new structure. + */ + int j; + struct elt_list *elt_list_ptr; +/* + * Sort elements in reaction and combine + */ + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + } +/* + * Malloc space and store element data + */ + elt_list_ptr = + (struct elt_list *) PHRQ_malloc((size_t) (count_elts + 1) * + sizeof(struct elt_list)); + if (elt_list_ptr == NULL) + { + malloc_error(); + } + else + { + for (j = 0; j < count_elts; j++) + { + elt_list_ptr[j].elt = elt_list[j].elt; + elt_list_ptr[j].coef = elt_list[j].coef; + } + elt_list_ptr[count_elts].elt = NULL; + } + return (elt_list_ptr); +} +/* ---------------------------------------------------------------------- */ +struct elt_list * Phreeqc:: +NameDouble2elt_list(const cxxNameDouble &nd) +/* ---------------------------------------------------------------------- */ +{ +/* + * Takes NameDouble allocates space and fills new elt_list struct + */ + struct elt_list *elt_list_ptr = (struct elt_list *) PHRQ_malloc((nd.size() + 1) * sizeof(struct elt_list)); + if (elt_list_ptr == NULL) + { + malloc_error(); + } + else + { + cxxNameDouble::const_iterator it = nd.begin(); + int i = 0; + for( ; it != nd.end(); it++) + { + elt_list_ptr[i].elt = element_store(it->first.c_str()); + elt_list_ptr[i].coef = it->second; + i++; + } + elt_list_ptr[i].elt = NULL; + elt_list_ptr[i].coef = 0; + } + return (elt_list_ptr); +} +/* ********************************************************************** + * + * Routines related to structure "inverse" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +struct inverse * Phreeqc:: +inverse_alloc(void) +/* ---------------------------------------------------------------------- */ +/* + * Allocates space for a new inverse structure at position count_inverse. + * Initializes structure. + * arguments + * input: none + * output: pointer to an inverse structure + * return: OK + */ +{ + struct inverse *inverse_ptr = NULL; + + count_inverse++; + inverse = + (struct inverse *) PHRQ_realloc(inverse, + (size_t) count_inverse * + sizeof(struct inverse)); + if (inverse == NULL) + { + malloc_error(); + return inverse_ptr; + } + inverse_ptr = &(inverse[count_inverse - 1]); +/* + * Initialize variables + */ + inverse_ptr->description = NULL; + inverse_ptr->count_uncertainties = 0; + inverse_ptr->count_solns = 0; + inverse_ptr->count_elts = 0; + inverse_ptr->count_isotopes = 0; + inverse_ptr->count_i_u = 0; + inverse_ptr->count_phases = 0; + inverse_ptr->count_force_solns = 0; +/* + * allocate space for pointers in structure to NULL + */ + + inverse_ptr->uncertainties = + (LDBLE *) PHRQ_malloc((size_t) sizeof(LDBLE)); + if (inverse_ptr->uncertainties == NULL) + { + malloc_error(); + return inverse_ptr; + } + + inverse_ptr->ph_uncertainties = + (LDBLE *) PHRQ_malloc((size_t) sizeof(LDBLE)); + if (inverse_ptr->ph_uncertainties == NULL) + { + malloc_error(); + return inverse_ptr; + } + + inverse_ptr->force_solns = (int *) PHRQ_malloc((size_t) sizeof(int)); + if (inverse_ptr->force_solns == NULL) + { + malloc_error(); + return inverse_ptr; + } + + inverse_ptr->dalk_dph = NULL; + inverse_ptr->dalk_dc = NULL; + + inverse_ptr->solns = NULL; + + inverse_ptr->elts = + (struct inv_elts *) PHRQ_malloc((size_t) sizeof(struct inv_elts)); + if (inverse_ptr->elts == NULL) + { + malloc_error(); + return inverse_ptr; + } + inverse_ptr->elts[0].name = NULL; + inverse_ptr->elts[0].uncertainties = NULL; + + inverse_ptr->isotopes = + (struct inv_isotope *) PHRQ_malloc((size_t) + sizeof(struct inv_isotope)); + if (inverse_ptr->isotopes == NULL) + { + malloc_error(); + return inverse_ptr; + } + inverse_ptr->isotopes[0].isotope_name = NULL; + inverse_ptr->isotopes[0].isotope_number = 0; + inverse_ptr->isotopes[0].elt_name = NULL; + + inverse_ptr->i_u = + (struct inv_isotope *) PHRQ_malloc((size_t) + sizeof(struct inv_isotope)); + if (inverse_ptr->i_u == NULL) + { + malloc_error(); + return inverse_ptr; + } + inverse_ptr->i_u[0].isotope_name = NULL; + inverse_ptr->i_u[0].isotope_number = 0; + inverse_ptr->i_u[0].elt_name = NULL; + + inverse_ptr->phases = + (struct inv_phases *) PHRQ_malloc((size_t) sizeof(struct inv_phases)); + if (inverse_ptr->phases == NULL) + { + malloc_error(); + return inverse_ptr; + } + + return (inverse_ptr); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +inverse_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ +/* + * Compare inverse values for n_user + */ + const struct inverse *nptr1; + const struct inverse *nptr2; + + nptr1 = (const struct inverse *) ptr1; + nptr2 = (const struct inverse *) ptr2; + if (nptr1->n_user > nptr2->n_user) + return (1); + if (nptr1->n_user < nptr2->n_user) + return (-1); + return (0); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +inverse_delete(int i) +/* ---------------------------------------------------------------------- */ +{ +/* + * Deletes inverse i from list (i is not user number), + * Frees memory allocated to inverse struct + * Input: i, number of inverse struct to delete + * Return: OK + */ + int j; + + inverse_free(&(inverse[i])); + for (j = i; j < (count_inverse - 1); j++) + { + memcpy((void *) &(inverse[j]), (void *) &(inverse[j + 1]), + (size_t) sizeof(struct inverse)); + } + count_inverse--; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +inverse_free(struct inverse *inverse_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Free all memory for an inverse structure. + */ + int i; + + inverse_ptr->description = + (char *) free_check_null(inverse_ptr->description); +/* Free solns */ + inverse_ptr->solns = (int *) free_check_null(inverse_ptr->solns); + +/* Free uncertainties */ + inverse_ptr->uncertainties = + (LDBLE *) free_check_null(inverse_ptr->uncertainties); + inverse_ptr->ph_uncertainties = + (LDBLE *) free_check_null(inverse_ptr->ph_uncertainties); + +/* Free force_solns */ + inverse_ptr->force_solns = + (int *) free_check_null(inverse_ptr->force_solns); + +/* Free elts */ + for (i = 0; i < inverse_ptr->count_elts; i++) + { + inverse_ptr->elts[i].uncertainties = + (LDBLE *) free_check_null(inverse_ptr->elts[i].uncertainties); + }; + inverse_ptr->elts = + (struct inv_elts *) free_check_null(inverse_ptr->elts); + +/* Free isotopes */ + for (i = 0; i < inverse_ptr->count_isotopes; i++) + { + inverse_ptr->isotopes[i].uncertainties = + (LDBLE *) free_check_null(inverse_ptr->isotopes[i].uncertainties); + }; + inverse_ptr->isotopes = + (struct inv_isotope *) free_check_null(inverse_ptr->isotopes); + + for (i = 0; i < inverse_ptr->count_i_u; i++) + { + inverse_ptr->i_u[i].uncertainties = + (LDBLE *) free_check_null(inverse_ptr->i_u[i].uncertainties); + }; + inverse_ptr->i_u = + (struct inv_isotope *) free_check_null(inverse_ptr->i_u); + +/* Free phases */ + for (i = 0; i < inverse_ptr->count_phases; i++) + { + inverse_ptr->phases[i].isotopes = + (struct isotope *) free_check_null(inverse_ptr->phases[i]. + isotopes); + } + inverse_ptr->phases = + (struct inv_phases *) free_check_null(inverse_ptr->phases); + +/* Free carbon derivatives */ + inverse_ptr->dalk_dph = (LDBLE *) free_check_null(inverse_ptr->dalk_dph); + inverse_ptr->dalk_dc = (LDBLE *) free_check_null(inverse_ptr->dalk_dc); + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +inverse_isotope_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + int i; + const struct inv_isotope *iso_ptr1, *iso_ptr2; + + iso_ptr1 = (const struct inv_isotope *) ptr1; + iso_ptr2 = (const struct inv_isotope *) ptr2; + i = strcmp_nocase(iso_ptr1->elt_name, iso_ptr2->elt_name); + if (i != 0) + return (i); + if (iso_ptr1->isotope_number < iso_ptr2->isotope_number) + { + return (-1); + } + else if (iso_ptr1->isotope_number > iso_ptr2->isotope_number) + { + return (1); + } + return (0); +} + +/* ---------------------------------------------------------------------- */ +struct inverse * Phreeqc:: +inverse_search(int n_user, int *n) +/* ---------------------------------------------------------------------- */ +{ +/* Linear search of the structure array "inverse" for user number n_user. + * + * Arguments: + * n_user input, user number + * n output, position in inverse + * + * Returns: + * if found, the address of the inverse element + * if not found, NULL + * + */ + int i; + for (i = 0; i < count_inverse; i++) + { + if (inverse[i].n_user == n_user) + { + *n = i; + return (&(inverse[i])); + } + } +/* + * An inverse structure with n_user was not found + */ + return (NULL); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +inverse_sort(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Sort array of inverse structures + */ + if (count_inverse > 0) + { + qsort(inverse, (size_t) count_inverse, + (size_t) sizeof(struct inverse), inverse_compare); + } + return (OK); +} + +/* ********************************************************************** + * + * Routines related to structure "master" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +struct master * Phreeqc:: +master_alloc(void) +/* ---------------------------------------------------------------------- */ +/* + * Allocates space to a master structure and initializes the space. + * arguments: void + * return: pointer to a master structure + */ +{ + struct master *ptr; + ptr = (struct master *) PHRQ_malloc(sizeof(struct master)); + if (ptr == NULL) + malloc_error(); +/* + * set pointers in structure to NULL + */ + ptr->in = FALSE; + ptr->number = -1; + ptr->last_model = -1; + ptr->type = 0; + ptr->primary = FALSE; + ptr->coef = 0.0; + ptr->total = 0.0; + ptr->isotope_ratio = 0; + ptr->isotope_ratio_uncertainty = 0; + ptr->isotope = 0; + ptr->total_primary = 0; + ptr->elt = NULL; + ptr->alk = 0.0; + ptr->gfw = 0.0; + ptr->gfw_formula = NULL; + ptr->unknown = NULL; + ptr->s = NULL; + ptr->rxn_primary = NULL; + ptr->rxn_secondary = NULL; + ptr->pe_rxn = NULL; + ptr->minor_isotope = FALSE; + return (ptr); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +master_delete(char *ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Delete master species: Free memory of master species structure, free + * the structure, and remove from array master. + * + * Input + * ptr character string with name of element or valence state + * Returns + * TRUE if master species was deleted. + * FALSE if master species was not found. + */ + int j, n; + + if (master_search(ptr, &n) == NULL) + return (FALSE); + master_free(master[n]); + for (j = n; j < (count_master - 1); j++) + { + master[j] = master[j + 1]; + } + count_master--; + return (TRUE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +master_free(struct master *master_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Free memory pointed to by master species pointer, master_ptr. + * Frees master_ptr itself. + */ + if (master_ptr == NULL) + return (ERROR); + rxn_free(master_ptr->rxn_primary); + rxn_free(master_ptr->rxn_secondary); + master_ptr = (struct master *) free_check_null(master_ptr); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +struct master * Phreeqc:: +master_bsearch(const char *ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Uses binary search. Assumes master is in sort order. + * Find master species for string (*ptr) containing name of element or valence state. + * + * Input: ptr pointer to string containing element name + * + * Return: pointer to master structure containing name ptr or NULL. + */ + void *void_ptr; + if (count_master == 0) + { + return (NULL); + } + void_ptr = bsearch((const char *) ptr, + (char *) master, + (unsigned) count_master, + sizeof(struct master *), master_compare_string); + if (void_ptr == NULL) + { + char * dup = string_duplicate(ptr); + replace("(+","(", dup); + void_ptr = bsearch((const char *) dup, + (char *) master, + (unsigned) count_master, + sizeof(struct master *), master_compare_string); + dup = (char *) free_check_null(dup); + } + if (void_ptr == NULL) + { + return (NULL); + } + else + { + return (*(struct master **) void_ptr); + } +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +master_compare_string(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + const char *string_ptr; + const struct master *master_ptr; + + string_ptr = (const char *) ptr1; + master_ptr = *(const struct master **) ptr2; + return (strcmp_nocase(string_ptr, master_ptr->elt->name)); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +master_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + const struct master *master_ptr1, *master_ptr2; + master_ptr1 = *(const struct master **) ptr1; + master_ptr2 = *(const struct master **) ptr2; + return (strcmp_nocase(master_ptr1->elt->name, master_ptr2->elt->name)); +} + +/* ---------------------------------------------------------------------- */ +struct master * Phreeqc:: +master_bsearch_primary(const char *ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Find primary master species for first element in the string, ptr. + * Uses binary search. Assumes master is in sort order. + */ + int l; + char *ptr1; + char elt[MAX_LENGTH]; + struct master *master_ptr_primary; +/* + * Find element name + */ + char * temp_name = string_duplicate(ptr); + ptr1 = temp_name; + get_elt(&ptr1, elt, &l); + free_check_null(temp_name); +/* + * Search master species list + */ + master_ptr_primary = master_bsearch(elt); + if (master_ptr_primary == NULL) + { + input_error++; + error_string = sformatf( + "Could not find primary master species for %s.", ptr); + error_msg(error_string, CONTINUE); + } + return (master_ptr_primary); +} +/* ---------------------------------------------------------------------- */ +struct master * Phreeqc:: +master_bsearch_secondary(char *ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Find secondary master species that corresponds to the primary master species. + * i.e. S(6) for S. + */ + int l; + char *ptr1; + char elt[MAX_LENGTH]; + struct master *master_ptr_primary, *master_ptr=NULL, *master_ptr_secondary=NULL; + int j; +/* + * Find element name + */ + ptr1 = ptr; + get_elt(&ptr1, elt, &l); +/* + * Search master species list + */ + master_ptr_primary = master_bsearch(elt); + if (master_ptr_primary == NULL) + { + input_error++; + error_string = sformatf( + "Could not find primary master species for %s.", ptr); + error_msg(error_string, CONTINUE); + } +/* + * If last in list or not redox +*/ + if (master_ptr_primary) + { + if ((master_ptr_primary->number >= count_master - 1) || + (master[master_ptr_primary->number + 1]->elt->primary != master_ptr_primary)) + { + return(master_ptr_primary); + } + /* + * Find secondary master with same species as primary + */ + master_ptr = NULL; + for (j = master_ptr_primary->number + 1; j < count_master; j++) + { + if (master[j]->s == master_ptr_primary->s) + { + master_ptr = master[j]; + } + } + } +/* + * + */ + if (master_ptr != NULL && master_ptr->elt != NULL && (master_ptr->elt->primary == master_ptr_primary)) + { + master_ptr_secondary = master_ptr; + } + else + { + input_error++; + error_string = sformatf( + "Could not find secondary master species for %s.", ptr); + error_msg(error_string, STOP); + } + + + return (master_ptr_secondary); +} +/* ---------------------------------------------------------------------- */ +struct master * Phreeqc:: +master_search(char *ptr, int *n) +/* ---------------------------------------------------------------------- */ +{ +/* + * Linear search of master to find master species in string, ptr. + * Returns pointer if found. n contains position in array master. + * Returns NULL if not found. + */ + int i; + struct master *master_ptr; +/* + * Search master species list + */ + *n = -999; + for (i = 0; i < count_master; i++) + { + if (strcmp(ptr, master[i]->elt->name) == 0) + { + *n = i; + master_ptr = master[i]; + return (master_ptr); + } + } + return (NULL); +} +/* ********************************************************************** + * + * Routines related to structure "phases" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +struct phase * Phreeqc:: +phase_alloc(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Allocates space to a phase structure and initializes + * arguments: void + * return: pointer to new phase structure + */ + struct phase *phase_ptr; +/* + * Allocate space + */ + phase_ptr = (struct phase *) PHRQ_malloc(sizeof(struct phase)); + if (phase_ptr == NULL) + malloc_error(); +/* + * Initialize space + */ + phase_init(phase_ptr); + return (phase_ptr); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +phase_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ +/* + * Compares names of phases for sort + */ + const struct phase *phase_ptr1, *phase_ptr2; + phase_ptr1 = *(const struct phase **) ptr1; + phase_ptr2 = *(const struct phase **) ptr2; + return (strcmp_nocase(phase_ptr1->name, phase_ptr2->name)); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +phase_compare_string(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + const char *char_ptr; + const struct phase *phase_ptr; + char_ptr = (const char *) ptr1; + phase_ptr = *(const struct phase **) ptr2; + return (strcmp_nocase(char_ptr, phase_ptr->name)); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +phase_delete(int i) +/* ---------------------------------------------------------------------- */ +{ +/* + * Deletes phase i from list, phases + * Frees memory allocated to phase[i] and renumbers phases to remove number i. + * Input: i, number of phase + * Return: OK + */ + int j; + + phase_free(phases[i]); + phases[i] = (struct phase *) free_check_null(phases[i]); + for (j = i; j < (count_phases - 1); j++) + { + phases[j] = phases[j + 1]; + } + count_phases--; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +phase_free(struct phase *phase_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Frees memory allocated within phase[i], does not free phase structure + * Input: i, number of phase + * Return: OK + */ + if (phase_ptr == NULL) + return (ERROR); + phase_ptr->next_elt = + (struct elt_list *) free_check_null(phase_ptr->next_elt); + phase_ptr->next_sys_total = + (struct elt_list *) free_check_null(phase_ptr->next_sys_total); + rxn_free(phase_ptr->rxn); + rxn_free(phase_ptr->rxn_s); + rxn_free(phase_ptr->rxn_x); + phase_ptr->add_logk = + (struct name_coef *) free_check_null(phase_ptr->add_logk); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +struct phase * Phreeqc:: +phase_bsearch(const char *ptr, int *j, int print) +/* ---------------------------------------------------------------------- */ +{ +/* Binary search the structure array "phases" for a name that is equal to + * ptr. Assumes array phases is in sort order. + * + * Arguments: + * name input, a character string to be located in phases. + * j index number in array phases. + * + * Returns: + * if found, pointer to phase structure. + * if not found, NULL + * + */ + void *void_ptr; + + void_ptr = NULL; + if (count_phases > 0) + { + void_ptr = (void *) + bsearch((char *) ptr, + (char *) phases, + (size_t) count_phases, + (size_t) sizeof(struct phase *), phase_compare_string); + } + if (void_ptr == NULL && print == TRUE) + { + error_string = sformatf( "Could not find phase in list, %s.", ptr); + error_msg(error_string, CONTINUE); + } + + if (void_ptr == NULL) + { + *j = -1; + return (NULL); + } + + *j = (int) ((struct phase **) void_ptr - phases); + return (*(struct phase **) void_ptr); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +phase_init(struct phase *phase_ptr) +/* ---------------------------------------------------------------------- */ +/* + * set pointers in phase structure to NULL + */ +{ + int i; + + phase_ptr->name = NULL; + phase_ptr->formula = NULL; + phase_ptr->in = FALSE; + phase_ptr->lk = 0.0; + for (i = 0; i < MAX_LOG_K_INDICES; i++) + phase_ptr->logk[i] = 0.0; + phase_ptr->original_units = kjoules; + phase_ptr->count_add_logk = 0; + phase_ptr->add_logk = NULL; + phase_ptr->moles_x = 0; + phase_ptr->delta_max = 0; + phase_ptr->p_soln_x = 0; + phase_ptr->fraction_x = 0; + phase_ptr->log10_lambda = 0; + phase_ptr->log10_fraction_x = 0; + phase_ptr->dn = 0; + phase_ptr->dnb = 0; + phase_ptr->dnc = 0; + phase_ptr->gn = 0; + phase_ptr->gntot = 0; + phase_ptr->t_c = 0.0; + phase_ptr->p_c = 0.0; + phase_ptr->omega = 0.0; + phase_ptr->pr_a = 0.0; + phase_ptr->pr_b = 0.0; + phase_ptr->pr_alpha = 0.0; + phase_ptr->pr_tk = 0; + phase_ptr->pr_p = 0; + phase_ptr->pr_phi = 1.0; + phase_ptr->pr_aa_sum2 = 0; + for (i = 0; i < 9; i++) + phase_ptr->delta_v[i] = 0.0; + phase_ptr->pr_si_f = 0; + phase_ptr->pr_in = false; + phase_ptr->type = SOLID; + phase_ptr->next_elt = NULL; + phase_ptr->next_sys_total = NULL; + phase_ptr->check_equation = TRUE; + phase_ptr->rxn = NULL; + phase_ptr->rxn_s = NULL; + phase_ptr->rxn_x = NULL; + phase_ptr->replaced = 0; + phase_ptr->in_system = 1; + phase_ptr->original_deltav_units = cm3_per_mol; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +struct phase * Phreeqc:: +phase_store(const char *name) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the hash table for phases. + * + * If found, pointer to the appropriate phase structure is returned. + * + * If the string is not found, a new entry is made at the end of + * the phases array (position count_phases) and count_phases is + * incremented. A new entry is made in the hash table. Pointer to + * the new structure is returned. + * + * Arguments: + * name input, character string to be located or stored. + * + * Returns: + * The address of a phase structure that contains the phase data. + * If phase existed, it is reinitialized. The structure returned + * contains only the name of the phase. + */ + int n; + struct phase *phase_ptr; + ENTRY item, *found_item; + char token[MAX_LENGTH]; + const char *ptr; +/* + * Search list + */ + + strcpy(token, name); + str_tolower(token); + ptr = string_hsave(token); + + item.key = ptr; + item.data = NULL; + found_item = hsearch_multi(phases_hash_table, item, FIND); + if (found_item != NULL) + { + phase_ptr = (struct phase *) (found_item->data); + phase_free(phase_ptr); + phase_init(phase_ptr); + phase_ptr->name = string_hsave(name); + return (phase_ptr); + } +/* + * Make new phase structure and return pointer to it + */ + /* make sure there is space in phases */ + n = count_phases++; + if (count_phases >= max_phases) + { + space((void **) ((void *) &phases), count_phases, &max_phases, + sizeof(struct phase *)); + } + phases[n] = phase_alloc(); + /* set name in phase structure */ + phases[n]->name = string_hsave(name); +/* + * Update hash table + */ + item.key = ptr; + item.data = (void *) phases[n]; + found_item = hsearch_multi(phases_hash_table, item, ENTER); + if (found_item == NULL) + { + error_string = sformatf( "Hash table error in phase_store."); + error_msg(error_string, CONTINUE); + } + + return (phases[n]); +} +/* ********************************************************************** + * + * Routines related to structure "rates" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +struct rate * Phreeqc:: +rate_bsearch(char *ptr, int *j) +/* ---------------------------------------------------------------------- */ +{ +/* Binary search the structure array "rates" for a name that is equal to + * ptr. Assumes array rates is in sort order. + * + * Arguments: + * name input, a character string to be located in rates. + * j index number in array rates. + * + * Returns: + * if found, pointer to rate structure. + * if not found, NULL + * + */ + void *void_ptr; + + if (count_rates == 0) + { + *j = -1; + return (NULL); + } + void_ptr = (void *) + bsearch((char *) ptr, + (char *) rates, + (size_t) count_rates, + (size_t) sizeof(struct rate *), rate_compare_string); + + if (void_ptr == NULL) + { + *j = -1; + return (NULL); + } + + *j = (int) ((struct rate *) void_ptr - rates); + return ((struct rate *) void_ptr); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +rate_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ +/* + * Compares names of rates for sort + */ + const struct rate *rate_ptr1, *rate_ptr2; + rate_ptr1 = *(const struct rate **) ptr1; + rate_ptr2 = *(const struct rate **) ptr2; + return (strcmp_nocase(rate_ptr1->name, rate_ptr2->name)); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +rate_compare_string(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + const char *char_ptr; + const struct rate *rate_ptr; + char_ptr = (const char *) ptr1; + rate_ptr = *(const struct rate **) ptr2; + return (strcmp_nocase(char_ptr, rate_ptr->name)); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +rate_free(struct rate *rate_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Frees memory allocated within rate[i], does not free rate structure + * Input: i, number of rate + * Return: OK + */ + + + if (rate_ptr == NULL) + return (ERROR); + rate_ptr->commands = (char *) free_check_null(rate_ptr->commands); + if (rate_ptr->linebase != NULL) + { + char cmd[] = "new; quit"; + basic_run(cmd, rate_ptr->linebase, rate_ptr->varbase, rate_ptr->loopbase); + rate_ptr->linebase = NULL; + rate_ptr->varbase = NULL; + rate_ptr->loopbase = NULL; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +struct rate * Phreeqc:: +rate_search(const char *name_in, int *n) +/* ---------------------------------------------------------------------- */ +{ +/* Linear search of the structure array "rates" for name. + * + * Arguments: + * name input, name of rate + * n output, position in rates + * + * Returns: + * if found, the address of the pp_assemblage element + * if not found, NULL + */ + std::map::iterator it; + + const char * name; + name = string_hsave(name_in); + + it = rates_map.find(name); + if (it != rates_map.end()) + { + *n = it->second; + if (*n >= 0) + { + return &(rates[it->second]); + } + return NULL; + } + + int i; + *n = -1; + for (i = 0; i < count_rates; i++) + { + if (strcmp_nocase(rates[i].name, name) == 0) + { + *n = i; + rates_map[name] = i; + return (&(rates[i])); + } + } +/* + * rate name not found + */ + rates_map[name] = *n; + return (NULL); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +rate_sort(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Sort array of rate structures + */ + if (count_rates > 0) + { + qsort(rates, (size_t) count_rates, (size_t) sizeof(struct rate), + rate_compare); + } + return (OK); +} + +/* ********************************************************************** + * + * Routines related to structure "reaction", balanced chemical reactions + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +struct reaction * Phreeqc:: +rxn_alloc(int ntokens) +/* ---------------------------------------------------------------------- */ +{ + int i; +/* + * Allocates space to a rxn structure + * input: ntokens, number of tokens in reaction + * return: pointer to a species structure + */ + struct reaction *rxn_ptr; +/* + * Malloc reaction structure + */ + rxn_ptr = (struct reaction *) PHRQ_malloc(sizeof(struct reaction)); + if (rxn_ptr == NULL) + malloc_error(); +/* + * zero log k data + */ + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + rxn_ptr->logk[i] = 0.0; + } +/* + * zero dz data + */ + for (i = 0; i < 3; i++) + { + rxn_ptr->dz[i] = 0.0; + } +/* + * Malloc rxn_token structure + */ + rxn_ptr->token = + (struct rxn_token *) PHRQ_malloc((size_t) ntokens * + sizeof(struct rxn_token)); + for (i = 0; i < ntokens; i++) + { + rxn_ptr->token[i].s = NULL; + rxn_ptr->token[i].name = NULL; + rxn_ptr->token[i].coef = 0.0; + } + + if (rxn_ptr->token == NULL) + malloc_error(); + return (rxn_ptr); +} + +/* ---------------------------------------------------------------------- */ +struct reaction * Phreeqc:: +rxn_dup(struct reaction *rxn_ptr_old) +/* ---------------------------------------------------------------------- */ +{ +/* + * mallocs space for a reaction and copies the reaction + * input: rxn_ptr_old, pointer to a reaction structure to copy + * + * Return: rxn_ptr_new, pointer to duplicated structure to copy + */ + int i; + struct reaction *rxn_ptr_new; + + if (rxn_ptr_old == NULL) + return (NULL); + for (i = 0; rxn_ptr_old->token[i].s != NULL; i++); + + rxn_ptr_new = rxn_alloc(i + 1); +/* + * Copy logk data + */ + memcpy(rxn_ptr_new->logk, rxn_ptr_old->logk, (size_t) MAX_LOG_K_INDICES * sizeof(LDBLE)); +/* + * Copy dz data + */ + memcpy(rxn_ptr_new->dz, rxn_ptr_old->dz, (size_t) (3 * sizeof(LDBLE))); +/* + * Copy tokens + */ + memcpy(rxn_ptr_new->token, rxn_ptr_old->token, + (size_t) (i + 1) * sizeof(struct rxn_token)); + + return (rxn_ptr_new); +} +/* ---------------------------------------------------------------------- */ +struct reaction * Phreeqc:: +cxxChemRxn2rxn(cxxChemRxn &cr) +/* ---------------------------------------------------------------------- */ +{ +/* + * mallocs space for a reaction and copies the cxxChemRxn to a struct reaction + * + * Return: rxn_ptr_new, pointer to new structure + */ + for (int i = 0; i < (int) cr.Get_tokens().size(); i++) + { + if (cr.Get_tokens()[i].s != NULL) + { + cr.Get_tokens()[i].s = s_store(cr.Get_tokens()[i].s->name, cr.Get_tokens()[i].s->z, FALSE); + } + if (cr.Get_tokens()[i].name != NULL) + { + cr.Get_tokens()[i].name = string_hsave(cr.Get_tokens()[i].name); + } + else + { + if (cr.Get_tokens()[i].s != NULL) + { + cr.Get_tokens()[i].name = string_hsave(cr.Get_tokens()[i].s->name); + } + else + { + cr.Get_tokens()[i].name=NULL; + } + } + } + + count_trxn = 0; + trxn_add(cr, 1.0, 1); + + struct reaction *rxn_ptr_new = rxn_alloc(count_trxn + 1); + trxn_copy(rxn_ptr_new); + + // cleanup pointers for copy operator name, and s may point into another instance + + for (int i = 0; rxn_ptr_new->token[i].s != NULL; i++) + { + rxn_ptr_new->token[i].name = string_hsave(rxn_ptr_new->token[i].name); + LDBLE z = rxn_ptr_new->token[i].s->z; + rxn_ptr_new->token[i].s = s_store(rxn_ptr_new->token[i].name, z, false); + } + return (rxn_ptr_new); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +rxn_find_coef(struct reaction * r_ptr, const char *str) +/* ---------------------------------------------------------------------- */ +{ +/* + * Finds coefficient of token in reaction. + * input: r_ptr, pointer to a reaction structure + * str, string to find as reaction token + * + * Return: 0.0, if token not found + * coefficient of token, if found. + */ + struct rxn_token *r_token; + LDBLE coef; + + r_token = r_ptr->token + 1; + coef = 0.0; + while (r_token->s != NULL) + { + if (strcmp(r_token->s->name, str) == 0) + { + coef = r_token->coef; + break; + } + r_token++; + } + return (coef); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +rxn_free(struct reaction *rxn_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Frees space allocated for a reaction structure + * input: rxn_ptr, pointer to reaction structure + * return: ERROR, if pointer is NULL + * OK, otherwise. + */ + if (rxn_ptr == NULL) + return (ERROR); + rxn_ptr->token = (struct rxn_token *) free_check_null(rxn_ptr->token); + rxn_ptr = (struct reaction *) free_check_null(rxn_ptr); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +rxn_print(struct reaction *rxn_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Frees space allocated for a reaction structure + * input: rxn_ptr, pointer to reaction structure + * return: ERROR, if pointer is NULL + * OK, otherwise. + */ + struct rxn_token *next_token; + int i; + if (rxn_ptr == NULL) + return (ERROR); + next_token = rxn_ptr->token; + output_msg(sformatf( "log k data:\n")); + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + output_msg(sformatf( "\t%f\n", (double) rxn_ptr->logk[i])); + } + output_msg(sformatf( "Reaction definition\n")); + while (next_token->s != NULL || next_token->name != NULL) + { + output_msg(sformatf( "\tcoef %f ", next_token->coef)); + if (next_token->s != NULL) + { + output_msg(sformatf( "\tspecies token: %s ", + next_token->s->name)); + } + if (next_token->name != NULL) + { + output_msg(sformatf( "\tname token: %s", next_token->name)); + } + output_msg(sformatf( "\n")); + next_token++; + } + output_msg(sformatf( "dz data\n")); + for (i = 0; i < 3; i++) + { + output_msg(sformatf( "\t%d %e\n", i, (double) rxn_ptr->dz[i])); + + } + return (OK); +} + +/* ********************************************************************** + * + * Routines related to structure "species" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +struct species * Phreeqc:: +s_alloc(void) +/* ---------------------------------------------------------------------- */ +/* + * Allocates space to a species structure, initializes + * arguments: void + * return: pointer to a species structure + */ +{ + struct species *s_ptr; + s_ptr = (struct species *) PHRQ_malloc(sizeof(struct species)); + if (s_ptr == NULL) + malloc_error(); +/* + * set pointers in structure to NULL, variables to zero + */ + s_init(s_ptr); + + return (s_ptr); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +s_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + const struct species *s_ptr1, *s_ptr2; + s_ptr1 = *(const struct species **) ptr1; + s_ptr2 = *(const struct species **) ptr2; + return (strcmp(s_ptr1->name, s_ptr2->name)); + +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +s_delete(int i) +/* ---------------------------------------------------------------------- */ +{ +/* + * Delete species i: free memory and renumber array of pointers, s. + */ + int j; + + s_free(s[i]); + s[i] = (struct species *) free_check_null(s[i]); + for (j = i; j < (count_s - 1); j++) + { + s[j] = s[j + 1]; + } + count_s--; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +s_free(struct species *s_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Free space allocated for species structure, s_ptr. Does not free s_ptr. + */ + if (s_ptr == NULL) + return (ERROR); + s_ptr->next_elt = (struct elt_list *) free_check_null(s_ptr->next_elt); + s_ptr->next_secondary = + (struct elt_list *) free_check_null(s_ptr->next_secondary); + s_ptr->next_sys_total = + (struct elt_list *) free_check_null(s_ptr->next_sys_total); + s_ptr->add_logk = (struct name_coef *) free_check_null(s_ptr->add_logk); + rxn_free(s_ptr->rxn); + rxn_free(s_ptr->rxn_s); + rxn_free(s_ptr->rxn_x); + return (OK); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +s_init(struct species *s_ptr) +/* ---------------------------------------------------------------------- */ +/* + * return: pointer to a species structure + */ +{ + int i; +/* + * set pointers in structure to NULL + */ + s_ptr->name = NULL; + s_ptr->mole_balance = NULL; + s_ptr->in = FALSE; + s_ptr->number = 0; + s_ptr->primary = NULL; + s_ptr->secondary = NULL; + s_ptr->gfw = 0.0; + s_ptr->z = 0.0; + s_ptr->dw = 0.0; + s_ptr->dw_t = 0.0; + s_ptr->dw_a = 0.0; + s_ptr->dw_a2 = 0.0; + s_ptr->erm_ddl = 1.0; + s_ptr->equiv = 0; + s_ptr->alk = 0.0; + s_ptr->carbon = 0.0; + s_ptr->co2 = 0.0; + s_ptr->h = 0.0; + s_ptr->o = 0.0; + s_ptr->dha = 0.0; + s_ptr->dhb = 0.0; + s_ptr->a_f = 0.0; + s_ptr->lk = 0.0; + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + s_ptr->logk[i] = 0.0; + } + for (i = 0; i < 10; i++) + { + s_ptr->Jones_Dole[i] = 0.0; + } +/* VP: Density Start */ + for (i = 0; i < 6; i++) + { + s_ptr->millero[i] = 0.0; + } +/* VP: Density End */ + s_ptr->original_units = kjoules; + s_ptr->count_add_logk = 0; + s_ptr->add_logk = NULL; + s_ptr->lg = 0.0; + s_ptr->lg_pitzer = 0.0; + s_ptr->lm = 0.0; + s_ptr->la = 0.0; + s_ptr->dg = 0.0; + s_ptr->dg_total_g = 0; + s_ptr->moles = 0.0; + s_ptr->type = 0; + s_ptr->gflag = 0; + s_ptr->exch_gflag = 0; + s_ptr->next_elt = NULL; + s_ptr->next_secondary = NULL; + s_ptr->next_sys_total = NULL; + s_ptr->check_equation = TRUE; + s_ptr->rxn = NULL; + s_ptr->rxn_s = NULL; + s_ptr->rxn_x = NULL; + s_ptr->tot_g_moles = 0; + s_ptr->tot_dh2o_moles = 0; + for (i = 0; i < 5; i++) + { + s_ptr->cd_music[i] = 0.0; + } + for (i = 0; i < 3; i++) + { + s_ptr->dz[i] = 0.0; + } + s_ptr->original_deltav_units = cm3_per_mol; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +struct species * Phreeqc:: +s_search(const char *name) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the hash table for species. + * + * Arguments: + * name input, a character string to be located in species. + * i is obsolete. + * + * Returns: + * If found, pointer to the appropriate species structure is returned. + * else, NULL pointer is returned. + */ + struct species *s_ptr; + ENTRY item, *found_item; + char safe_name[MAX_LENGTH]; + + strcpy(safe_name, name); + item.key = safe_name; + item.data = NULL; + found_item = hsearch_multi(species_hash_table, item, FIND); + if (found_item != NULL) + { + s_ptr = (struct species *) (found_item->data); + return (s_ptr); + } + return (NULL); +} + +/* ---------------------------------------------------------------------- */ +struct species * Phreeqc:: +s_store(const char *name, LDBLE l_z, int replace_if_found) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the hash table for species. + * + * Pointer to a species structure is always returned. + * + * If the string is not found, a new entry is made at the end of + * the elements array (position count_elements) and count_elements is + * incremented. A new entry is made in the hash table. Pointer to + * the new structure is returned. + * If "name" is found and replace is true, pointers in old species structure + * are freed and replaced with additional input. + * If "name" is found and replace is false, the old species structure is not + * modified and a pointer to it is returned. + * + * Arguments: + * name input, character string to be found in "species". + * l_z input, charge on "name" + * replace_if_found input, TRUE means reinitialize species if found + * FALSE means just return pointer if found. + * + * Returns: + * pointer to species structure "s" where "name" can be found. + */ + int n; + struct species *s_ptr; + ENTRY item, *found_item; +/* + * Search list + */ + item.key = name; + item.data = NULL; + found_item = hsearch_multi(species_hash_table, item, FIND); + + if (found_item != NULL && replace_if_found == FALSE) + { + s_ptr = (struct species *) (found_item->data); + return (s_ptr); + } + else if (found_item != NULL && replace_if_found == TRUE) + { + s_ptr = (struct species *) (found_item->data); + s_free(s_ptr); + s_init(s_ptr); + } + else + { + n = count_s++; + /* make sure there is space in s */ + if (count_s >= max_s) + { + space((void **) ((void *) &s), count_s, &max_s, + sizeof(struct species *)); + } + /* Make new species structure */ + s[n] = s_alloc(); + s_ptr = s[n]; + } + /* set name and z in pointer in species structure */ + s_ptr->name = string_hsave(name); + s_ptr->z = l_z; +/* + * Update hash table + */ + item.key = s_ptr->name; + item.data = (void *) s_ptr; + found_item = hsearch_multi(species_hash_table, item, ENTER); + if (found_item == NULL) + { + error_string = sformatf( "Hash table error in species_store."); + error_msg(error_string, CONTINUE); + } + + return (s_ptr); +} +/* ********************************************************************** + * + * Routines related to structure "save_values" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +struct save_values * Phreeqc:: +save_values_bsearch(struct save_values *k, int *n) +/* ---------------------------------------------------------------------- */ +{ +/* + * Binary search save_values to find if one exists with given coefficients + * Save_Values is assumed to be in sort order by count_subscripts and + * values of subscripts + */ + void *void_ptr; + if (count_save_values == 0) + { + *n = -999; + return (NULL); + } + void_ptr = (void *) + bsearch((char *) k, + (char *) save_values, + (size_t) count_save_values, + (size_t) sizeof(struct save_values), save_values_compare); + if (void_ptr == NULL) + { + *n = -999; + return (NULL); + } + *n = (int) ((struct save_values *) void_ptr - save_values); + return ((struct save_values *) void_ptr); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +save_values_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + int i; + const struct save_values *save_values_ptr1, *save_values_ptr2; + save_values_ptr1 = (const struct save_values *) ptr1; + save_values_ptr2 = (const struct save_values *) ptr2; + if (save_values_ptr1->count_subscripts < + save_values_ptr2->count_subscripts) + { + return (-1); + } + else if (save_values_ptr1->count_subscripts > + save_values_ptr2->count_subscripts) + { + return (1); + } + else + { + for (i = 0; i < save_values_ptr1->count_subscripts; i++) + { + if (save_values_ptr1->subscripts[i] < + save_values_ptr2->subscripts[i]) + { + return (-1); + } + else if (save_values_ptr1->subscripts[i] > + save_values_ptr2->subscripts[i]) + { + return (1); + } + } + } + return (0); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +save_values_sort(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Sort array of save_values structures + */ + if (count_save_values > 0) + { + qsort(save_values, (size_t) count_save_values, + (size_t) sizeof(struct save_values), save_values_compare); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +save_values_store(struct save_values *s_v) +/* ---------------------------------------------------------------------- */ +{ +/* + * Look for subscripts + */ + int n, i; + struct save_values *s_v_ptr; + + s_v_ptr = save_values_bsearch(s_v, &n); + if (s_v_ptr != NULL) + { + s_v_ptr->value = s_v->value; + } + else + { + save_values = + (struct save_values *) PHRQ_realloc(save_values, + (size_t) (count_save_values + + 1) * + sizeof(struct save_values)); + if (save_values == NULL) + malloc_error(); + save_values[count_save_values].value = s_v->value; + save_values[count_save_values].count_subscripts = + s_v->count_subscripts; + i = s_v->count_subscripts; + if (i == 0) + i = 1; + save_values[count_save_values].subscripts = + (int *) PHRQ_malloc((size_t) i * sizeof(int)); + if (save_values[count_save_values].subscripts == NULL) + malloc_error(); + save_values[count_save_values].subscripts = + (int *) memcpy(save_values[count_save_values].subscripts, + s_v->subscripts, (size_t) i * sizeof(int)); + count_save_values++; + save_values_sort(); + } + + if (count_save_values > 0) + { + qsort(save_values, (size_t) count_save_values, + (size_t) sizeof(struct save_values), save_values_compare); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +isotope_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + int i; + const struct isotope *iso_ptr1, *iso_ptr2; + + iso_ptr1 = (const struct isotope *) ptr1; + iso_ptr2 = (const struct isotope *) ptr2; + i = strcmp_nocase(iso_ptr1->elt_name, iso_ptr2->elt_name); + if (i != 0) + return (i); + if (iso_ptr1->isotope_number < iso_ptr2->isotope_number) + { + return (-1); + } + else if (iso_ptr1->isotope_number > iso_ptr2->isotope_number) + { + return (1); + } + return (0); +} +/* ********************************************************************** + * + * Routines related to structure "species_list" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +species_list_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + int j; + const char *name1, *name2; + const struct species_list *nptr1, *nptr2; + + nptr1 = (const struct species_list *) ptr1; + nptr2 = (const struct species_list *) ptr2; + +/* + * Put H+ first + */ + if (nptr1->master_s != nptr2->master_s) + { + /* + if (nptr1->master_s == s_hplus) + return (-1); + if (nptr2->master_s == s_hplus) + return (1); + */ + if ((strcmp(nptr1->master_s->name,"H+") == 0) || (strcmp(nptr1->master_s->name,"H3O+") == 0)) + return (-1); + if ((strcmp(nptr2->master_s->name,"H+") == 0) || (strcmp(nptr2->master_s->name,"H3O+") == 0)) + return (1); + } +/* + * Other element valence states + */ + if (nptr1->master_s->secondary != NULL) + { + name1 = nptr1->master_s->secondary->elt->name; + } + else + { + name1 = nptr1->master_s->primary->elt->name; + } + if (nptr2->master_s->secondary != NULL) + { + name2 = nptr2->master_s->secondary->elt->name; + } + else + { + name2 = nptr2->master_s->primary->elt->name; + } +/* + * Compare name of primary or secondary master species; log molality + */ + + j = strcmp(name1, name2); + +/* + * Different master species + */ + if (j != 0) + return (j); + +/* + * Else, descending order by log molality + */ + if (nptr1->s->lm > nptr2->s->lm) + { + return (-1); + } + else if (nptr1->s->lm < nptr2->s->lm) + { + return (1); + } + else + { + return (0); + } +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +species_list_compare_alk(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + const struct species_list *nptr1, *nptr2; + LDBLE alk1, alk2; + + nptr1 = (const struct species_list *) ptr1; + nptr2 = (const struct species_list *) ptr2; +/* + * Else, descending order by log molality + */ + alk1 = fabs(under(nptr1->s->lm) * nptr1->s->alk); + alk2 = fabs(under(nptr2->s->lm) * nptr2->s->alk); + + if (alk1 > alk2) + { + return (-1); + } + else if (alk1 < alk2) + { + return (1); + } + else + { + return (0); + } +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +species_list_compare_master(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + const char *name1, *name2; + const struct species_list *nptr1, *nptr2; + + nptr1 = (const struct species_list *) ptr1; + nptr2 = (const struct species_list *) ptr2; + +/* + * Put H+ first + */ + if (nptr1->master_s != nptr2->master_s) + { + /* + if (nptr1->master_s == s_hplus) + return (-1); + if (nptr2->master_s == s_hplus) + return (1); + */ + if ((strcmp(nptr1->master_s->name,"H+") == 0) || (strcmp(nptr1->master_s->name,"H3O+") == 0)) + return (-1); + if ((strcmp(nptr2->master_s->name,"H+") == 0) || (strcmp(nptr2->master_s->name,"H3O+") == 0)) + return (1); + } +/* + * Other element valence states + */ + if (nptr1->master_s->secondary != NULL) + { + name1 = nptr1->master_s->secondary->elt->name; + } + else + { + name1 = nptr1->master_s->primary->elt->name; + } + if (nptr2->master_s->secondary != NULL) + { + name2 = nptr2->master_s->secondary->elt->name; + } + else + { + name2 = nptr2->master_s->primary->elt->name; + } +/* + * Compare name of primary or secondary master species; log molality + */ + + return (strcmp(name1, name2)); +} + + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +species_list_sort(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Sort list using rules in species_list_compare + */ + if (count_species_list > 0) + { + qsort(&species_list[0], (size_t) count_species_list, + (size_t) sizeof(struct species_list), species_list_compare); + } + return (OK); +} + +/* ********************************************************************** + * + * Routines related to structure "surface" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +struct Change_Surf * Phreeqc:: +change_surf_alloc(int count) +/* ---------------------------------------------------------------------- */ +{ + if (count == 1) + return (change_surf); + change_surf = + (struct Change_Surf *) PHRQ_realloc(change_surf, + (size_t) count * + sizeof(struct Change_Surf)); + if (change_surf == NULL) + malloc_error(); + change_surf[count - 1].cell_no = -99; + change_surf[count - 1].next = FALSE; + change_surf[count - 2].next = TRUE; + + return (change_surf); +} +/* ---------------------------------------------------------------------- */ +struct master * Phreeqc:: +surface_get_psi_master(const char *name, int plane) +/* ---------------------------------------------------------------------- */ +{ + struct master *master_ptr; + std::string token; + + if (name == NULL) + return (NULL); + token = name; + token.append("_psi"); + switch (plane) + { + case SURF_PSI: + break; + case SURF_PSI1: + token.append("b"); + break; + case SURF_PSI2: + token.append("d"); + break; + default: + error_msg("Unknown plane for surface_get_psi_master", STOP); + } + master_ptr = master_bsearch(token.c_str()); + return (master_ptr); +} +/* ********************************************************************** + * + * Routines related to structure "trxn" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +rxn_token_temp_compare(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + const struct rxn_token_temp *rxn_token_temp_ptr1, *rxn_token_temp_ptr2; + rxn_token_temp_ptr1 = (const struct rxn_token_temp *) ptr1; + rxn_token_temp_ptr2 = (const struct rxn_token_temp *) ptr2; + return (strcmp(rxn_token_temp_ptr1->name, rxn_token_temp_ptr2->name)); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +trxn_add(cxxChemRxn &r_ptr, LDBLE coef, int combine) +/* ---------------------------------------------------------------------- */ +{ +/* + * Adds reactions together. + * + * Global variable count_trxn determines which position in trxn is used. + * If count_trxn=0, then the equation effectively is copied into trxn. + * If count_trxn>0, then new equation is added to existing equation. + * + * Arguments: + * *r_ptr points to rxn structure to add. + * + * coef added equation is multiplied by coef. + * combine if TRUE, reaction is reaction is sorted and + * like terms combined. + */ +/* + * Accumulate log k for reaction + */ + if (count_trxn == 0) + { + for (int i = 0; i < MAX_LOG_K_INDICES; i++) + { + trxn.logk[i] = r_ptr.Get_logk()[i]; + } + for (int i = 0; i < 3; i++) + { + trxn.dz[i] = r_ptr.Get_dz()[i]; + } + } + else + { + for (int i = 0; i < MAX_LOG_K_INDICES; i++) + { + trxn.logk[i] += coef * (r_ptr.Get_logk()[i]); + } + for (int i = 0; i < 3; i++) + { + trxn.dz[i] += coef * r_ptr.Get_dz()[i]; + } + } +/* + * Copy equation into work space + */ + for (size_t j = 0; j < r_ptr.Get_tokens().size(); j++) + { + if (count_trxn + 1 >= max_trxn) + { + space((void **) ((void *) &(trxn.token)), count_trxn + 1, + &max_trxn, sizeof(struct rxn_token_temp)); + } + trxn.token[count_trxn].name = r_ptr.Get_tokens()[j].name; + trxn.token[count_trxn].s = r_ptr.Get_tokens()[j].s; + trxn.token[count_trxn].coef = coef * r_ptr.Get_tokens()[j].coef; + count_trxn++; + } + if (combine == TRUE) + trxn_combine(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +trxn_add(struct reaction *r_ptr, LDBLE coef, int combine) +/* ---------------------------------------------------------------------- */ +{ +/* + * Adds reactions together. + * + * Global variable count_trxn determines which position in trxn is used. + * If count_trxn=0, then the equation effectively is copied into trxn. + * If count_trxn>0, then new equation is added to existing equation. + * + * Arguments: + * *r_ptr points to rxn structure to add. + * + * coef added equation is multiplied by coef. + * combine if TRUE, reaction is reaction is sorted and + * like terms combined. + */ + int i; + struct rxn_token *next_token; +/* + * Accumulate log k for reaction + */ + if (count_trxn == 0) + { + memcpy((void *) trxn.logk, (void *) r_ptr->logk, + (size_t) MAX_LOG_K_INDICES * sizeof(LDBLE)); + for (i = 0; i < 3; i++) + { + trxn.dz[i] = r_ptr->dz[i]; + } + } + else + { + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + trxn.logk[i] += coef * (r_ptr->logk[i]); + } + for (i = 0; i < 3; i++) + { + trxn.dz[i] += coef * r_ptr->dz[i]; + } + } +/* + * Copy equation into work space + */ + next_token = r_ptr->token; + while (next_token->s != NULL) + { + if (count_trxn + 1 >= max_trxn) + { + space((void **) ((void *) &(trxn.token)), count_trxn + 1, + &max_trxn, sizeof(struct rxn_token_temp)); + } + trxn.token[count_trxn].name = next_token->s->name; + trxn.token[count_trxn].s = next_token->s; + trxn.token[count_trxn].coef = coef * next_token->coef; + count_trxn++; + next_token++; + } + if (combine == TRUE) + trxn_combine(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +trxn_add_phase(struct reaction *r_ptr, LDBLE coef, int combine) +/* ---------------------------------------------------------------------- */ +{ +/* + * Adds reactions together. + * + * Global variable count_trxn determines which position in trxn is used. + * If count_trxn=0, then the equation effectively is copied into trxn. + * If count_trxn>0, then new equation is added to existing equation. + * + * Arguments: + * *r_ptr points to rxn structure to add. + * + * coef added equation is multiplied by coef. + * combine if TRUE, reaction is reaction is sorted and + * like terms combined. + */ + int i; + struct rxn_token *next_token; +/* + * Accumulate log k for reaction + */ + if (count_trxn == 0) + { + memcpy((void *) trxn.logk, (void *) r_ptr->logk, + (size_t) MAX_LOG_K_INDICES * sizeof(LDBLE)); + } + else + { + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + trxn.logk[i] += coef * (r_ptr->logk[i]); + } + } +/* + * Copy equation into work space + */ + next_token = r_ptr->token; + while (next_token->s != NULL || next_token->name != NULL) + { + if (count_trxn + 1 >= max_trxn) + { + space((void **) ((void *) &(trxn.token)), count_trxn + 1, + &max_trxn, sizeof(struct rxn_token_temp)); + } + if (next_token->s != NULL) + { + trxn.token[count_trxn].name = next_token->s->name; + trxn.token[count_trxn].s = next_token->s; + } + else + { + trxn.token[count_trxn].name = next_token->name; + trxn.token[count_trxn].s = NULL; + } + trxn.token[count_trxn].coef = coef * next_token->coef; + count_trxn++; + next_token++; + } + if (combine == TRUE) + trxn_combine(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +trxn_combine(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Combines coefficients of tokens that are equal in temporary + * reaction structure, trxn. + */ + int j, k; +/* + * Sort trxn species + */ + trxn_sort(); +/* + * Combine trxn tokens + */ + j = 1; + for (k = 2; k < count_trxn; k++) + { + if (trxn.token[k].s != NULL) + { + if ((j > 0) && (trxn.token[k].s == trxn.token[j].s)) + { + trxn.token[j].coef += trxn.token[k].coef; + if (equal(trxn.token[j].coef, 0.0, 1e-5)) + j--; + } + else + { + j++; + if (k != j) + { + trxn.token[j].name = trxn.token[k].name; + trxn.token[j].s = trxn.token[k].s; + trxn.token[j].coef = trxn.token[k].coef; + } + } + } + else + { + if ((j > 0) && (trxn.token[k].s == trxn.token[j].s) + && (trxn.token[k].name == trxn.token[j].name)) + { + trxn.token[j].coef += trxn.token[k].coef; + if (equal(trxn.token[j].coef, 0.0, 1e-5)) + j--; + } + else + { + j++; + if (k != j) + { + trxn.token[j].name = trxn.token[k].name; + trxn.token[j].s = trxn.token[k].s; + trxn.token[j].coef = trxn.token[k].coef; + } + } + } + } + count_trxn = j + 1; /* number excluding final NULL */ + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +trxn_copy(struct reaction *rxn_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Copies trxn to a reaction structure. + * + * Input: rxn_ptr, pointer to reaction structure to copy trxn to. + * + */ + int i; +/* + * Copy logk data + */ + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + rxn_ptr->logk[i] = trxn.logk[i]; + } +/* + * Copy dz data + */ + for (i = 0; i < 3; i++) + { + rxn_ptr->dz[i] = trxn.dz[i]; + } +/* + * Copy tokens + */ + for (i = 0; i < count_trxn; i++) + { + rxn_ptr->token[i].s = trxn.token[i].s; + rxn_ptr->token[i].name = trxn.token[i].name; + rxn_ptr->token[i].coef = trxn.token[i].coef; + } + rxn_ptr->token[count_trxn].s = NULL; + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +trxn_find_coef(const char *str, int start) +/* ---------------------------------------------------------------------- */ +{ +/* + * Finds coefficient of specified token in trxn. + * Input: str, token name in reaction. + * + * Return: 0.0, if token not found. + * coefficient of token, if token found. + */ + int i; + LDBLE coef; + + coef = 0.0; + for (i = start; i < count_trxn; i++) + { + if (strcmp(trxn.token[i].s->name, str) == 0) + { + coef = trxn.token[i].coef; + break; + } + } + return (coef); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +trxn_multiply(LDBLE coef) +/* ---------------------------------------------------------------------- */ +{ +/* + * Multiplies temporary reaction, trxn, by a constant + * + * Arguments: + * input: coef multiplier. + */ + int i; +/* + * Multiply log k for reaction + */ + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + trxn.logk[i] *= coef; + } +/* + * Multiply dz for reaction + */ + for (i = 0; i < 3; i++) + { + trxn.dz[i] *= coef; + } +/* + * Multiply coefficients of reaction + */ + for (i = 0; i < count_trxn; i++) + { + trxn.token[i].coef *= coef; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +trxn_print(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Prints trxn + */ + int i; +/* + * Print log k for reaction + */ + + output_msg(sformatf( "\tlog k data:\n")); + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + output_msg(sformatf( "\t\t%f\n", (double) trxn.logk[i])); + } + +/* + * Print dz for reaction + */ + output_msg(sformatf( "\tdz data:\n")); + for (i = 0; i < 3; i++) + { + output_msg(sformatf( "\t\t%f\n", (double) trxn.dz[i])); + } +/* + * Print stoichiometry + */ + output_msg(sformatf( "\tReaction stoichiometry\n")); + for (i = 0; i < count_trxn; i++) + { + output_msg(sformatf( "\t\t%-20s\t%10.2f\n", trxn.token[i].name, + (double) trxn.token[i].coef)); + } + output_msg(sformatf( "\n")); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +trxn_reverse_k(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Changes K from dissociation to association and back + */ + int i; +/* + * Accumulate log k for reaction + */ + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + trxn.logk[i] = -trxn.logk[i]; + } + for (i = 0; i < 3; i++) + { + trxn.dz[i] = -trxn.dz[i]; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +trxn_sort(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Compare names in tokens in trxn array for sorting + */ + if (count_trxn - 1 > 0) + { + qsort(&trxn.token[1], + (size_t) count_trxn - 1, + (size_t) sizeof(struct rxn_token_temp), rxn_token_temp_compare); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +trxn_swap(const char *token) +/* ---------------------------------------------------------------------- */ +{ +/* + * Moves specified token to initial position in reaction. + * Input: token, token name to move to initial position. + * + * Return: ERROR, if token not found. + * OK, if token moved to initial position. + */ + int i, j; + LDBLE coef; +/* + * Locate token + */ + for (j = 0; j < count_trxn; j++) + { + if (strcmp(trxn.token[j].s->name, token) == 0) + break; + } + if (j >= count_trxn) + { + input_error++; + error_string = sformatf( "Could not find token in equation, %s.", token); + error_msg(error_string, CONTINUE); + for (i = 0; i < count_trxn; i++) + { + output_msg(sformatf( "%f\t%s\t", + (double) trxn.token[i].coef, trxn.token[i].name)); + } + output_msg(sformatf( "\n")); + return (ERROR); + } +/* + * Swap token to first position + */ + trxn.token[count_trxn].name = trxn.token[0].name; + trxn.token[count_trxn].s = trxn.token[0].s; + trxn.token[count_trxn].coef = trxn.token[0].coef; + + trxn.token[0].name = trxn.token[j].name; + trxn.token[0].s = trxn.token[j].s; + trxn.token[0].coef = trxn.token[j].coef; + + trxn.token[j].name = trxn.token[count_trxn].name; + trxn.token[j].s = trxn.token[count_trxn].s; + trxn.token[j].coef = trxn.token[count_trxn].coef; +/* + * Make coefficient of token -1.0 + */ + coef = -1.0 / trxn.token[0].coef; + trxn_multiply(coef); + return (OK); +} + +/* ********************************************************************** + * + * Routines related to structure "unknown" + * + * ********************************************************************** */ +/* ---------------------------------------------------------------------- */ +struct unknown * Phreeqc:: +unknown_alloc(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Allocates space to an "unknown" structure + * arguments: void + * return: pointer to an "unknown" structure + */ + struct unknown *unknown_ptr; +/* + * Allocate space + */ + unknown_ptr = (struct unknown *) PHRQ_malloc(sizeof(struct unknown)); + if (unknown_ptr == NULL) + malloc_error(); +/* + * set pointers in structure to NULL + */ + unknown_ptr->type = 0; + unknown_ptr->moles = 0.0; + unknown_ptr->ln_moles = 0.0; + unknown_ptr->f = 0.0; + unknown_ptr->sum = 0.0; + unknown_ptr->delta = 0.0; + unknown_ptr->la = 0.0; + unknown_ptr->number = 0; + unknown_ptr->description = NULL; + unknown_ptr->master = NULL; + unknown_ptr->phase = NULL; + unknown_ptr->si = 0.0; + unknown_ptr->s = NULL; + unknown_ptr->exch_comp = NULL; + unknown_ptr->pp_assemblage_comp_name = NULL; + unknown_ptr->pp_assemblage_comp_ptr = NULL; + unknown_ptr->ss_name = NULL; + unknown_ptr->ss_ptr = NULL; + unknown_ptr->ss_comp_name = NULL; + unknown_ptr->ss_comp_ptr = NULL; + unknown_ptr->ss_comp_number = 0; + unknown_ptr->ss_in = FALSE; + unknown_ptr->surface_comp = NULL; + unknown_ptr->related_moles = 0.0; + unknown_ptr->potential_unknown = NULL; + unknown_ptr->potential_unknown1 = NULL; + unknown_ptr->potential_unknown2 = NULL; + unknown_ptr->count_comp_unknowns = 0; + unknown_ptr->comp_unknowns = NULL; + unknown_ptr->phase_unknown = NULL; + unknown_ptr->surface_charge = NULL; + unknown_ptr->mass_water = 0.0; + unknown_ptr->dissolve_only = FALSE; + unknown_ptr->inert_moles = 0.0; + unknown_ptr->V_m = 0.0; + unknown_ptr->pressure = 0.0; + unknown_ptr->mb_number = 0; + unknown_ptr->iteration = 0; + + return (unknown_ptr); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +unknown_delete(int i) +/* ---------------------------------------------------------------------- */ +{ +/* + * Delete unknow from list x + */ + int j; + + unknown_free(x[i]); + for (j = i; j < (count_unknowns); j++) + { + x[j] = x[j + 1]; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +unknown_free(struct unknown *unknown_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Frees space allocated to an unknown structure, frees unknown_ptr. + */ + if (unknown_ptr == NULL) + return (ERROR); + unknown_ptr->master = + (struct master **) free_check_null(unknown_ptr->master); + if (unknown_ptr->type == SURFACE_CB) + { + /* + surface_charge_free(unknown_ptr->surface_charge); + unknown_ptr->surface_charge = (struct surface_charge *) free_check_null(unknown_ptr->surface_charge); + */ + } + unknown_ptr->comp_unknowns = + (struct unknown **) free_check_null(unknown_ptr->comp_unknowns); + unknown_ptr = (struct unknown *) free_check_null(unknown_ptr); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +system_duplicate(int i, int save_old) +/* ---------------------------------------------------------------------- */ +{ + Utilities::Rxn_copy(Rxn_solution_map, i, save_old); + + Utilities::Rxn_copy(Rxn_pp_assemblage_map, i, save_old); + + Utilities::Rxn_copy(Rxn_exchange_map, i, save_old); + + Utilities::Rxn_copy(Rxn_surface_map, i, save_old); + + Utilities::Rxn_copy(Rxn_gas_phase_map, i, save_old); + + Utilities::Rxn_copy(Rxn_kinetics_map, i, save_old); + + Utilities::Rxn_copy(Rxn_ss_assemblage_map, i, save_old); + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +struct logk * Phreeqc:: +logk_store(char *name, int replace_if_found) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the hash table for logk. + * + * Pointer to a logk structure is always returned. + * + * If the string is not found, a new entry is made in the hash table. Pointer to + * the new structure is returned. + * If "name" is found and replace is true, pointers in old logk structure + * are freed and replaced with additional input. + * If "name" is found and replace is false, the old logk structure is not + * modified and a pointer to it is returned. + * + * Arguments: + * name input, character string to be found in "logk". + * replace_if_found input, TRUE means reinitialize logk structure if found + * FALSE means just return pointer if found. + * + * Returns: + * pointer to logk structure "logk" where "name" can be found. + */ + int n; + struct logk *logk_ptr; + ENTRY item, *found_item; +/* + * Search list + */ + str_tolower(name); + item.key = name; + item.data = NULL; + found_item = hsearch_multi(logk_hash_table, item, FIND); + + if (found_item != NULL && replace_if_found == FALSE) + { + logk_ptr = (struct logk *) (found_item->data); + return (logk_ptr); + } + else if (found_item != NULL && replace_if_found == TRUE) + { + logk_ptr = (struct logk *) (found_item->data); + logk_init(logk_ptr); + } + else + { + n = count_logk++; + /* make sure there is space in s */ + if (count_logk >= max_logk) + { + space((void **) ((void *) &logk), count_logk, &max_logk, + sizeof(struct logk *)); + } + /* Make new logk structure */ + logk[n] = logk_alloc(); + logk_ptr = logk[n]; + } + /* set name and z in pointer in logk structure */ + logk_ptr->name = string_hsave(name); +/* + * Update hash table + */ + item.key = logk_ptr->name; + item.data = (void *) logk_ptr; + found_item = hsearch_multi(logk_hash_table, item, ENTER); + if (found_item == NULL) + { + error_string = sformatf( "Hash table error in logk_store."); + error_msg(error_string, CONTINUE); + } + + return (logk_ptr); +} + +/* ---------------------------------------------------------------------- */ +struct logk * Phreeqc:: +logk_alloc(void) +/* ---------------------------------------------------------------------- */ +/* + * Allocates space to a logk structure, initializes + * arguments: void + * return: pointer to a logk structure + */ +{ + struct logk *logk_ptr; + logk_ptr = (struct logk *) PHRQ_malloc(sizeof(struct logk)); + if (logk_ptr == NULL) + malloc_error(); +/* + * set pointers in structure to NULL, variables to zero + */ + logk_init(logk_ptr); + + return (logk_ptr); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +logk_init(struct logk *logk_ptr) +/* ---------------------------------------------------------------------- */ +/* + * return: pointer to a logk structure + */ +{ + int i; +/* + * set pointers in structure to NULL + */ + logk_ptr->name = NULL; +/* + * set varibles = 0 + */ + logk_ptr->lk = 0.0; + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + logk_ptr->log_k[i] = 0.0; + logk_ptr->log_k_original[i] = 0.0; + } + logk_ptr->count_add_logk = 0; + logk_ptr->add_logk = NULL; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +logk_copy2orig(struct logk *logk_ptr) +/* ---------------------------------------------------------------------- */ +/* + * Copies log k data to logk_original + */ +{ + int i; + for (i = 0; i < MAX_LOG_K_INDICES; i++) + { + logk_ptr->log_k_original[i] = logk_ptr->log_k[i]; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +struct logk * Phreeqc:: +logk_search(const char *name_in) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function locates the string "name" in the hash table for logk. + * + * Arguments: + * name input, character string to be found in "logk". + * + * Returns: + * pointer to logk structure "logk" where "name" can be found. + * or NULL if not found. + */ + struct logk *logk_ptr; + ENTRY item, *found_item; +/* + * Search list + */ + char * name = string_duplicate(name_in); + str_tolower(name); + item.key = name; + item.data = NULL; + found_item = hsearch_multi(logk_hash_table, item, FIND); + free_check_null(name); + if (found_item != NULL) + { + logk_ptr = (struct logk *) (found_item->data); + return (logk_ptr); + } + return (NULL); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +entity_exists(const char *name, int n_user) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads solution, 0 Solution + * reaction, 1 Reaction + * exchange, 2 Exchange + * surface, 3 Surface + * gas_phase, 4 Gas_phase + * equilibrium_phases, 5 Pure_phase + * solid_solution, 6 Ss_phase + * kinetics, 7 Kinetics + * mix, 8 Mix + * reaction_temperature 9 Temperature + * unknown 10 UnKnown + */ + int return_value; + char token[MAX_LENGTH]; + enum entity_type type; +/* + * Read keyword + */ + strncpy(token, name, MAX_LENGTH-1); + token[MAX_LENGTH-1] = '\0'; + type = get_entity_enum(token); + return_value = TRUE; + switch (type) + { + case UnKnown: + warning_msg + ("EXISTS expecting keyword solution, mix, kinetics, reaction, reaction_temperature, equilibrium_phases, exchange, surface, gas_phase, or solid_solutions."); + return_value = 2; + break; + case Solution: /* Solution */ + if (Utilities::Rxn_find(Rxn_solution_map, n_user) == NULL) + { + return_value = FALSE; + } + break; + case Pure_phase: /* Pure phases */ + if (Utilities::Rxn_find(Rxn_pp_assemblage_map, n_user) == NULL) + { + return_value = FALSE; + } + break; + case Reaction: /* Reaction */ + if (Utilities::Rxn_find(Rxn_reaction_map, n_user) == NULL) + { + return_value = FALSE; + } + break; + case Mix: /* Mix */ + if (Utilities::Rxn_find(Rxn_mix_map, n_user) == NULL) + { + return_value = FALSE; + } + break; + case Exchange: /* Ex */ + if (Utilities::Rxn_find(Rxn_exchange_map, n_user) == NULL) + { + return_value = FALSE; + } + break; + case Surface: /* Surface */ + if (Utilities::Rxn_find(Rxn_surface_map, n_user) == NULL) + { + return_value = FALSE; + } + break; + case Temperature: + if (Utilities::Rxn_find(Rxn_temperature_map, n_user) == NULL) + { + return_value = FALSE; + } + case Pressure: + if (Utilities::Rxn_find(Rxn_pressure_map, n_user) == NULL) + { + return_value = FALSE; + } + case Gas_phase: /* Gas */ + if (Utilities::Rxn_find(Rxn_gas_phase_map, n_user) == NULL) + { + return_value = FALSE; + } + break; + case Kinetics: /* Kinetics */ + if (Utilities::Rxn_find(Rxn_kinetics_map, n_user) == NULL) + { + return_value = FALSE; + } + break; + case Ss_phase: /* solid_solutions */ + if (Utilities::Rxn_find(Rxn_ss_assemblage_map, n_user) == NULL) + { + return_value = FALSE; + } + break; + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +enum entity_type Phreeqc:: +get_entity_enum(char *name) +/* ---------------------------------------------------------------------- */ +{ +/* + * Reads solution, 0 Solution + * reaction, 1 Reaction + * exchange, 2 Exchange + * surface, 3 Surface + * gas_phase, 4 Gas_phase + * equilibrium_phases, 5 Pure_phase + * solid_solution, 6 Ss_phase + * kinetics, 7 Kinetics + * mix, 8 Mix + * reaction_temperature 9 Temperature + * reaction_pressure + * unknown 10 UnKnown + * + */ + int i; + char *ptr; + char token[MAX_LENGTH]; +/* + * Read keyword + */ + ptr = name; + copy_token(token, &ptr, &i); + check_key(token); + + switch (next_keyword) + { + case Keywords::KEY_SOLUTION: /* Solution */ + return (Solution); + break; + case Keywords::KEY_EQUILIBRIUM_PHASES: /* Pure phases */ + return (Pure_phase); + break; + case Keywords::KEY_REACTION: /* Reaction */ + return (Reaction); + break; + case Keywords::KEY_MIX: /* Mix */ + return (Mix); + break; + case Keywords::KEY_EXCHANGE: /* Ex */ + return (Exchange); + break; + case Keywords::KEY_SURFACE: /* Surface */ + return (Surface); + break; + case Keywords::KEY_REACTION_TEMPERATURE: /* Temperature */ + return (Temperature); + break; + case Keywords::KEY_REACTION_PRESSURE: /* Pressure */ + return (Pressure); + break; + case Keywords::KEY_GAS_PHASE: /* Gas */ + return (Gas_phase); + break; + case Keywords::KEY_KINETICS: /* Kinetics */ + return (Kinetics); + break; + case Keywords::KEY_SOLID_SOLUTIONS: /* solid_solutions */ + return (Ss_phase); + break; + default: + warning_msg + ("EXISTS expecting keyword solution, mix, kinetics, reaction, reaction_temperature, equilibrium_phases, exchange, surface, gas_phase, or solid_solutions."); + break; + } + return (UnKnown); +} + +/* + * copier routines + */ +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +copier_add(struct copier *copier_ptr, int n_user, int start, int end) +/* ---------------------------------------------------------------------- */ +/* + * add new set of copy instructions + */ +{ + + if (copier_ptr->count >= copier_ptr->max) + { + copier_ptr->max = copier_ptr->count * 2; + copier_ptr->n_user = + (int *) PHRQ_realloc(copier_ptr->n_user, + (size_t) (copier_ptr->max * sizeof(int))); + if (copier_ptr->n_user == NULL) + { + malloc_error(); + return (OK); + } + copier_ptr->start = + (int *) PHRQ_realloc(copier_ptr->start, + (size_t) (copier_ptr->max * sizeof(int))); + if (copier_ptr->start == NULL) + { + malloc_error(); + return (OK); + } + copier_ptr->end = + (int *) PHRQ_realloc(copier_ptr->end, + (size_t) (copier_ptr->max * sizeof(int))); + if (copier_ptr->end == NULL) + { + malloc_error(); + return (OK); + } + } + copier_ptr->n_user[copier_ptr->count] = n_user; + copier_ptr->start[copier_ptr->count] = start; + copier_ptr->end[copier_ptr->count] = end; + copier_ptr->count++; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +copier_free(struct copier *copier_ptr) +/* ---------------------------------------------------------------------- */ +/* + * initialize copier structure + */ +{ + + copier_ptr->n_user = (int *) free_check_null(copier_ptr->n_user); + copier_ptr->start = (int *) free_check_null(copier_ptr->start); + copier_ptr->end = (int *) free_check_null(copier_ptr->end); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +copier_init(struct copier *copier_ptr) +/* ---------------------------------------------------------------------- */ +/* + * initialize copier structure + */ +{ + + copier_ptr->count = 0; + copier_ptr->max = 10; + copier_ptr->n_user = + (int *) PHRQ_malloc((size_t) (copier_ptr->max * sizeof(int))); + copier_ptr->start = + (int *) PHRQ_malloc((size_t) (copier_ptr->max * sizeof(int))); + copier_ptr->end = + (int *) PHRQ_malloc((size_t) (copier_ptr->max * sizeof(int))); + return (OK); +} +#include "StorageBin.h" + +void Phreeqc:: +Use2cxxStorageBin(cxxStorageBin & sb) +{ + //Add everything from use structure to storagebin sb + + sb.Get_system().Set_io(sb.Get_io()); + if (use.Get_mix_in()) + { + cxxMix *entity = Utilities::Rxn_find(Rxn_mix_map, use.Get_n_mix_user()); + if (entity != NULL) + { + sb.Set_Mix(use.Get_n_mix_user(), entity); + } + } + else if (use.Get_solution_in()) + { + cxxSolution *entity = Utilities::Rxn_find(Rxn_solution_map, use.Get_n_solution_user()); + if (entity != NULL) + { + sb.Set_Solution(use.Get_n_solution_user(), entity); + } + } + if (use.Get_pp_assemblage_in()) + { + cxxPPassemblage *entity_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, use.Get_n_pp_assemblage_user()); + if (entity_ptr != NULL) + { + sb.Set_PPassemblage(use.Get_n_pp_assemblage_user(), entity_ptr); + } + } + if (use.Get_exchange_in()) + { + cxxExchange *entity_ptr = Utilities::Rxn_find(Rxn_exchange_map, use.Get_n_exchange_user()); + if (entity_ptr != NULL) + { + sb.Set_Exchange(use.Get_n_exchange_user(), entity_ptr); + } + } + if (use.Get_surface_in()) + { + cxxSurface *entity_ptr = Utilities::Rxn_find(Rxn_surface_map, use.Get_n_surface_user()); + if (entity_ptr != NULL) + { + sb.Set_Surface(use.Get_n_surface_user(), entity_ptr); + } + } + if (use.Get_gas_phase_in()) + { + cxxGasPhase *entity_ptr = Utilities::Rxn_find(Rxn_gas_phase_map, use.Get_n_gas_phase_user()); + if (entity_ptr != NULL) + { + sb.Set_GasPhase(use.Get_n_gas_phase_user(), entity_ptr); + } + } + if (use.Get_ss_assemblage_in()) + { + cxxSSassemblage *entity_ptr = Utilities::Rxn_find(Rxn_ss_assemblage_map, use.Get_n_ss_assemblage_user()); + if (entity_ptr != NULL) + { + sb.Set_SSassemblage(use.Get_n_ss_assemblage_user(), entity_ptr); + } + } + if (use.Get_kinetics_in()) + { + cxxKinetics *entity_ptr = Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_kinetics_user()); + if (entity_ptr != NULL) + { + sb.Set_Kinetics(use.Get_n_kinetics_user(), entity_ptr); + } + } + if (use.Get_reaction_in()) + { + cxxReaction *entity = Utilities::Rxn_find(Rxn_reaction_map, use.Get_n_reaction_user()); + if (entity != NULL) + { + sb.Set_Reaction(use.Get_n_reaction_user(), entity); + } + } + if (use.Get_temperature_in()) + { + cxxTemperature *entity = Utilities::Rxn_find(Rxn_temperature_map, use.Get_n_temperature_user()); + if (entity != NULL) + { + sb.Set_Temperature(use.Get_n_temperature_user(), entity); + } + } + if (use.Get_pressure_in()) + { + cxxPressure *entity = Utilities::Rxn_find(Rxn_pressure_map, use.Get_n_pressure_user()); + if (entity != NULL) + { + sb.Set_Pressure(use.Get_n_pressure_user(), entity); + } + } +} + +void Phreeqc:: +phreeqc2cxxStorageBin(cxxStorageBin & sb) + // + // Fills StorageBin sb with all reactants from phreeqc instance. + // equivalent to old import_phreeqc. + // +{ + // Solutions + { + std::map::iterator it; + for (it = Rxn_solution_map.begin(); it != Rxn_solution_map.end(); it++) + { + sb.Set_Solution(it->second.Get_n_user(), &(it->second)); + } + } + // Exchangers + { + std::map::iterator it; + for (it = Rxn_exchange_map.begin(); it != Rxn_exchange_map.end(); it++) + { + sb.Set_Exchange(it->second.Get_n_user(), &(it->second)); + } + } + // GasPhases + { + std::map::iterator it; + for (it = Rxn_gas_phase_map.begin(); it != Rxn_gas_phase_map.end(); it++) + { + sb.Set_GasPhase(it->second.Get_n_user(), &(it->second)); + } + } + + // Kinetics + { + std::map::iterator it; + for (it = Rxn_kinetics_map.begin(); it != Rxn_kinetics_map.end(); it++) + { + sb.Set_Kinetics(it->second.Get_n_user(), &(it->second)); + } + } + // PPassemblages + { + std::map::iterator it; + for (it = Rxn_pp_assemblage_map.begin(); it != Rxn_pp_assemblage_map.end(); it++) + { + sb.Set_PPassemblage(it->second.Get_n_user(), &(it->second)); + } + } + // SSassemblages + { + std::map::iterator it; + for (it = Rxn_ss_assemblage_map.begin(); it != Rxn_ss_assemblage_map.end(); it++) + { + sb.Set_SSassemblage(it->second.Get_n_user(), &(it->second)); + } + } + // Surfaces + { + std::map::iterator it; + for (it = Rxn_surface_map.begin(); it != Rxn_surface_map.end(); it++) + { + sb.Set_Surface(it->second.Get_n_user(), &(it->second)); + } + } + // Mixes + { + std::map::iterator it; + for (it = Rxn_mix_map.begin(); it != Rxn_mix_map.end(); it++) + { + sb.Set_Mix(it->second.Get_n_user(), &(it->second)); + } + } + + // Reactions + { + std::map::iterator it; + for (it = Rxn_reaction_map.begin(); it != Rxn_reaction_map.end(); it++) + { + sb.Set_Reaction(it->second.Get_n_user(), &(it->second)); + } + } + + // Temperatures + { + std::map::iterator it; + for (it = Rxn_temperature_map.begin(); it != Rxn_temperature_map.end(); it++) + { + sb.Set_Temperature(it->second.Get_n_user(), &(it->second)); + } + } + + // Pressures + { + std::map::iterator it; + for (it = Rxn_pressure_map.begin(); it != Rxn_pressure_map.end(); it++) + { + sb.Set_Pressure(it->second.Get_n_user(), &(it->second)); + } + } +} + +void Phreeqc:: +phreeqc2cxxStorageBin(cxxStorageBin & sb, int n) + // + // copy phreeqc reactants numbered n to StorageBin sb + // +{ + // Solutions + { + cxxSolution *entity_ptr = Utilities::Rxn_find(Rxn_solution_map, n); + if (entity_ptr != NULL) + { + sb.Set_Solution(n, entity_ptr); + } + } + // Exchangers + { + cxxExchange *entity_ptr = Utilities::Rxn_find(Rxn_exchange_map, n); + if (entity_ptr != NULL) + { + sb.Set_Exchange(n, entity_ptr); + } + } + + // GasPhases + { + cxxGasPhase *entity_ptr = Utilities::Rxn_find(Rxn_gas_phase_map, n); + if (entity_ptr != NULL) + { + sb.Set_GasPhase(n, entity_ptr); + } + } + + // Kinetics + { + cxxKinetics *entity_ptr = Utilities::Rxn_find(Rxn_kinetics_map, n); + if (entity_ptr != NULL) + { + sb.Set_Kinetics(n, entity_ptr); + } + } + // PPassemblages + { + cxxPPassemblage *entity_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, n); + if (entity_ptr != NULL) + { + sb.Set_PPassemblage(n, entity_ptr); + } + } + // SSassemblages + { + cxxSSassemblage *entity_ptr = Utilities::Rxn_find(Rxn_ss_assemblage_map, n); + if (entity_ptr != NULL) + { + sb.Set_SSassemblage(n, entity_ptr); + } + } + // Surfaces + { + cxxSurface *entity_ptr = Utilities::Rxn_find(Rxn_surface_map, n); + if (entity_ptr != NULL) + { + sb.Set_Surface(n, entity_ptr); + } + } +} +void Phreeqc:: +cxxStorageBin2phreeqc(cxxStorageBin & sb, int n) +// +// copy all reactants from storage bin number n to phreeqc +// replaces any existing reactants in phreeqc +// +{ + // Solutions + { + std::map < int, cxxSolution >::const_iterator it = sb.Get_Solutions().find(n); + if (it != sb.Get_Solutions().end()) + { + Rxn_solution_map[n] = it->second; + } + } + // Exchangers + { + std::map < int, cxxExchange >::const_iterator it = sb.Get_Exchangers().find(n); + if (it != sb.Get_Exchangers().end()) + { + Rxn_exchange_map[n] = it->second; + } + } + + // GasPhases + { + std::map < int, cxxGasPhase >::const_iterator it = sb.Get_GasPhases().find(n); + if (it != sb.Get_GasPhases().end()) + { + Rxn_gas_phase_map[n] = it->second; + } + } + + // Kinetics + { + std::map < int, cxxKinetics >::const_iterator it = sb.Get_Kinetics().find(n); + if (it != sb.Get_Kinetics().end()) + { + Rxn_kinetics_map[n] = it->second; + } + } + // PPassemblages + { + std::map < int, cxxPPassemblage >::const_iterator it = sb.Get_PPassemblages().find(n); + if (it != sb.Get_PPassemblages().end()) + { + Rxn_pp_assemblage_map[n] = it->second; + } + } + // SSassemblages + { + std::map < int, cxxSSassemblage >::const_iterator it = sb.Get_SSassemblages().find(n); + if (it != sb.Get_SSassemblages().end()) + { + Rxn_ss_assemblage_map[n] = it->second; + } + } + // Surfaces + { + std::map < int, cxxSurface >::const_iterator it = sb.Get_Surfaces().find(n); + if (it != sb.Get_Surfaces().end()) + { + Rxn_surface_map[n] = it->second; + } + } + // Mixes + { + std::map < int, cxxMix >::const_iterator it = sb.Get_Mixes().find(n); + if (it != sb.Get_Mixes().end()) + { + Rxn_mix_map[n] = it->second; + } + } + + // Reactions + { + std::map < int, cxxReaction >::const_iterator it = sb.Get_Reactions().find(n); + if (it != sb.Get_Reactions().end()) + { + Rxn_reaction_map[n] = it->second; + } + } + // Temperatures + { + std::map < int, cxxTemperature >::const_iterator it = sb.Get_Temperatures().find(n); + if (it != sb.Get_Temperatures().end()) + { + Rxn_temperature_map[n] = it->second; + } + } + // Pressures + { + std::map < int, cxxPressure >::const_iterator it = sb.Get_Pressures().find(n); + if (it != sb.Get_Pressures().end()) + { + Rxn_pressure_map[n] = it->second; + } + } +} +void Phreeqc:: +cxxStorageBin2phreeqc(cxxStorageBin & sb) +// +// copy data from storage bin to phreeqc +// replaces any existing reactants in phreeqc +// +{ + // Solutions + { + std::map < int, cxxSolution >::const_iterator it = sb.Get_Solutions().begin(); + for ( ; it != sb.Get_Solutions().end(); it++) + { + Rxn_solution_map[it->first] = it->second; + } + } + // Exchangers + { + std::map < int, cxxExchange >::const_iterator it = sb.Get_Exchangers().begin(); + for ( ; it != sb.Get_Exchangers().end(); it++) + { + Rxn_exchange_map[it->first] = it->second; + } + } + + // GasPhases + { + std::map < int, cxxGasPhase >::const_iterator it = sb.Get_GasPhases().begin(); + for ( ; it != sb.Get_GasPhases().end(); it++) + { + Rxn_gas_phase_map[it->first] = it->second; + } + } + + // Kinetics + { + std::map < int, cxxKinetics >::const_iterator it = sb.Get_Kinetics().begin(); + for ( ; it != sb.Get_Kinetics().end(); it++) + { + Rxn_kinetics_map[it->first] = it->second; + } + } + // PPassemblages + { + std::map < int, cxxPPassemblage >::const_iterator it = sb.Get_PPassemblages().begin(); + for ( ; it != sb.Get_PPassemblages().end(); it++) + { + Rxn_pp_assemblage_map[it->first] = it->second; + } + } + // SSassemblages + { + std::map < int, cxxSSassemblage >::const_iterator it = sb.Get_SSassemblages().begin(); + for ( ; it != sb.Get_SSassemblages().end(); it++) + { + Rxn_ss_assemblage_map[it->first] = it->second; + } + } + // Surfaces + { + std::map < int, cxxSurface >::const_iterator it = sb.Get_Surfaces().begin(); + for ( ; it != sb.Get_Surfaces().end(); it++) + { + Rxn_surface_map[it->first] = it->second; + } + } + // Mixes + { + std::map < int, cxxMix >::const_iterator it = sb.Get_Mixes().begin(); + for ( ; it != sb.Get_Mixes().end(); it++) + { + Rxn_mix_map[it->first] = it->second; + } + } + + // Reactions + { + std::map < int, cxxReaction >::const_iterator it = sb.Get_Reactions().begin(); + for ( ; it != sb.Get_Reactions().end(); it++) + { + Rxn_reaction_map[it->first] = it->second; + } + } + // Temperatures + { + std::map < int, cxxTemperature >::const_iterator it = sb.Get_Temperatures().begin(); + for ( ; it != sb.Get_Temperatures().end(); it++) + { + Rxn_temperature_map[it->first] = it->second; + } + } + // Pressures + { + std::map < int, cxxPressure >::const_iterator it = sb.Get_Pressures().begin(); + for ( ; it != sb.Get_Pressures().end(); it++) + { + Rxn_pressure_map[it->first] = it->second; + } + } +} + + diff --git a/phreeqcpp/sundialsmath.cpp b/phreeqcpp/sundialsmath.cpp new file mode 100644 index 00000000..c060716f --- /dev/null +++ b/phreeqcpp/sundialsmath.cpp @@ -0,0 +1,126 @@ +/************************************************************************** + * * + * File : sundialsmath.c * + * Programmers : Scott D. Cohen and Alan C. Hindmarsh @ LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the implementation file for a C math library. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ + + +#include +#include +#include "sundialsmath.h" +#include "sundialstypes.h" + +#define ZERO RCONST(0.0) +#define ONE RCONST(1.0) +#define TWO RCONST(2.0) + + +realtype +UnitRoundoff(void) +{ + realtype u; + volatile realtype one_plus_u; + + u = ONE; + one_plus_u = ONE + u; + while (one_plus_u != ONE) + { + u /= TWO; + one_plus_u = ONE + u; + } + u *= TWO; + + return (u); +} + + +realtype +RPowerI(realtype base, int exponent) +{ + int i, expt; + realtype prod; + + prod = ONE; + expt = ABS(exponent); + for (i = 1; i <= expt; i++) + prod *= base; + if (exponent < 0) + prod = ONE / prod; + return (prod); +} + + +realtype +RPowerR(realtype base, realtype exponent) +{ + + if (base <= ZERO) + return (ZERO); + + /* return((realtype)pow((double)base,(double)exponent)); */ + return ((realtype) pow(base, exponent)); +} + + +realtype +RSqrt(realtype x) +{ + if (x <= ZERO) + return (ZERO); + + /* return((realtype) sqrt((double) x)); */ + return ((realtype) sqrt(x)); +} diff --git a/phreeqcpp/sundialsmath.h b/phreeqcpp/sundialsmath.h new file mode 100644 index 00000000..59e49d3a --- /dev/null +++ b/phreeqcpp/sundialsmath.h @@ -0,0 +1,162 @@ +/************************************************************************** + * * + * File : sundialsmath.h * + * Programmers : Scott D. Cohen and Alan C. Hindmarsh @ LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This is the header file for a C math library. The routines * + * listed here work with the type realtype as defined in * + * sundialstypes.h. * + * To do single precision floating point arithmetic, set the type * + * realtype to be float. To do double precision arithmetic, set * + * the type realtype to be double. The default implementations * + * for RPowerR and RSqrt call standard math library functions * + * which do double precision arithmetic. If this is unacceptable * + * when realtype is float, then the user should re-implement * + * these two routines by calling single precision routines * + * available on his/her machine. * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ +#ifndef _sundialsmath_h +#define _sundialsmath_h + +#include "sundialstypes.h" + + +/****************************************************************** + * * + * Macros : MIN, MAX, ABS, SQR * + *----------------------------------------------------------------* + * MIN(A, B) returns the minimum of A and B. * + * * + * MAX(A, B) returns the maximum of A and B. * + * * + * ABS(A) returns the absolute value of A. * + * * + * SQR(A) returns the square of A. * + * * + ******************************************************************/ +#ifndef MIN +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#endif + +#ifndef MAX +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#endif + +#ifndef ABS +#define ABS(A) ((A < 0) ? -(A) : (A)) +#endif + +#ifndef SQR +#define SQR(A) ((A) * (A)) +#endif + +/****************************************************************** + * * + * Function : UnitRoundoff * + * Usage : realtype uround; * + * uround = UnitRoundoff(); * + *----------------------------------------------------------------* + * UnitRoundoff returns the unit roundoff u for real floating * + * point arithmetic, where u is defined to be the smallest * + * positive real such that 1.0 + u != 1.0. * + * * + ******************************************************************/ + + realtype UnitRoundoff(void); + + +/****************************************************************** + * * + * Function : RPowerI * + * Usage : int exponent; * + * realtype base, ans; * + * ans = RPowerI(base,exponent); * + *----------------------------------------------------------------* + * RPowerI returns the value base^exponent, where base is a * + * realtype and exponent is an int. * + * * + ******************************************************************/ + + realtype RPowerI(realtype base, int exponent); + + +/****************************************************************** + * * + * Function : RPowerR * + * Usage : realtype base, exponent, ans; * + * ans = RPowerR(base,exponent); * + *----------------------------------------------------------------* + * RPowerR returns the value base^exponent, where both base and * + * exponent are realtype. If base < 0.0, then RPowerR returns 0.0 * + * * + ******************************************************************/ + + realtype RPowerR(realtype base, realtype exponent); + + +/****************************************************************** + * * + * Function : RSqrt * + * Usage : realtype sqrt_x; * + * sqrt_x = RSqrt(x); * + *----------------------------------------------------------------* + * RSqrt(x) returns the square root of x. If x < 0.0, then RSqrt * + * returns 0.0. * + * * + ******************************************************************/ + + realtype RSqrt(realtype x); + + +#endif diff --git a/phreeqcpp/sundialstypes.h b/phreeqcpp/sundialstypes.h new file mode 100644 index 00000000..1003410f --- /dev/null +++ b/phreeqcpp/sundialstypes.h @@ -0,0 +1,183 @@ +#if defined(WIN32) +#include +#endif +#include "phrqtype.h" +#ifndef _INC_SUNDIALSTYPES_H +#define _INC_SUNDIALSTYPES_H +/************************************************************************** + * * + * File : sundialstypes.h * + * Programmers : Scott D. Cohen and Alan C. Hindmarsh @ LLNL * + * Version of : 26 June 2002 * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California * + * Produced at the Lawrence Livermore National Laboratory * + * All rights reserved * + * For details, see LICENSE below * + *------------------------------------------------------------------------* + * This header file exports three types: realtype, integertype, * + * and booleantype, as well as the constants TRUE and FALSE. * + * * + * Users should #include "sundialstypes.h" in any file that * + * shhould be easily modifiable to work with different real or * + * integer types and use the exported names realtype and * + * integertype within such a file. * + * The types for realtype and integertype below have been set to * + * double and long int, respectively. A user should modify these * + * type declarations as he/she sees fit. For example, if a user * + * wants the work with type float because double precision * + * floating point arithmetic is too expensive on the user's * + * machine, then the definition below should be changed to: * + * * + * typedef float realtype; * + * * + * Similarly, if a user does not need to work with extremely large * + * integers (see the system header file for the limits * + * on type int and long int on your machine), then the user * + * should change the definition below to: * + * * + * typedef int integertype; * + * * + * The constants SUNDIALS_FLOAT, SUNDIALS_DOUBLE, SUNDIALS_INT, * + * SUNDIALS_LONG indicate the underlying types for realtype and * + * integertype. They should be set as follows: * + * * + * (1) #define SUNDIALS_FLOAT 1 * + * #define SUNDIALS_DOUBLE 0 (real is float) * + * * + * (2) #define SUNDIALS_FLOAT 0 * + * #define SUNDIALS_DOUBLE 1 (real is double) * + * * + * (3) #define SUNDIALS_INT 1 * + * #define SUNDIALS_LONG 0 (integer is int) * + * * + * (4) #define SUNDIALS_INT 0 * + * #define SUNDIALS_LONG 1 (integer is long int) * + * * + * Thus the legal types for realtype are float and double, while * + * the legal types for integertype are int and long int. * + * The macro RCONST gives a user a convenient way to define real * + * constants. To use the real constant 1.0, for example, the * + * user should write * + * * + * #define ONE RCONST(1.0) * + * * + * If realtype is double, then RCONST(1.0) expands to 1.0. * + * If realtype is float, then RCONST(1.0) expands to 1.0F. * + * There is never a need to explicitly cast 1.0 to (realtype). * + * * + *------------------------------------------------------------------------* + * LICENSE * + *------------------------------------------------------------------------* + * Copyright (c) 2002, The Regents of the University of California. * + * Produced at the Lawrence Livermore National Laboratory. * + * Written by S.D. Cohen, A.C. Hindmarsh, R. Serban, * + * D. Shumaker, and A.G. Taylor. * + * UCRL-CODE-155951 (CVODE) * + * UCRL-CODE-155950 (CVODES) * + * UCRL-CODE-155952 (IDA) * + * UCRL-CODE-237203 (IDAS) * + * UCRL-CODE-155953 (KINSOL) * + * All rights reserved. * + * * + * This file is part of SUNDIALS. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright * + * notice, this list of conditions and the disclaimer below. * + * * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the disclaimer (as noted below) * + * in the documentation and/or other materials provided with the * + * distribution. * + * * + * 3. Neither the name of the UC/LLNL nor the names of its contributors * + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * + * REGENTS OF THE UNIVERSITY OF CALIFORNIA, THE U.S. DEPARTMENT OF ENERGY * + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + **************************************************************************/ +#ifndef _sundialstypes_h +#define _sundialstypes_h + + +/****************************************************************** + * * + * Types : realtype, integertype * + *----------------------------------------------------------------* + * The types realtype and integertype are currently set to double * + * and int, respectively. See the documentation at the top for * + * usage details and a description of associated constants and * + * macros. * + * * + ******************************************************************/ + + typedef LDBLE realtype; + typedef long int integertype; + +#define SUNDIALS_FLOAT 0 +#define SUNDIALS_DOUBLE 1 + +#define SUNDIALS_LONG 1 +#define SUNDIALS_INT 0 + +#if SUNDIALS_FLOAT + +#define RCONST(x) x##F + +#elif SUNDIALS_DOUBLE + +#define RCONST(x) x + +#endif + + +/****************************************************************** + * * + * Type : booleantype * + * Constants : FALSE, TRUE * + *----------------------------------------------------------------* + * ANSI C does not have a built-in boolean type. Below is the * + * definition for a new type booleantype. The advantage of using * + * the name booleantype (instead of int) is an increase in code * + * readability. * + * It allows the programmer to make a distinction between int and * + * boolean data. Variables of type booleantype are intended to * + * have only the two values FALSE and TRUE which are defined * + * below to be equal to 0 and 1, respectively. * + * * + ******************************************************************/ + +#ifndef booleantype +#define booleantype int +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#endif +/* +#ifdef __cplusplus +} +#endif +*/ +#endif /* _INC_SUNDIALSTYPES_H */ diff --git a/phreeqcpp/tally.cpp b/phreeqcpp/tally.cpp new file mode 100644 index 00000000..994c6af2 --- /dev/null +++ b/phreeqcpp/tally.cpp @@ -0,0 +1,1430 @@ +#include "Utils.h" +#include "Phreeqc.h" +#include "phqalloc.h" +#include "Temperature.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "Reaction.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "cxxKinetics.h" +#include "Solution.h" +/* + Calling sequence + +Initialization: +--------------- + +build_tally_table(); finds all elements (rows), + possible return values (columns), + allocates space +get_tally_table_rows_columns(int *rows, int *columns) + returns number of rows and columns in table +get_tally_table_row_heading(int row, char *string) + row is C row number + returns row descripter for row +get_tally_table_column_heading(int column, int *type, char *string) + column is C column number + returns column heading for column + +Each call to phreeqc: +--------------------- + +zero_tally_table(); initialize table to 0s +set_reaction_moles(n_user, moles) n_user is reservoir number + moles is number of moles of reaction to add + +int set_reaction_temperature(n_user, tc) + +fill_tally_table(int *n_user, int index_conservative, int n_buffer) + n_user is reservoir number + index_conservative is solution number + where conservative mixing is stored + slot is 0 for initial + + run phreeqc here. + +fill_tally_table(int *n_user, int index_conservative, int n_buffer) + n_user is reservoir number + index_conservative is solution number + where conservative mixing is stored + slot is 1 for final +store_tally_table(LDBLE *array, int row_dim, int col_dim, LDBLE fill_factor) + row_dim is Fortran dimension + col_dim is Fortran dimension + array is space from Fortran + stores conservative mixing (column 0) + stores reaction (column 1) + difference between slot 1 and slot 0 for + all other entities (columns 2-n) + +Finalization: +------------- +int free_tally_table(void); Frees space + +*/ +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_all_components(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Counts components in any defined solution, gas_phase, exchanger, + * surface, or pure_phase_assemblage + * + * Returns n_comp, which is total, including H, O, elements, and Charge + * names contains character strings with names of components + */ + int i, j; +/* + * Accumulate all aqueous components + */ + add_all_components_tally(); + + // add secondary master species + for (i = 0; i < count_master; i++) + { + if (master[i]->total > 0.0 && master[i]->s->type == AQ && master[i]->primary == TRUE) + { + for (int j = i + 1; j < count_master; j++) + { + if (master[j]->elt->primary == master[i]) + { + master[j]->total = 1.0; + } + else + { + break; + } + } + } + } + + +/* + * Count components + Alkalinity + total_h + total_o + */ + tally_count_component = 3; + for (i = 0; i < count_master; i++) + { + if (master[i]->total > 0.0 && master[i]->s->type == AQ) + { + tally_count_component++; + } + } +/* + * Put information in buffer. + * Buffer contains an entry for every primary master + * species that can be used in the transport problem. + */ + t_buffer = + (struct tally_buffer *) PHRQ_malloc((size_t) tally_count_component * + sizeof(struct tally_buffer)); + + // store alkalinity + j = 0; + t_buffer[j].name = string_hsave("Alkalinity"); + t_buffer[j].master = master_bsearch("Alkalinity"); + t_buffer[j].gfw = t_buffer[j].master->elt->gfw; + j++; + + // store total_h + t_buffer[j].name = string_hsave("Total_H"); + t_buffer[j].master = NULL; + compute_gfw("H", &(t_buffer[j].gfw)); + j++; + + // store total_o + t_buffer[j].name = string_hsave("Total_O"); + t_buffer[j].master = NULL; + compute_gfw("O", &(t_buffer[j].gfw)); + j++; + + for (i = 0; i < count_master; i++) + { + if (master[i]->total > 0.0 && master[i]->s->type == AQ) + { + t_buffer[j].name = master[i]->elt->name; + t_buffer[j].master = master[i]; + t_buffer[j].gfw = master[i]->elt->gfw; + j++; + } + } + /* + * Return value + */ + /**n_comp = count_component;*/ + count_tally_table_rows = tally_count_component; + return (OK); +} +#ifdef SKIP +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_all_components(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Counts components in any defined solution, gas_phase, exchanger, + * surface, or pure_phase_assemblage + * + * Returns n_comp, which is total, including H, O, elements, and Charge + * names contains character strings with names of components + */ + int i, j; +/* + * Accumulate all aqueous components + */ + add_all_components_tally(); +/* + * Count components, 2 for hydrogen, oxygen, + others, + */ + tally_count_component = 0; + for (i = 0; i < count_master; i++) + { + if (master[i]->total > 0.0 && master[i]->s->type == AQ) + { + tally_count_component++; + } + } +/* + * Put information in buffer. + * Buffer contains an entry for every primary master + * species that can be used in the transport problem. + * Each entry in buffer is sent to HST for transort. + */ + t_buffer = + (struct tally_buffer *) PHRQ_malloc((size_t) tally_count_component * + sizeof(struct tally_buffer)); + j = 0; + for (i = 0; i < count_master; i++) + { + if (master[i]->total > 0.0 && master[i]->s->type == AQ) + { + t_buffer[j].name = master[i]->elt->name; + t_buffer[j].master = master[i]; + t_buffer[j].gfw = master[i]->elt->gfw; + j++; + } + } + /* + * Return value + */ + /**n_comp = count_component;*/ + count_tally_table_rows = tally_count_component; + return (OK); +} +#endif +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +store_tally_table(LDBLE * l_array, int row_dim_in, int col_dim, LDBLE fill_factor) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + int row_dim = row_dim_in + 1; + if (tally_table == NULL) + { + input_error++; + error_msg("Tally table not defined, get_tally_table_rows_columns", + CONTINUE); + return (ERROR); + } + if (count_tally_table_rows > row_dim) + { + input_error++; + error_msg + ("Too many tally table rows for Fortran storage, store_tally_table", + CONTINUE); + return (ERROR); + } + if (count_tally_table_columns > col_dim) + { + input_error++; + error_msg + ("Too many tally table columns for Fortran storage, store_tally_table", + CONTINUE); + return (ERROR); + } + /* + * store conservative mixing solution + */ + for (j = 0; j < count_tally_table_rows; j++) + { + l_array[j] = tally_table[0].total[1][j].moles; + } + /* + * store reaction solution + */ + for (j = 0; j < count_tally_table_rows; j++) + { + l_array[row_dim + j] = tally_table[1].total[1][j].moles; + } + /* + * Calculate deltas + */ + + diff_tally_table(); + + /* + * store deltas for all other columns + */ + for (i = 2; i < count_tally_table_columns; i++) + { + for (j = 0; j < count_tally_table_rows; j++) + { + l_array[i * row_dim + j] = + tally_table[i].total[2][j].moles / fill_factor; + } + } + + /* + * Add row for total moles of reactant + */ + for (i = 0; i < count_tally_table_columns; i++) + { + l_array[i * row_dim + count_tally_table_rows] = + tally_table[i].moles / fill_factor; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_tally_table_rows_columns(int *rows, int *columns) +/* ---------------------------------------------------------------------- */ +{ + *rows = 0; + *columns = 0; + if (tally_table == NULL) + { + input_error++; + error_msg("tally table not defined, get_tally_table_rows_columns", + CONTINUE); + return (ERROR); + } + *rows = count_tally_table_rows; + *columns = count_tally_table_columns; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_tally_table_row_heading(int row, char *string) +/* ---------------------------------------------------------------------- */ +{ + /* + * row is C row number + */ + strcpy(string, ""); + if (tally_table == NULL) + { + input_error++; + error_msg("Tally table not defined, get_tally_table row_heading", + CONTINUE); + return (ERROR); + } + if (row >= count_tally_table_rows) + { + input_error++; + error_msg + ("Row exceeds tally table size, get_tally_table row_heading", + CONTINUE); + return (ERROR); + } + strcpy(string, t_buffer[row].name); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_tally_table_column_heading(int column, int *type, char *string) +/* ---------------------------------------------------------------------- */ +{ + /* + * column is C column number + */ + *type = -1; + strcpy(string, ""); + if (tally_table == NULL) + { + input_error++; + error_msg("tally table not defined, get_tally_table_column_heading", + CONTINUE); + return (ERROR); + } + if (column >= count_tally_table_columns) + { + input_error++; + error_msg + ("column exceeds tally table size, get_tally_table_column_heading", + CONTINUE); + return (ERROR); + } + strcpy(string, tally_table[column].name); + *type = tally_table[column].type; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +free_tally_table(void) +/* ---------------------------------------------------------------------- */ +{ + int i, k; + if (tally_table == NULL) + return (OK); + for (i = 0; i < count_tally_table_columns; i++) + { + if (tally_table[i].formula != NULL) + tally_table[i].formula = + (struct elt_list *) free_check_null(tally_table[i].formula); + for (k = 0; k < 3; k++) + { + tally_table[i].total[k] = + (struct tally_buffer *) free_check_null(tally_table[i]. + total[k]); + } + } + tally_table = (struct tally *) free_check_null(tally_table); + t_buffer = (struct tally_buffer *) free_check_null(t_buffer); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +zero_tally_table(void) +/* ---------------------------------------------------------------------- */ +{ + int i, j, k; + for (i = 0; i < count_tally_table_columns; i++) + { + tally_table[i].moles = 0.0; + for (j = 0; j < count_tally_table_rows; j++) + { + for (k = 0; k < 3; k++) + { + tally_table[i].total[k][j].moles = 0; + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +diff_tally_table(void) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + /* + output_msg("Difference\n\n"); + */ + for (i = 0; i < count_tally_table_columns; i++) + { + for (j = 0; j < count_tally_table_rows; j++) + { + tally_table[i].total[2][j].moles = + tally_table[i].total[1][j].moles - + tally_table[i].total[0][j].moles; + } + + /* + output_msg(sformatf( "Column %d\t%s\tType: %d\n", i, tally_table[i].name, tally_table[i].type)); + for (j = 0; j < count_tally_table_rows; j++) { + output_msg(sformatf( "\t%d\t%s\t%e\n", j, tally_table[i].total[2][j].name, (double) tally_table[i].total[2][j].moles)); + } + */ + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_tally_table(void) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + output_msg(sformatf( "Tally_table\n\n")); + for (i = 0; i < count_tally_table_columns; i++) + { + output_msg(sformatf( "%s\tType: %d\n", tally_table[i].name, + tally_table[i].type)); + output_msg(sformatf( "\n")); + output_msg(sformatf( "\t%15s\t%15s\t%15s\n", "Initial", + "Final", "Difference")); + for (j = 0; j < count_tally_table_rows; j++) + { + output_msg(sformatf( "%5s\t%15g\t%15g\t%15g\n", + t_buffer[j].name, tally_table[i].total[0][j].moles, + tally_table[i].total[1][j].moles, + tally_table[i].total[2][j].moles)); + } + output_msg(sformatf( "\n")); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +fill_tally_table(int *n_user, int index_conservative, int n_buffer) +/* ---------------------------------------------------------------------- */ +{ +/* + * Routine accumulates elements from all solutions, phases, gas phases, + * exchangers, and surfaces. + */ + int found; + LDBLE moles; + //char *ptr; + /* + * Cycle through tally table columns + */ + for (int i = 0; i < count_tally_table_columns; i++) + { + switch (tally_table[i].type) + { + case Solution: +/* + * fill solution + */ + if (n_user[Solution] < 0 || n_buffer == 0) + break; + { + cxxSolution *solution_ptr = NULL;; + if (i == 0) + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, index_conservative); + } + else if (i == 1) + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, n_user[Solution]); + } + else + { + error_msg("Solution is not in first two columns of tally_table", STOP); + } + if (solution_ptr == NULL) + break; + /* + * Add secondary master species + */ + + + xsolution_zero(); + + // adds primary master species + add_solution(solution_ptr, 1.0, 1.0); + + // adds secondary master species + cxxNameDouble::iterator jit = solution_ptr->Get_totals().begin(); + for ( ; jit != solution_ptr->Get_totals().end(); jit++) + { + struct master *master_ptr = master_bsearch(jit->first.c_str()); + master_ptr->total = jit->second; + } + + // Fill table + master_to_tally_table(tally_table[i].total[n_buffer]); + + // Add alkalinity + tally_table[i].total[n_buffer][0].moles = solution_ptr->Get_total_alkalinity(); + + // Add total_h + tally_table[i].total[n_buffer][1].moles = solution_ptr->Get_total_h(); + + // Add total_o + tally_table[i].total[n_buffer][2].moles = solution_ptr->Get_total_o(); + } + break; +#ifdef SKIP + case Solution: +/* + * fill solution + */ + if (n_user[Solution] < 0 || n_buffer == 0) + break; + { + cxxSolution *solution_ptr; + if (i == 0) + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, index_conservative); + } + else if (i == 1) + { + solution_ptr = Utilities::Rxn_find(Rxn_solution_map, n_user[Solution]); + } + else + { + solution_ptr = NULL; + error_msg + ("Solution is not in first two columns of tally_table", + STOP); + } + if (solution_ptr == NULL) + break; + xsolution_zero(); + add_solution(solution_ptr, 1.0, 1.0); + count_elts = 0; + paren_count = 0; + for (int j = 0; j < count_master; j++) + { + if (master[j]->total > 0.0) + { + char * temp_name = string_duplicate(master[j]->elt->primary->elt->name); + ptr = temp_name; + get_elts_in_species(&ptr, master[j]->total); + free_check_null(temp_name); + } + } + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + elt_list_to_tally_table(tally_table[i].total[n_buffer]); + } + break; +#endif + case Reaction: + /* + * fill reaction + */ + if (n_user[Reaction] < 0) + break; + { + cxxReaction *reaction_ptr = Utilities::Rxn_find(Rxn_reaction_map, n_user[Reaction]); + if (reaction_ptr == NULL) + break; + count_elts = 0; + paren_count = 0; + if (n_buffer == 1) + { + moles = reaction_ptr->Get_steps()[0]; + } + else + { + moles = 0.0; + } + reaction_calc(reaction_ptr); + add_elt_list(reaction_ptr->Get_elementList(), moles); + elt_list_to_tally_table(tally_table[i].total[n_buffer]); + } + break; + case Pure_phase: + /* + * fill an equilibrium phase + */ + if (n_user[Pure_phase] < 0) + break; + { + cxxPPassemblage * pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, n_user[Pure_phase]); + if (pp_assemblage_ptr == NULL) + break; + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + if (string_hsave(it->second.Get_name().c_str()) == + tally_table[i].name) + break; + if (strcmp_nocase(it->second.Get_name().c_str(), + tally_table[i].name) == 0) + break; + } + if (it == pp_assemblage_ptr->Get_pp_assemblage_comps().end()) + break; + count_elts = 0; + paren_count = 0; + moles = it->second.Get_moles(); + tally_table[i].moles = moles; + add_elt_list(tally_table[i].formula, moles); + elt_list_to_tally_table(tally_table[i].total[n_buffer]); + } + break; + case Exchange: + { + /* + * fill exchange + */ + if (n_user[Exchange] < 0) + break; + cxxExchange * exchange_ptr = Utilities::Rxn_find(Rxn_exchange_map, n_user[Exchange]); + if (exchange_ptr == NULL) + break; + count_elts = 0; + paren_count = 0; + for (size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++) + { + add_elt_list(exchange_ptr->Get_exchange_comps()[j].Get_totals(), 1.0); + } + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + elt_list_to_tally_table(tally_table[i].total[n_buffer]); + } + break; + case Surface: + /* + * fill surface + */ + if (n_user[Surface] < 0) + break; + { + cxxSurface * surface_ptr = Utilities::Rxn_find(Rxn_surface_map, n_user[Surface]); + if (surface_ptr == NULL) + break; + count_elts = 0; + paren_count = 0; + for (size_t j = 0; j < surface_ptr->Get_surface_comps().size(); j++) + { + add_elt_list(surface_ptr->Get_surface_comps()[j].Get_totals(), 1.0); + } + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + elt_list_to_tally_table(tally_table[i].total[n_buffer]); + } + break; + case Ss_phase: + if (n_user[Ss_phase] < 0) + break; + { + /* + * fill an solid solution phase + */ + + cxxSSassemblage * ss_assemblage_ptr = Utilities::Rxn_find(Rxn_ss_assemblage_map, n_user[Ss_phase]); + if (ss_assemblage_ptr == NULL) + break; + found = FALSE; + moles = 0.0; + std::vector ss_ptrs = ss_assemblage_ptr->Vectorize(); + for (size_t j = 0; j < ss_ptrs.size(); j++) + { + cxxSS * ss_ptr = ss_ptrs[j]; + cxxSScomp * comp_ptr = NULL; + size_t k; + for (k = 0; k < ss_ptr->Get_ss_comps().size(); k++) + { + comp_ptr = &(ss_ptr->Get_ss_comps()[k]); + int l; + struct phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + if (phase_ptr->name == tally_table[i].name) + break; + if (strcmp_nocase(phase_ptr->name, tally_table[i].name) == 0) + break; + } + if (k < ss_ptr->Get_ss_comps().size() && comp_ptr) + { + moles = comp_ptr->Get_moles(); + found = TRUE; + break; + } + } + if (found == FALSE) + break; + count_elts = 0; + paren_count = 0; + tally_table[i].moles = moles; + add_elt_list(tally_table[i].formula, moles); + elt_list_to_tally_table(tally_table[i].total[n_buffer]); + } + break; + case Gas_phase: + /* + * fill in gas phase + */ + if (n_user[Gas_phase] < 0) + break; + { + cxxGasPhase * gas_phase_ptr = Utilities::Rxn_find(Rxn_gas_phase_map, n_user[Gas_phase]); + if (gas_phase_ptr == NULL) + break; + count_elts = 0; + paren_count = 0; + const std::vector *gc = &(gas_phase_ptr->Get_gas_comps()); + for (size_t l = 0; l < gc->size(); l++) + { + int k; + struct phase *phase_ptr = phase_bsearch((*gc)[l].Get_phase_name().c_str(), &k, FALSE); + + add_elt_list(phase_ptr->next_elt, (*gc)[l].Get_moles()); + } + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + elt_list_to_tally_table(tally_table[i].total[n_buffer]); + break; + } + case Kinetics: + { + /* + * fill in kinetics + */ + if (n_user[Kinetics] < 0) + break; + cxxKinetics *kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, n_user[Kinetics]); + if (kinetics_ptr == NULL) + break; + cxxKineticsComp * kinetics_comp_ptr = NULL; + size_t j; + for (j = 0; j < kinetics_ptr->Get_kinetics_comps().size(); j++) + { + kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + if (string_hsave(kinetics_comp_ptr->Get_rate_name().c_str()) == tally_table[i].name) + break; + if (strcmp_nocase + (kinetics_comp_ptr->Get_rate_name().c_str(), tally_table[i].name) == 0) + break; + } + if (j >= kinetics_ptr->Get_kinetics_comps().size()) + break; + moles = 0.0; + if (kinetics_comp_ptr) + { + moles = kinetics_comp_ptr->Get_m(); + } + tally_table[i].moles = moles; + count_elts = 0; + paren_count = 0; + add_elt_list(tally_table[i].formula, moles); + elt_list_to_tally_table(tally_table[i].total[n_buffer]); + } + break; + case Mix: + break; + case Temperature: + case Pressure: + break; + case UnKnown: + break; + } + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +elt_list_to_tally_table(struct tally_buffer *buffer_ptr) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + for (i = 0; i < count_tally_table_rows; i++) + { + buffer_ptr[i].moles = 0.0; + } + /* + * copy element list amounts to buffer in tally table + * for column number + */ + + for (j = 0; j < count_elts; j++) + { + if (elt_list[j].elt->primary->s == s_h2o) + continue; + if (elt_list[j].elt->primary->s == s_hplus) + continue; + if (elt_list[j].elt->primary->s == s_h3oplus) + continue; + if (elt_list[j].elt->primary->type != AQ) + continue; + for (i = 0; i < count_tally_table_rows; i++) + { + if (buffer_ptr[i].master != NULL) + { + if (elt_list[j].elt->primary == + buffer_ptr[i].master->elt->primary) + { + buffer_ptr[i].moles = elt_list[j].coef; + break; + } + } + } + if (i >= count_tally_table_rows) + { + error_msg("Should not be here in elt_list_to_tally_table", STOP); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +master_to_tally_table(struct tally_buffer *buffer_ptr) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + for (i = 0; i < count_tally_table_rows; i++) + { + buffer_ptr[i].moles = 0.0; + } + /* + * copy element list amounts to buffer in tally table + * for column number + */ + for (j = 0; j < count_master; j++) + { + if (master[j]->total <= 0) + continue; + if (master[j]->elt->primary->s == s_h2o) + continue; + if (master[j]->elt->primary->s == s_hplus) + continue; + if (master[j]->elt->primary->s == s_h3oplus) + continue; + if (master[j]->elt->primary->type != AQ) + continue; + for (i = 0; i < count_tally_table_rows; i++) + { + if (master[j] == buffer_ptr[i].master) + { + buffer_ptr[i].moles = master[j]->total; + break; + } + } + if (i >= count_tally_table_rows) + { + error_msg("Should not be here in master_to_tally_table", STOP); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +build_tally_table(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Routine accumulates elements from all solutions, phases, gas phases, + * exchangers, and surfaces. Counts number of aqueous components + * to transport. Stores in global variable tally_count_component. + * Also calculates a number greater than all user numbers and + * stores in global variable first_user_number. + */ + int j, k, l, n, p, save_print_use; + int count_tt_pure_phase, count_tt_ss_phase, count_tt_kinetics; + struct phase *phase_ptr; + char token[MAX_LENGTH]; + char *ptr; +/* + * make list of all elements in all entitites + * defines the number of rows in the table + */ + get_all_components(); + + save_print_use = pr.use; + pr.use = FALSE; +/* + * find nuber of columns + */ + count_tally_table_columns = 0; +/* + * add one for conservative mixing + */ + n = count_tally_table_columns; + extend_tally_table(); + tally_table[n].name = string_hsave("Solution_conservative"); + tally_table[n].type = Solution; +/* + * add one for mixing plus reaction + */ + n = count_tally_table_columns; + extend_tally_table(); + tally_table[n].name = string_hsave("Solution_reaction"); + tally_table[n].type = Solution; +/* + * add one for reactions + */ + if (Rxn_reaction_map.size() > 0) + { + n = count_tally_table_columns; + extend_tally_table(); + tally_table[n].name = string_hsave("Reaction"); + tally_table[n].type = Reaction; + } +/* + * add one for exchangers + */ + if (Rxn_exchange_map.size() > 0) + { + n = count_tally_table_columns; + extend_tally_table(); + tally_table[n].name = string_hsave("Exchange"); + tally_table[n].type = Exchange; + } +/* + * add one for surface + */ + if (Rxn_surface_map.size() > 0) + { + n = count_tally_table_columns; + extend_tally_table(); + tally_table[n].name = string_hsave("Surface"); + tally_table[n].type = Surface; + } +/* + * add one for gases + */ + if (Rxn_gas_phase_map.size() > 0) + { + n = count_tally_table_columns; + extend_tally_table(); + tally_table[n].name = string_hsave("Gas_phase"); + tally_table[n].type = Gas_phase; + } +/* + * Count pure phases + */ + count_tt_pure_phase = 0; + if (Rxn_pp_assemblage_map.size() > 0) + { + /* + * Go through all pure phases in pure phase assemblages + */ + std::map::iterator it; + for (it = Rxn_pp_assemblage_map.begin(); it != Rxn_pp_assemblage_map.end(); it++) + { + cxxPPassemblage * pp_assemblage_ptr = &(it->second); + std::map::iterator jit; + jit = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; jit != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); jit++) + { + cxxPPassemblageComp * comp_ptr = &(jit->second); + int l; + struct phase * phase_ptr = phase_bsearch(jit->first.c_str(), &l, FALSE); + /* + * check if already in tally_table + */ + for (k = 1; k < count_tally_table_columns; k++) + { + if (tally_table[k].type == Pure_phase && + tally_table[k].name == phase_ptr->name && + tally_table[k].add_formula == + string_hsave(comp_ptr->Get_add_formula().c_str())) + break; + } + if (k < count_tally_table_columns) + continue; + /* + * Add to table + */ + count_tt_pure_phase++; + n = count_tally_table_columns; + extend_tally_table(); + tally_table[n].name = phase_ptr->name; + tally_table[n].type = Pure_phase; + tally_table[n].add_formula = string_hsave(comp_ptr->Get_add_formula().c_str()); + count_elts = 0; + paren_count = 0; + if (comp_ptr->Get_add_formula().size() > 0) + { + strcpy(token, comp_ptr->Get_add_formula().c_str()); + ptr = &(token[0]); + get_elts_in_species(&ptr, 1.0); + } + else + { + strcpy(token, phase_ptr->formula); + add_elt_list(phase_ptr->next_elt, 1.0); + } + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + tally_table[n].formula = elt_list_save(); + } + } + } +/* + * Add solid-solution pure phases + */ + count_tt_ss_phase = 0; + if (Rxn_ss_assemblage_map.size() > 0) + { + /* + * Go through all components of all solid solutions in solid-solution assemblages + */ + std::map::iterator it; + for (it = Rxn_ss_assemblage_map.begin(); it != Rxn_ss_assemblage_map.end(); it++) + { + cxxSSassemblage *ss_assemblage_ptr = &(it->second); + std::vector ss_ptrs = ss_assemblage_ptr->Vectorize(); + for (j = 0; j < (int) ss_ptrs.size(); j++) + { + cxxSS * ss_ptr = ss_ptrs[j]; + for (k = 0; k < (int) ss_ptr->Get_ss_comps().size(); k++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[k]); + int l; + struct phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE); + /* + * check if already in tally_table + */ + for (l = 1; l < count_tally_table_columns; l++) + { + if (tally_table[l].type == Ss_phase && + tally_table[l].name == phase_ptr->name) + break; + } + if (l < count_tally_table_columns) + continue; + /* + * Add to table + */ + count_tt_ss_phase++; + n = count_tally_table_columns; + extend_tally_table(); + tally_table[n].name = phase_ptr->name; + tally_table[n].type = Ss_phase; + count_elts = 0; + paren_count = 0; + strcpy(token, phase_ptr->formula); + add_elt_list(phase_ptr->next_elt, 1.0); + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + tally_table[n].formula = elt_list_save(); + } + } + } + } +/* + * Add kinetic reactants + */ + count_tt_kinetics = 0; + if (Rxn_kinetics_map.size() > 0) + { + std::map::iterator it; + for (it = Rxn_kinetics_map.begin(); it != Rxn_kinetics_map.end(); it++) + { + cxxKinetics *kinetics_ptr = &(it->second); + for (j = 0; j < (int) kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp *kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + /* + * check if already in tally_table + */ + for (l = 1; l < count_tally_table_columns; l++) + { + if (tally_table[l].type == Kinetics && + tally_table[l].name == string_hsave(kinetics_comp_ptr->Get_rate_name().c_str())) + break; + } + if (l < count_tally_table_columns) + continue; + /* + * Add to table + */ + count_tt_kinetics++; + n = count_tally_table_columns; + extend_tally_table(); + tally_table[n].name = string_hsave(kinetics_comp_ptr->Get_rate_name().c_str()); + tally_table[n].type = Kinetics; + /* + * get formula for kinetic component + */ + count_elts = 0; + paren_count = 0; + phase_ptr = NULL; + if (kinetics_comp_ptr->Get_namecoef().size() == 1) + { + strcpy(token, kinetics_comp_ptr->Get_namecoef().begin()->first.c_str()); + phase_ptr = phase_bsearch(token, &p, FALSE); + } + if (phase_ptr != NULL) + { + add_elt_list(phase_ptr->next_elt, 1.0); + } + else + { + cxxNameDouble::iterator it = kinetics_comp_ptr->Get_namecoef().begin(); + for ( ; it != kinetics_comp_ptr->Get_namecoef().end(); it++) + { + std::string name = it->first; + LDBLE coef = it->second; + char * temp_name = string_duplicate(name.c_str()); + ptr = temp_name; + get_elts_in_species(&ptr, 1.0 * coef); + free_check_null(temp_name); + } + } + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + tally_table[n].formula = elt_list_save(); + } + } + } + +#ifdef SKIP + /* + * Debug print for table definition + */ + output_msg(sformatf( "List of rows for tally table\n")); + for (i = 0; i < count_tally_table_rows; i++) + { + output_msg(sformatf( "\t%-s\n", buffer[i].name)); + } + output_msg(sformatf( "\nList of columns for tally table\n")); + for (i = 0; i < count_tally_table_columns; i++) + { + output_msg(sformatf( "\t%-20s\tType: %d\n", + tally_table[i].name, tally_table[i].type)); + if (tally_table[i].formula != NULL) + { + for (j = 0; tally_table[i].formula[j].elt != NULL; j++) + { + output_msg(sformatf( "\t\t%-10s\t%f\n", + tally_table[i].formula[j].elt->name, + (double) tally_table[i].formula[j].coef)); + } + } + } +#endif + pr.use = save_print_use; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +add_all_components_tally(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Routine accumulates elements from all solutions, phases, gas phases, + * exchangers, and surfaces. Counts number of aqueous components + * to transport. Stores in global variable tally_count_component. + * Also calculates a number greater than all user numbers and + * stores in global variable first_user_number. + */ + int save_print_use; + + save_print_use = pr.use; + pr.use = FALSE; +/* + * Delete solutions less than -1 + */ + +/* + * add all solutions + */ + xsolution_zero(); + { + std::map::iterator it = Rxn_solution_map.begin(); + for (; it != Rxn_solution_map.end(); it++) + { + add_solution(&it->second, 1.0 / it->second.Get_mass_water(), 1.0); + } + } +/* + * add all irrev reactions + */ + { + std::map::iterator it = Rxn_reaction_map.begin(); + for (; it != Rxn_reaction_map.end(); it++) + { + add_reaction(&it->second, 1, 1.0); + } + } +/* + * Add pure phases + */ + { + std::map::iterator it = Rxn_pp_assemblage_map.begin(); + for (; it != Rxn_pp_assemblage_map.end(); it++) + { + add_pp_assemblage(&(it->second)); + } + } +/* + * Exchangers + */ + { + std::map::iterator it = Rxn_exchange_map.begin(); + for (; it != Rxn_exchange_map.end(); it++) + { + add_exchange(&it->second); + } + } +/* + * Surfaces + */ + { + std::map::iterator it = Rxn_surface_map.begin(); + for (; it != Rxn_surface_map.end(); it++) + { + add_surface(&it->second); + } + } +/* + * Gases + */ + { + std::map::iterator it = Rxn_gas_phase_map.begin(); + for ( ; it != Rxn_gas_phase_map.end(); it++) + { + add_gas_phase(&it->second); + } + } +/* + * Add solid-solution pure phases + */ + { + std::map::iterator it; + for (it = Rxn_ss_assemblage_map.begin(); it != Rxn_ss_assemblage_map.end(); it++) + { + add_ss_assemblage(&(it->second)); + } + } +/* + * Add elements in kinetic reactions + */ + { + std::map::iterator it = Rxn_kinetics_map.begin(); + for ( ; it != Rxn_kinetics_map.end(); it++) + { + calc_dummy_kinetic_reaction_tally(&(it->second)); + add_kinetics(&(it->second)); + } + } +/* + * reset pr.use + */ + pr.use = save_print_use; + return; +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +calc_dummy_kinetic_reaction_tally(cxxKinetics *kinetics_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Go through kinetic components and add positive amount of each reactant + */ + LDBLE coef; + char *ptr; + struct phase *phase_ptr; +/* + * Go through list and generate list of elements and + * coefficient of elements in reaction + */ + kinetics_ptr->Get_totals().clear(); + count_elts = 0; + paren_count = 0; + for (size_t i = 0; i < kinetics_ptr->Get_kinetics_comps().size(); i++) + { + cxxKineticsComp * kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[i]); + coef = 1.0; +/* + * Reactant is a pure phase, copy formula into token + */ + phase_ptr = NULL; + if (kinetics_comp_ptr->Get_namecoef().size() == 1) + { + std::string name = kinetics_comp_ptr->Get_namecoef().begin()->first; + int j; + phase_ptr = phase_bsearch(name.c_str(), &j, FALSE); + } + if (phase_ptr != NULL) + { + add_elt_list(phase_ptr->next_elt, coef); + } + else + { + cxxNameDouble::iterator it = kinetics_comp_ptr->Get_namecoef().begin(); + for ( ; it != kinetics_comp_ptr->Get_namecoef().end(); it++) + { + std::string name = it->first; + char * temp_name = string_duplicate(name.c_str()); + ptr = temp_name; + get_elts_in_species(&ptr, coef); + free_check_null(temp_name); + } + } + } + kinetics_ptr->Set_totals(elt_list_NameDouble()); + + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +extend_tally_table(void) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + /* + * adds another column to tally_table + * increments number of columns + */ + tally_table = + (struct tally *) PHRQ_realloc((void *) tally_table, + (size_t) (count_tally_table_columns + + 1) * sizeof(struct tally)); + if (tally_table == NULL) + malloc_error(); + for (i = 0; i < 3; i++) + { + tally_table[count_tally_table_columns].total[i] = + (struct tally_buffer *) + PHRQ_malloc((size_t) (count_tally_table_rows) * + sizeof(struct tally_buffer)); + if (tally_table[count_tally_table_columns].total[i] == NULL) + malloc_error(); + for (j = 0; j < count_tally_table_rows; j++) + { + tally_table[count_tally_table_columns].total[i][j].name = + t_buffer[j].name; + tally_table[count_tally_table_columns].total[i][j].master = + t_buffer[j].master; + } + } + tally_table[count_tally_table_columns].name = NULL; + tally_table[count_tally_table_columns].type = UnKnown; + tally_table[count_tally_table_columns].add_formula = NULL; + tally_table[count_tally_table_columns].moles = 0.0; + tally_table[count_tally_table_columns].formula = NULL; + count_tally_table_columns++; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_reaction_moles(int n_user, LDBLE moles) +/* ---------------------------------------------------------------------- */ +{ + cxxReaction *reaction_ptr = Utilities::Rxn_find(Rxn_reaction_map, n_user); + if (reaction_ptr == NULL) + return (ERROR); + std::vector v; + v.push_back(moles); + reaction_ptr->Set_steps(v); + reaction_ptr->Set_countSteps(1); + reaction_ptr->Set_equalIncrements(true); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_reaction_temperature(int n_user, LDBLE tc) +/* ---------------------------------------------------------------------- */ +{ + cxxTemperature * temperature_ptr = Utilities::Rxn_find(Rxn_temperature_map, n_user); + if (temperature_ptr == NULL) + return (ERROR); + temperature_ptr->Get_temps().clear(); + temperature_ptr->Get_temps().push_back(tc); + temperature_ptr->Set_equalIncrements(false); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_kinetics_time(int n_user, LDBLE step) +/* ---------------------------------------------------------------------- */ +{ + cxxKinetics *kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, n_user); + + if (kinetics_ptr == NULL) + return (ERROR); + kinetics_ptr->Get_steps().clear(); + kinetics_ptr->Get_steps().push_back(step); + kinetics_ptr->Set_equalIncrements(false); + return (OK); +} + diff --git a/phreeqcpp/test.xml b/phreeqcpp/test.xml new file mode 100644 index 00000000..ed476808 --- /dev/null +++ b/phreeqcpp/test.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + diff --git a/phreeqcpp/tidy.cpp b/phreeqcpp/tidy.cpp new file mode 100644 index 00000000..2199e67c --- /dev/null +++ b/phreeqcpp/tidy.cpp @@ -0,0 +1,5540 @@ +#include "Utils.h" +#include "Phreeqc.h" +#include "phqalloc.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "cxxKinetics.h" +#include "Solution.h" + +#define ZERO_TOL 1.0e-30 + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_model(void) +/* ---------------------------------------------------------------------- */ +{ + int n_user, last; + int new_named_logk; + /* + * Determine if any new elements, species, phases have been read + */ + state = INITIALIZE; + new_model = FALSE; + new_pp_assemblage = FALSE; + new_surface = FALSE; + new_exchange = FALSE; + new_reaction = FALSE; + new_temperature = FALSE; + new_mix = FALSE; + new_solution = FALSE; + new_gas_phase = FALSE; + new_inverse = FALSE; + new_punch = FALSE; + new_surface = FALSE; + new_ss_assemblage = FALSE; + new_kinetics = FALSE; + new_pitzer = FALSE; + new_named_logk = FALSE; + + if (keycount[Keywords::KEY_SOLUTION_SPECIES] > 0 || /*"species" */ + keycount[Keywords::KEY_SOLUTION_MASTER_SPECIES] > 0 || /*"master" */ + keycount[Keywords::KEY_PHASES] > 0 || /*"phases" */ + keycount[Keywords::KEY_EXCHANGE_SPECIES] > 0 || /*"exchange_species" */ + keycount[Keywords::KEY_EXCHANGE_MASTER_SPECIES] > 0 || /*"master_exchange_species" */ + keycount[Keywords::KEY_SURFACE_SPECIES] > 0 || /*"surface_species" */ + keycount[Keywords::KEY_SURFACE_MASTER_SPECIES] > 0 || /*"master_surface_species" */ + keycount[Keywords::KEY_RATES] > 0 || /*"rates" */ + keycount[Keywords::KEY_LLNL_AQUEOUS_MODEL_PARAMETERS] > 0 || /*"llnl_aqueous_model_parameters" */ + (keycount[Keywords::KEY_DATABASE] > 0 && simulation == 0) || /*"database" */ + keycount[Keywords::KEY_NAMED_EXPRESSIONS] > 0 || /*"named_analytical_expressions" */ + keycount[Keywords::KEY_ISOTOPES] > 0 || /*"isotopes" */ + keycount[Keywords::KEY_CALCULATE_VALUES] > 0 || /*"calculate_values" */ + keycount[Keywords::KEY_ISOTOPE_RATIOS] > 0 || /*"isotopes_ratios", */ + keycount[Keywords::KEY_ISOTOPE_ALPHAS] > 0 || /*"isotopes_alphas" */ + keycount[Keywords::KEY_PITZER] > 0 || /*"pitzer" */ + keycount[Keywords::KEY_SIT] > 0 /*"sit" */ + ) + { + new_model = TRUE; + } + if (keycount[Keywords::KEY_EQUILIBRIUM_PHASES] > 0 || + keycount[Keywords::KEY_EQUILIBRIUM_PHASES_RAW] > 0 || + keycount[Keywords::KEY_EQUILIBRIUM_PHASES_MODIFY]) + { + new_pp_assemblage = TRUE; /*"pure_phases" */ + } + if (keycount[Keywords::KEY_SURFACE] > 0 || + keycount[Keywords::KEY_SURFACE_RAW] > 0 || + keycount[Keywords::KEY_SURFACE_MODIFY]) + { + new_surface = TRUE; /*"surface" */ + } + if (keycount[Keywords::KEY_EXCHANGE] > 0 || + keycount[Keywords::KEY_EXCHANGE_RAW] > 0 || + keycount[Keywords::KEY_EXCHANGE_MODIFY]) + { + new_exchange = TRUE; /*"exchange" */ + } + if (keycount[Keywords::KEY_REACTION] > 0 /*|| + keycount[Keywords::KEY_REACTION_RAW] > 0 || + keycount[Keywords::KEY_REACTION_MODIFY]*/) + { + new_reaction = TRUE; /*"reaction" */ + } + if (keycount[Keywords::KEY_REACTION_TEMPERATURE] > 0 /*|| + keycount[Keywords::KEY_REACTION_TEMPERATURE_RAW] > 0 || + keycount[Keywords::KEY_REACTION_TEMPERATURE_MODIFY]*/) + { + new_temperature = TRUE; /*"reacton_temperature" */ + } + if (keycount[Keywords::KEY_MIX] > 0 || + keycount[Keywords::KEY_MIX_RAW] > 0) + { + new_mix = TRUE; /*"mix" */ + } + if (keycount[Keywords::KEY_SOLUTION] > 0 || + keycount[Keywords::KEY_SOLUTION_SPREAD] > 0 || + keycount[Keywords::KEY_SOLUTION_RAW] > 0 || + keycount[Keywords::KEY_SOLUTION_MODIFY]) + { /*"solution" */ + new_solution = TRUE; + } + if (keycount[Keywords::KEY_GAS_PHASE] > 0 || + keycount[Keywords::KEY_GAS_PHASE_RAW] > 0 || + keycount[Keywords::KEY_GAS_PHASE_MODIFY]) + { + new_gas_phase = TRUE; /*"gas_phase" */ + } + if (keycount[Keywords::KEY_SOLID_SOLUTIONS] > 0 || + keycount[Keywords::KEY_SOLID_SOLUTIONS_RAW] > 0 || + keycount[Keywords::KEY_SOLID_SOLUTIONS_MODIFY]) + { + new_ss_assemblage = TRUE; /*"solid_solutions" */ + } + if (keycount[Keywords::KEY_KINETICS] > 0 /*|| + keycount[Keywords::KEY_KINETICS_RAW] > 0 || + keycount[Keywords::KEY_KINETICS_MODIFY]*/) + { + new_kinetics = TRUE; /*"kinetics" */ + } + if (keycount[Keywords::KEY_INVERSE_MODELING] > 0) + { + new_inverse = TRUE; /*"inverse_modeling" */ + } + if (keycount[Keywords::KEY_SELECTED_OUTPUT] > 0 || /*"selected_output" */ + keycount[Keywords::KEY_USER_PUNCH] > 0) /*"user_punch" */ + { + new_punch = TRUE; + } + + if (keycount[Keywords::KEY_COPY] > 0) + { + new_copy = TRUE; /*"copy" */ + } + if (keycount[Keywords::KEY_PITZER] > 0) + { + new_pitzer = TRUE; /*"pitzer" */ + } + if (keycount[Keywords::KEY_NAMED_EXPRESSIONS] > 0) + { + new_named_logk = TRUE; /*"named_log_k" */ + } + +/* + * Sort arrays + */ + +/* species */ + if (new_model == TRUE) + { + qsort(s, (size_t) count_s, (size_t) sizeof(struct species *), s_compare); + +/* master species */ + qsort(master, (unsigned) count_master, sizeof(struct master *), master_compare); + +/* elements */ + qsort(elements, (size_t) count_elements, (size_t) sizeof(struct element *), element_compare); +/* phases */ + qsort(phases, (size_t) count_phases, (size_t) sizeof(struct phase *), phase_compare); + + } + +/* named_log_k */ + if (new_named_logk) + { + tidy_logk(); + } +/* + * Check pointers, write reactions for species + */ + if (new_model) + { + sum_species_map.clear(); + + tidy_species(); + + tidy_phases(); + + tidy_master_isotope(); +/* + * calculate gfw of water, kg/mole + */ + compute_gfw("H2O", &gfw_water); + gfw_water *= 0.001; + } +/* + * tidy surface data + */ + if (new_model || new_surface) + { + tidy_surface(); + } +/* + * tidy inverse data + */ + if (new_inverse) + { + tidy_inverse(); + } +/* + * tidy gas phase data + */ + if (new_gas_phase) + { + tidy_gas_phase(); + } +/* + * tidy pp_assemblage data + */ + if (new_model || new_pp_assemblage) + { + tidy_pp_assemblage(); + } +/* + * tidy ss_assemblage data + */ + if (new_model || new_ss_assemblage) + { + tidy_ss_assemblage(); + } +/* + * tidy exchange data, after pp_assemblages + */ + if (new_exchange) + { + tidy_exchange(); + tidy_min_exchange(); + tidy_kin_exchange(); + } +/* + * tidy surface data + */ + if (new_surface) + { + tidy_min_surface(); + tidy_kin_surface(); + } +/* + * tidy solution isotope data + */ + if (new_solution) + { + tidy_isotopes(); + } + if (new_model) + { + tidy_isotope_ratios(); + tidy_isotope_alphas(); + } +/* + * Duplicate kinetics + */ + if (new_kinetics) + { + std::map::iterator it; + for (it = Rxn_kinetics_map.begin(); it != Rxn_kinetics_map.end(); it++) + { + n_user = it->second.Get_n_user(); + last = it->second.Get_n_user_end(); + it->second.Set_n_user_end(n_user); + Utilities::Rxn_copies(Rxn_kinetics_map, n_user, last); + } + } + +/* + * Tidy pitzer information + */ + if (pitzer_model && new_model) + { + pitzer_tidy(); + } +/* + * Tidy SIT information + */ + if (sit_model && new_model) + { + sit_tidy(); + } +/* + * Tidy punch information + */ + if (get_input_errors() == 0 && (new_punch || new_model)) + { + tidy_punch(); + } +/* + * Tidy solution information + */ + if (new_solution) + { + tidy_solutions(); + } + + /* if (new_model || new_exchange || new_pp_assemblage || new_surface || new_gas_phase || new_kinetics) reset_last_model(); */ + if (new_model) + { + reset_last_model(); + } +/* + * make sure essential species are defined + */ + //if (new_model) + { + if (s_h2o == NULL) + { + input_error++; + //error_msg("H2O not defined.", STOP); + error_msg("H2O not defined.", CONTINUE); + } + else + { + if (s_h2o->primary == NULL) + { + input_error++; + error_msg("H2O, primary master species for O, not defined.", + CONTINUE); + } + if (s_h2o->secondary == NULL) + { + input_error++; + error_msg("H2O, secondary master species for O(-2), not defined.", + CONTINUE); + } + if (s_h2o->type != H2O) + { + input_error++; + error_msg("H2O can only be defined in SOLUTION_SPECIES.", + CONTINUE); + } + } + if (s_hplus == NULL && s_h3oplus == NULL) + { + input_error++; + error_msg("Neither H+ nor H3O+ are defined in solution_species.", + STOP); + } + else if (s_hplus == NULL && s_h3oplus != NULL) + { + s_hplus = s_h3oplus; + s_h3oplus = NULL; + } + else if (s_hplus != NULL && s_h3oplus == NULL) + { + } + else if (s_hplus != NULL && s_h3oplus != NULL) + { + input_error++; + error_msg("Cannot define both H+ and H3O+ in solution_species.", + STOP); + } + if (s_hplus->primary == NULL) + { + input_error++; + error_msg("H3O+, primary master species for H, not defined.", + CONTINUE); + } + if (s_hplus->secondary == NULL) + { + input_error++; + error_msg("H3O+, secondary master species for H(1), not defined.", + CONTINUE); + } + if (s_eminus == NULL) + { + input_error++; + error_msg("e- not defined in solution_species.", CONTINUE); + } + if (s_eminus->primary == NULL) + { + input_error++; + error_msg("e-, primary master species for E-, not defined.", + CONTINUE); + } + if (pitzer_model == FALSE || pitzer_pe == TRUE) + { + if (s_h2 == NULL) + { + input_error++; + error_msg("H2(aq) not defined in solution_species.", CONTINUE); + } + if (s_o2 == NULL) + { + input_error++; + error_msg("O2(aq) not defined in solution_species.", CONTINUE); + } + } + element_h_one = element_store("H(1)"); + if (element_h_one == NULL) + { + input_error++; + error_msg("H(1) not defined in solution_master_species.", CONTINUE); + } + } +/* + * Error check, program termination + */ + if (get_input_errors() > 0 || parse_error > 0) + { + error_msg("Calculations terminating due to input errors.", STOP); + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_species_input(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Check species data for completeness + */ + int i; + int return_value; + + return_value = OK; + for (i = 0; i < count_s; i++) + { + if (s[i]->next_elt == NULL) + { + input_error++; + return_value = ERROR; + error_string = sformatf( + "Elements in species have not been tabulated, %s.", + s[i]->name); + error_msg(error_string, CONTINUE); + } + if (s[i]->rxn == NULL) + { + input_error++; + return_value = ERROR; + error_string = sformatf( + "Reaction for species has not been defined, %s.", + s[i]->name); + error_msg(error_string, CONTINUE); + } + else + { + select_log_k_expression(s[i]->logk, s[i]->rxn->logk); + add_other_logk(s[i]->rxn->logk, s[i]->count_add_logk, + s[i]->add_logk); + } + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +select_log_k_expression(LDBLE * source_k, LDBLE * target_k) +/* ---------------------------------------------------------------------- */ +{ + int j, analytic; + + analytic = FALSE; + for (j = T_A1; j <= T_A6; j++) + { + if (source_k[j] != 0.0) + { + analytic = TRUE; + break; + } + } + if (analytic == TRUE) + { + target_k[logK_T0] = 0.0; + target_k[delta_h] = 0.0; + for (j = T_A1; j <= T_A6; j++) + { + target_k[j] = source_k[j]; + } + } + else + { + target_k[logK_T0] = source_k[logK_T0]; + target_k[delta_h] = source_k[delta_h]; + for (j = T_A1; j <= T_A6; j++) + { + target_k[j] = 0.0; + } + } + for (j = delta_v; j < MAX_LOG_K_INDICES; j++) + { + target_k[j] = source_k[j]; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_logk(void) +/* ---------------------------------------------------------------------- */ +/* + * Picks log k expression + */ +{ + int i; + for (i = 0; i < count_logk; i++) + { + select_log_k_expression(logk[i]->log_k_original, logk[i]->log_k); + logk[i]->done = FALSE; + } + for (i = 0; i < count_logk; i++) + { + if (logk[i]->done == FALSE) + { + add_logks(logk[i], 0); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_other_logk(LDBLE * source_k, int count_add_logk, + struct name_coef *add_logk) +/* ---------------------------------------------------------------------- */ +{ + int i, j, analytic; + struct logk *logk_ptr; + char token[MAX_LENGTH]; + LDBLE coef; + ENTRY item, *found_item; + + if (count_add_logk == 0) + return (OK); + for (i = 0; i < count_add_logk; i++) + { + coef = add_logk[i].coef; + strcpy(token, add_logk[i].name); + str_tolower(token); + item.key = token; + item.data = NULL; + found_item = hsearch_multi(logk_hash_table, item, FIND); + if (found_item == NULL) + { + input_error++; + error_string = sformatf( + "Could not find named temperature expression, %s\n", + token); + error_msg(error_string, CONTINUE); + return (ERROR); + } + logk_ptr = (struct logk *) found_item->data; + analytic = FALSE; + for (j = T_A1; j <= T_A6; j++) + { + if (logk_ptr->log_k[j] != 0.0) + { + analytic = TRUE; + break; + } + } + if (analytic == TRUE) + { + for (j = T_A1; j <= T_A6; j++) + { + source_k[j] += logk_ptr->log_k[j] * coef; + } + } + else + { + source_k[logK_T0] += logk_ptr->log_k[logK_T0] * coef; + source_k[delta_h] += logk_ptr->log_k[delta_h] * coef; + } + for (j = delta_v; j < MAX_LOG_K_INDICES; j++) + { + source_k[j] += logk_ptr->log_k[j] * coef; + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_logks(struct logk *logk_ptr, int repeats) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + struct logk *next_logk_ptr; + char token[MAX_LENGTH]; + LDBLE coef; + ENTRY item, *found_item; + /* + * Adds in other named_expressions to get complete log K + * Evaluates others recursively if necessary + */ + if (repeats > 15) + { + input_error++; + error_string = sformatf( "Circular definition of named_logk? %s\n", + logk_ptr->name); + error_msg(error_string, CONTINUE); + return (ERROR); + } + for (i = 0; i < logk_ptr->count_add_logk; i++) + { + coef = logk_ptr->add_logk[i].coef; + strcpy(token, logk_ptr->add_logk[i].name); + str_tolower(token); + item.key = token; + item.data = NULL; + found_item = hsearch_multi(logk_hash_table, item, FIND); + if (found_item == NULL) + { + input_error++; + error_string = sformatf( + "Could not find named temperature expression, %s\n", + token); + error_msg(error_string, CONTINUE); + return (ERROR); + } + next_logk_ptr = (struct logk *) found_item->data; + if (next_logk_ptr->done == FALSE) + { + /*output_msg(sformatf( "Done == FALSE\n", token)); */ + if (add_logks(next_logk_ptr, repeats + 1) == ERROR) + { + return (ERROR); + } + } + for (j = 0; j < MAX_LOG_K_INDICES; j++) + { + logk_ptr->log_k[j] += next_logk_ptr->log_k[j] * coef; + } + } + logk_ptr->done = TRUE; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +coef_in_master(struct master * master_ptr) +/* ---------------------------------------------------------------------- */ +{ + int l; + LDBLE coef; + char *ptr; + char elt_name[MAX_LENGTH]; + struct elt_list *next_elt; + + coef = 0.0; + char * temp_name = string_duplicate(master_ptr->elt->name); + ptr = temp_name; + get_elt(&ptr, elt_name, &l); + free_check_null(temp_name); + for (next_elt = master_ptr->s->next_elt; next_elt->elt != NULL; + next_elt++) + { + if (strcmp(elt_name, next_elt->elt->name) == 0) + { + coef = next_elt->coef; + break; + } + } + return (coef); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +rewrite_eqn_to_secondary(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Write equation for species in terms of secondary species + * Result is in trxn. + */ + LDBLE coef; + int repeat, i, add_count; + struct rxn_token_temp *token_ptr; +/* + * + */ + add_count = 0; + repeat = TRUE; +/* + * Reduce reaction equation to primary and secondary species + */ + while (repeat == TRUE) + { + repeat = FALSE; + /* Check for too many iterations */ + if (++add_count > MAX_ADD_EQUATIONS) + { + parse_error++; + error_string = sformatf( + "Could not reduce equation to secondary master species, %s.", + trxn.token[0].name); + error_msg(error_string, CONTINUE); + break; + } + + for (i = 1; i < count_trxn; i++) + { + token_ptr = &(trxn.token[i]); + if (token_ptr->s == NULL) + { + error_string = sformatf( + "NULL species pointer for species, %s.", + token_ptr->name); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + if (token_ptr->s->secondary == NULL + && token_ptr->s->primary == NULL) + { + coef = token_ptr->coef; + trxn_add(token_ptr->s->rxn, coef, TRUE); + repeat = TRUE; + break; + } + } + } + trxn_combine(); + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +replace_solids_gases(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Write equation for species in terms of secondary species + * Result is in trxn. + */ + LDBLE coef; + int n; + int repeat, i, add_count; + struct rxn_token_temp *token_ptr; + struct phase *phase_ptr; + int replaced; + char token[MAX_LENGTH]; +/* + * + */ + add_count = 0; + repeat = TRUE; + replaced = FALSE; +/* + * Reduce reaction equation to primary and secondary species + */ + while (repeat == TRUE) + { + repeat = FALSE; + /* Check for too many iterations */ + if (++add_count > MAX_ADD_EQUATIONS) + { + parse_error++; + error_string = sformatf( + "Could not remove all solids and gases from equation, %s.", + trxn.token[0].name); + error_msg(error_string, CONTINUE); + break; + } + + for (i = 1; i < count_trxn; i++) + { + token_ptr = &(trxn.token[i]); + if (token_ptr->s == NULL) + { + phase_ptr = phase_bsearch(token_ptr->name, &n, FALSE); + /* try phase name without (g) or (s) */ + if (phase_ptr == NULL) + { + strcpy(token, token_ptr->name); + replace("(g)", "", token); + replace("(s)", "", token); + replace("(G)", "", token); + replace("(S)", "", token); + phase_ptr = phase_bsearch(token, &n, FALSE); + } + if (phase_ptr == NULL) + { + input_error++; + error_string = sformatf( "Phase not found, %s.", + token_ptr->name); + error_msg(error_string, CONTINUE); + break; + } + coef = token_ptr->coef; + /* add reaction for solid/gas */ + /* debug + output_msg(sformatf( "Reaction to add.\n")); + rxn_print(phase_ptr->rxn); + */ + trxn_add_phase(phase_ptr->rxn, coef, FALSE); + + /* remove solid/gas from trxn list */ + trxn.token[i].name = phase_ptr->rxn->token[0].name; + trxn.token[i].s = phase_ptr->rxn->token[0].s; + trxn.token[i].coef = -coef * phase_ptr->rxn->token[0].coef; + repeat = TRUE; + replaced = TRUE; + /* debug + output_msg(sformatf( "Before combined.\n")); + trxn_print(); + */ + /* combine */ + trxn_combine(); + /* debug + output_msg(sformatf( "Combined.\n")); + trxn_print(); + */ + break; + } + } + } + trxn_combine(); + return (replaced); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +rewrite_eqn_to_primary(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Write equation for secondary master species in terms of primary master species + * Store result in reaction structure for master species + * rewrite if necessary. + * + */ + int repeat, j, add_count; + +/* + * Check secondary master species + */ + repeat = TRUE; + add_count = 0; +/* + * Check if reaction contains only primary master species + */ + while (repeat == TRUE) + { + repeat = FALSE; +/* + * Check for too many iterations + */ + if (++add_count > MAX_ADD_EQUATIONS) + { + parse_error++; + error_string = sformatf( + "Could not reduce equation to primary master species, %s.", + trxn.token[0].s->name); + error_msg(error_string, CONTINUE); + break; + } +/* + * Go through species in reaction for secondary master species, look for non-primary + * species as reactants, rewrite + */ + for (j = 1; j < count_trxn; j++) + { + if (trxn.token[j].s->primary == NULL) + { + trxn_add(trxn.token[j].s->rxn, trxn.token[j].coef, TRUE); + repeat = TRUE; + break; + } + } + } + trxn_combine(); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_gas_phase(void) +/* ---------------------------------------------------------------------- */ +{ + int n_user, last; + LDBLE P, V_m; + bool PR; +/* + * Find all gases for each gas_phase in phase list + */ + for (std::set::const_iterator nit = Rxn_new_gas_phase.begin(); nit != Rxn_new_gas_phase.end(); nit++) + { + std::map::iterator it = Rxn_gas_phase_map.find(*nit); + if (it == Rxn_gas_phase_map.end()) + { + assert(false); + } + cxxGasPhase *gas_phase_ptr = &(it->second); + PR = false; + P = 0.0; + for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++) + { + int k; + struct phase *phase_ptr = phase_bsearch(gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str(), &k, FALSE); + if (phase_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Gas not found in PHASES database, %s.", + gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + else + { + if (phase_ptr->t_c > 0 && phase_ptr->p_c > 0) + PR = true; + } + } + gas_phase_ptr->Set_pr_in(PR); + + if (gas_phase_ptr->Get_new_def()) + { + for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++) + { + /* + * Fixed pressure + */ + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + if (gas_phase_ptr->Get_solution_equilibria()) + { + input_error++; + error_string = sformatf( + "Gas phase %d: cannot use '-equilibrium' option with fixed pressure gas phase.", + gas_phase_ptr->Get_n_user()); + error_msg(error_string, CONTINUE); + } + /* calculate moles */ +#ifdef NPP + if (!isnan(gas_phase_ptr->Get_gas_comps()[j].Get_p_read())) +#else + if (gas_phase_ptr->Get_gas_comps()[j].Get_p_read() != NAN) +#endif + { + P += gas_phase_ptr->Get_gas_comps()[j].Get_p_read(); + if (!PR) + gas_phase_ptr->Get_gas_comps()[j].Set_moles( + gas_phase_ptr->Get_gas_comps()[j].Get_p_read() * gas_phase_ptr->Get_volume() / + R_LITER_ATM / gas_phase_ptr->Get_temperature()); + } + else + { + input_error++; + error_string = sformatf( + "Gas phase %d: partial pressure of gas component %s not defined.", + gas_phase_ptr->Get_n_user(), gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str()); + error_msg(error_string, CONTINUE); + } + } + else + { + /* + * Fixed volume + */ + if (!gas_phase_ptr->Get_solution_equilibria()) + { +#ifdef NPP + if (!isnan(gas_phase_ptr->Get_gas_comps()[j].Get_p_read())) +#else + if (gas_phase_ptr->Get_gas_comps()[j].Get_p_read() != NAN) +#endif + { + P += gas_phase_ptr->Get_gas_comps()[j].Get_p_read(); + if (!PR) + gas_phase_ptr->Get_gas_comps()[j].Set_moles ( + gas_phase_ptr->Get_gas_comps()[j].Get_p_read() * + gas_phase_ptr->Get_volume() / R_LITER_ATM / + gas_phase_ptr->Get_temperature()); + } + else + { + input_error++; + error_string = sformatf( + "Gas phase %d: moles of gas component %s not defined.", + gas_phase_ptr->Get_n_user(), + gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str()); + error_msg(error_string, CONTINUE); + } + } + } + } + + if (PR && P > 0) + { + std::vector phase_ptrs; + size_t j_PR; + std::vector &gc = gas_phase_ptr->Get_gas_comps(); + for (j_PR = 0; j_PR < gas_phase_ptr->Get_gas_comps().size(); j_PR++) + { + int k; + struct phase *phase_ptr = phase_bsearch(gas_phase_ptr->Get_gas_comps()[j_PR].Get_phase_name().c_str(), &k, FALSE); + if (gc[j_PR].Get_p_read() == 0) + continue; + if (phase_ptr) + { + phase_ptr->moles_x = gc[j_PR].Get_p_read() / P; + phase_ptrs.push_back(phase_ptr); + } + } + V_m = calc_PR(phase_ptrs, P, gas_phase_ptr->Get_temperature(), 0); + gas_phase_ptr->Set_v_m(V_m); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME) + { + gas_phase_ptr->Set_total_p(P); + } + for (j_PR = 0; j_PR < gas_phase_ptr->Get_gas_comps().size(); j_PR++) + { + int k; + struct phase *phase_ptr = phase_bsearch(gc[j_PR].Get_phase_name().c_str(), &k, FALSE); + if (gc[j_PR].Get_p_read() == 0) + { + gc[j_PR].Set_moles(0.0); + } else + { + if (phase_ptr) + { + gc[j_PR].Set_moles(phase_ptr->moles_x * gas_phase_ptr->Get_volume() / V_m); + gas_phase_ptr->Set_total_moles(gas_phase_ptr->Get_total_moles() + gc[j_PR].Get_moles()); + } + } + } + } + /* + * Duplicate gas phase, only if not solution equilibria + */ + if (!gas_phase_ptr->Get_solution_equilibria()) + { + gas_phase_ptr->Set_new_def(false); + n_user = gas_phase_ptr->Get_n_user(); + last = gas_phase_ptr->Get_n_user_end(); + gas_phase_ptr->Set_n_user_end(n_user); + for (int j1 = n_user + 1; j1 <= last; j1++) + { + Utilities::Rxn_copy(Rxn_gas_phase_map, n_user, j1); + } + } + else + { + gas_phase_ptr->Set_new_def(true); + } + } + } + return (OK); +} +#ifdef SKIP +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_gas_phase(void) +/* ---------------------------------------------------------------------- */ +{ + int n_user, last; + LDBLE P, V_m; + bool PR; +/* + * Find all gases for each gas_phase in phase list + */ + for (std::set::const_iterator nit = Rxn_new_gas_phase.begin(); nit != Rxn_new_gas_phase.end(); nit++) + { + std::map::iterator it = Rxn_gas_phase_map.find(*nit); + if (it == Rxn_gas_phase_map.end()) + { + assert(false); + } + cxxGasPhase *gas_phase_ptr = &(it->second); + PR = false; + P = 0.0; + std::vector gc = gas_phase_ptr->Get_gas_comps(); + for (size_t j = 0; j < gc.size(); j++) + { + int k; + struct phase *phase_ptr = phase_bsearch(gc[j].Get_phase_name().c_str(), &k, FALSE); + if (phase_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Gas not found in PHASES database, %s.", + gc[j].Get_phase_name().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + else + { + if (phase_ptr->t_c > 0 && phase_ptr->p_c > 0) + PR = true; + } + gas_phase_ptr->Set_pr_in(PR); + if (gas_phase_ptr->Get_new_def()) + { + if (j == gc.size() - 1) + gas_phase_ptr->Set_new_def(false); + /* + * Fixed pressure + */ + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) + { + if (gas_phase_ptr->Get_solution_equilibria()) + { + input_error++; + error_string = sformatf( + "Gas phase %d: cannot use '-equilibrium' option with fixed pressure gas phase.", + gas_phase_ptr->Get_n_user()); + error_msg(error_string, CONTINUE); + } + /* calculate moles */ + if (gc[j].Get_p_read() != NAN) + { + P += gc[j].Get_p_read(); + if (!PR) + gc[j].Set_moles( + gc[j].Get_p_read() * gas_phase_ptr->Get_volume() / + R_LITER_ATM / gas_phase_ptr->Get_temperature()); + } + else + { + input_error++; + error_string = sformatf( + "Gas phase %d: partial pressure of gas component %s not defined.", + gas_phase_ptr->Get_n_user(), gc[j].Get_phase_name().c_str()); + error_msg(error_string, CONTINUE); + } + } + else + { + /* + * Fixed volume + */ + if (!gas_phase_ptr->Get_solution_equilibria()) + { + if (gc[j].Get_p_read() != NAN) + { + P += gc[j].Get_p_read(); + if (!PR) + gc[j].Set_moles ( + gc[j].Get_p_read() * + gas_phase_ptr->Get_volume() / R_LITER_ATM / + gas_phase_ptr->Get_temperature()); + } + else + { + input_error++; + error_string = sformatf( + "Gas phase %d: moles of gas component %s not defined.", + gas_phase_ptr->Get_n_user(), + gc[j].Get_phase_name().c_str()); + error_msg(error_string, CONTINUE); + } + } + } + + + gas_phase_ptr->Set_gas_comps(gc); + + if (PR && P > 0 && j == gc.size() - 1) + { + std::vector phase_ptrs; + size_t j_PR; + for (j_PR = 0; j_PR < gas_phase_ptr->Get_gas_comps().size(); j_PR++) + { + int k; + struct phase *phase_ptr = phase_bsearch(gas_phase_ptr->Get_gas_comps()[j_PR].Get_phase_name().c_str(), &k, FALSE); + if (gc[j_PR].Get_p_read() == 0) + continue; + phase_ptr->moles_x = gc[j_PR].Get_p_read() / P; + phase_ptrs.push_back(phase_ptr); + } + V_m = calc_PR(phase_ptrs, P, gas_phase_ptr->Get_temperature(), 0); + gas_phase_ptr->Set_v_m(V_m); + if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME) + { + gas_phase_ptr->Set_total_p(P); + } + std::vector gc = gas_phase_ptr->Get_gas_comps(); + for (j_PR = 0; j_PR < gas_phase_ptr->Get_gas_comps().size(); j_PR++) + { + int k; + struct phase *phase_ptr = phase_bsearch(gc[j_PR].Get_phase_name().c_str(), &k, FALSE); + if (gc[j_PR].Get_p_read() == 0) + { + gc[j_PR].Set_moles(0.0); + } else + { + gc[j_PR].Set_moles(phase_ptr->moles_x * + gas_phase_ptr->Get_volume() / V_m); + gas_phase_ptr->Set_total_moles(gas_phase_ptr->Get_total_moles() + gc[j_PR].Get_moles()); + } + } + gas_phase_ptr->Set_gas_comps(gc); + } + /* + * Duplicate gas phase, only if not solution equilibria + */ + if (!gas_phase_ptr->Get_solution_equilibria()) + { + n_user = gas_phase_ptr->Get_n_user(); + last = gas_phase_ptr->Get_n_user_end(); + gas_phase_ptr->Set_n_user_end(n_user); + for (int j1 = n_user + 1; j1 <= last; j1++) + { + Utilities::Rxn_copy(Rxn_gas_phase_map, n_user, j1); + } + } + else + { + gas_phase_ptr->Set_new_def(true); + } + } + } + } + return (OK); +} +#endif +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_inverse(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * After all of data are read, fill in data for an inverse structure, + * including master species pointers, phase pointers, and uncertainties + * and a list of all elements from phases or -balance input. + */ + int i, j, k, l; + int count_in; + LDBLE value; + struct inv_elts *inv_elts; + struct master *master_ptr; + struct master *master_alk_ptr; + struct elt_list *elt_list_ptr; + master_alk_ptr = master_bsearch("Alkalinity"); + for (i = 0; i < count_inverse; i++) + { + if (inverse[i].new_def != TRUE) + continue; +/* + * Set default uncertainties for all solutions, if necessary + */ + if (inverse[i].count_uncertainties < inverse[i].count_solns) + { + inverse[i].uncertainties = + (LDBLE *) PHRQ_realloc(inverse[i].uncertainties, + (size_t) inverse[i].count_solns * + sizeof(LDBLE)); + if (inverse[i].uncertainties == NULL) + malloc_error(); + for (j = inverse[i].count_uncertainties; + j < inverse[i].count_solns; j++) + { + inverse[i].uncertainties[j] = + inverse[i].uncertainties[inverse[i].count_uncertainties - + 1]; + } + } +/* + * Set default ph uncertainties for all solutions, if necessary + */ + if (inverse[i].count_ph_uncertainties < inverse[i].count_solns) + { + inverse[i].ph_uncertainties = + (LDBLE *) PHRQ_realloc(inverse[i].ph_uncertainties, + (size_t) inverse[i].count_solns * + sizeof(LDBLE)); + if (inverse[i].ph_uncertainties == NULL) + malloc_error(); + for (j = inverse[i].count_ph_uncertainties; + j < inverse[i].count_solns; j++) + { + inverse[i].ph_uncertainties[j] = + inverse[i].ph_uncertainties[inverse[i]. + count_ph_uncertainties - 1]; + } + } +/* + * Set default force for all solutions + */ + if (inverse[i].count_force_solns < inverse[i].count_solns) + { + inverse[i].force_solns = + (int *) PHRQ_realloc(inverse[i].force_solns, + (size_t) inverse[i].count_solns * + sizeof(int)); + if (inverse[i].force_solns == NULL) + malloc_error(); + for (j = inverse[i].count_force_solns; j < inverse[i].count_solns; + j++) + { + inverse[i].force_solns[j] = FALSE; + } + } +/* + * Find master species for element, set uncertainties + */ + for (j = 0; j < inverse[i].count_elts; j++) + { + inverse[i].elts[j].master = + master_bsearch_primary(inverse[i].elts[j].name); + if (inverse[i].elts[j].master == NULL) + { + input_error++; + error_string = sformatf( "No master species for element, %s.", + inverse[i].elts[j].name); + error_msg(error_string, CONTINUE); + continue; + } + inverse[i].elts[j].uncertainties = + (LDBLE *) PHRQ_realloc(inverse[i].elts[j].uncertainties, + (size_t) inverse[i].count_solns * + sizeof(LDBLE)); + if (inverse[i].elts[j].uncertainties == NULL) + malloc_error(); + if (inverse[i].elts[j].count_uncertainties == 0) + { +/* use default uncertainties for element */ + for (k = 0; k < inverse[i].count_solns; k++) + { + inverse[i].elts[j].uncertainties[k] = + inverse[i].uncertainties[k]; + } + } + else if (inverse[i].elts[j].count_uncertainties < + inverse[i].count_solns) + { +/* use input uncertainties, fill in any missing at end */ + value = + inverse[i].elts[j].uncertainties[inverse[i].elts[j]. + count_uncertainties - 1]; + for (k = inverse[i].elts[j].count_uncertainties; + k < inverse[i].count_solns; k++) + { + inverse[i].elts[j].uncertainties[k] = value; + } + } + } +/* + * Find phase + */ + count_elts = 0; + paren_count = 0; + for (j = 0; j < inverse[i].count_phases; j++) + { + inverse[i].phases[j].phase = + phase_bsearch(inverse[i].phases[j].name, &k, FALSE); + if (inverse[i].phases[j].phase == NULL) + { + input_error++; + error_string = sformatf( "Could not find phase, %s.", + inverse[i].phases[j].name); + error_msg(error_string, CONTINUE); + continue; + } +/* + * Find isotope elements + */ + if (inverse[i].phases[j].count_isotopes > 0) + { + for (k = 0; k < inverse[i].phases[j].count_isotopes; k++) + { + inverse[i].phases[j].isotopes[k].primary = NULL; + inverse[i].phases[j].isotopes[k].master = NULL; + master_ptr = + master_bsearch(inverse[i].phases[j].isotopes[k]. + elt_name); + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Element not found for isotope calculation: %s.", + inverse[i].phases[j].isotopes[k].elt_name); + error_msg(error_string, CONTINUE); + continue; + } + if (master_ptr->primary != TRUE) + { + input_error++; + error_string = sformatf( + "Isotope ratio may only be used" + " for total element in phase.\n" + "Secondary species not allowed: %s.", + master_ptr->elt->name); + error_msg(error_string, CONTINUE); + continue; + } + inverse[i].phases[j].isotopes[k].primary = master_ptr; + inverse[i].phases[j].isotopes[k].master = master_ptr; + /* find coefficient for element */ + for (elt_list_ptr = inverse[i].phases[j].phase->next_elt; + elt_list_ptr->elt != NULL; elt_list_ptr++) + { + if (elt_list_ptr->elt == master_ptr->elt) + { + inverse[i].phases[j].isotopes[k].coef = + elt_list_ptr->coef; + break; + } + } + if (elt_list_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Element, %s,for which isotope ratio was defined is not found in phase, %s", + master_ptr->elt->name, + inverse[i].phases[j].phase->name); + error_msg(error_string, CONTINUE); + continue; + } + } + qsort(inverse[i].phases[j].isotopes, + (size_t) inverse[i].phases[j].count_isotopes, + (size_t) sizeof(struct isotope), isotope_compare); + } + add_elt_list(inverse[i].phases[j].phase->next_elt, 1.0); + + } + if (get_input_errors() > 0) + return (ERROR); +/* + * Sort elements in reaction and combine + */ + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + } +/* + * Mark master species list + */ + for (j = 0; j < count_master; j++) + master[j]->in = FALSE; + for (j = 0; j < count_elts; j++) + { + elt_list[j].elt->master->in = TRUE; + } + /* Include all input elements */ + for (j = 0; j < inverse[i].count_elts; j++) + { + inverse[i].elts[j].master->in = TRUE; + } + s_eminus->primary->in = TRUE; /* Include electrons */ + master_alk_ptr->in = TRUE; /* Include alkalinity */ +/* + * Unmark primary and mark secondary master species for redox elements + */ + count_in = 0; + inverse[i].count_redox_rxns = 0; + for (j = 0; j < count_master; j++) + { + /* skip all secondary master species in this loop */ + if (master[j]->primary == FALSE || master[j]->in == FALSE) + continue; + count_in++; + if (j + 1 == count_master) + continue; + /* if next master species is secondary, mark all + secondary master species until a primary is found */ + if (master[j + 1]->primary == FALSE) + { + master[j]->in = FALSE; + count_in--; + for (k = j + 1; k < count_master; k++) + { + if (master[k]->primary == FALSE) + { + count_in++; + master[k]->in = TRUE; + if (master[k]->s->primary == NULL) + { + inverse[i].count_redox_rxns++; + } + } + else + { + break; + } + } + } + } +/* + * Save list of master species in inv_elts structure + */ + inv_elts = + (struct inv_elts *) PHRQ_malloc((size_t) (count_in) * + sizeof(struct inv_elts)); + if (inv_elts == NULL) + malloc_error(); + count_in = 0; + for (j = 0; j < count_master; j++) + { + /* skip H(1) and O(-2) */ + if (master[j]->s == s_hplus || master[j]->s == s_h2o) + continue; + if (master[j]->in == TRUE) + { + /* set master */ + inv_elts[count_in].master = master[j]; + /* alloc uncertainties and set default */ + inv_elts[count_in].uncertainties = + (LDBLE *) PHRQ_malloc((size_t) inverse[i].count_solns * + sizeof(LDBLE)); + if (inv_elts[count_in].uncertainties == NULL) + malloc_error(); + for (k = 0; k < inverse[i].count_solns; k++) + { + inv_elts[count_in].uncertainties[k] = + inverse[i].uncertainties[k]; + } + count_in++; + } + } + if (s_co3->secondary->in == TRUE) + { + inverse[i].carbon = TRUE; + } + else + { + inverse[i].carbon = FALSE; + } +/* + * copy in input uncertainties + */ + /* copy primary redox to all secondary redox */ + for (j = 0; j < inverse[i].count_elts; j++) + { + master_ptr = master_bsearch(inverse[i].elts[j].name); + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( "Element not found, %s.", + inverse[i].elts[j].name); + error_msg(error_string, CONTINUE); + continue; + } + if (master_ptr->primary == FALSE + || master_ptr->s->secondary == NULL) + continue; + for (k = 0; k < count_in; k++) + { + if (master_ptr == inv_elts[k].master->elt->primary) + { + for (l = 0; l < inverse[i].count_solns; l++) + { + inv_elts[k].uncertainties[l] = + inverse[i].elts[j].uncertainties[l]; + } + } + } + inverse[i].elts[j].uncertainties = + (LDBLE *) free_check_null(inverse[i].elts[j].uncertainties); + } + /* copy masters that are not primary redox */ + for (j = 0; j < inverse[i].count_elts; j++) + { + master_ptr = master_bsearch(inverse[i].elts[j].name); + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( "Element not found, %s.", + inverse[i].elts[j].name); + error_msg(error_string, CONTINUE); + continue; + } + if (master_ptr->primary == TRUE + && master_ptr->s->secondary != NULL) + continue; + for (k = 0; k < count_in; k++) + { + if (master_ptr == inv_elts[k].master) + { + for (l = 0; l < inverse[i].count_solns; l++) + { + inv_elts[k].uncertainties[l] = + inverse[i].elts[j].uncertainties[l]; + } + break; + } + } + inverse[i].elts[j].uncertainties = + (LDBLE *) free_check_null(inverse[i].elts[j].uncertainties); + } +/* + * replace elts in inverse struct + */ + inverse[i].elts = + (struct inv_elts *) free_check_null(inverse[i].elts); + inverse[i].elts = inv_elts; + inverse[i].count_elts = count_in; + for (j = 0; j < inverse[i].count_elts; j++) + { +/* debug + output_msg(sformatf( "\t%d\t%s", j, inverse[i].elts[j].master->elt->name)); + for (k = 0; k < inverse[i].count_solns; k++) { + output_msg(sformatf( "\t%f", inverse[i].elts[j].uncertainties[k])); + } + output_msg(sformatf("\n")); + */ + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_phases(void) +/* ---------------------------------------------------------------------- */ +{ + int i; + int replaced; + /* + * Fix log Ks first, so they can possibly be added to other phase equations + */ + for (i = 0; i < count_phases; i++) + { + select_log_k_expression(phases[i]->logk, phases[i]->rxn->logk); + add_other_logk(phases[i]->rxn->logk, phases[i]->count_add_logk, + phases[i]->add_logk); + phases[i]->rxn->token[0].name = phases[i]->name; + phases[i]->rxn->token[0].s = NULL; + } + /* + * Rewrite all phases to secondary species + */ + for (i = 0; i < count_phases; i++) + { + /* + * Rewrite equation + */ + count_trxn = 0; + trxn_add_phase(phases[i]->rxn, 1.0, FALSE); + trxn.token[0].name = phases[i]->name; + /* debug + output_msg(sformatf( "%s PHASE.\n", phases[i]->name)); + trxn_print(); + */ + replaced = replace_solids_gases(); + phases[i]->replaced = replaced; + /* save rxn */ + /* + rxn_free(phases[i]->rxn); + phases[i]->rxn = rxn_alloc(count_trxn + 1); + trxn_copy(phases[i]->rxn); + */ + /* save rxn_s */ + trxn_reverse_k(); + rewrite_eqn_to_secondary(); + trxn_reverse_k(); + rxn_free(phases[i]->rxn_s); + phases[i]->rxn_s = rxn_alloc(count_trxn + 1); + trxn_copy(phases[i]->rxn_s); + /* + * Check equation + */ + if (phases[i]->check_equation == TRUE) + { + if (replaced == FALSE) + { + phase_rxn_to_trxn(phases[i], phases[i]->rxn); + } + else + { + phase_rxn_to_trxn(phases[i], phases[i]->rxn_s); + } + if (check_eqn(FALSE) == ERROR) + { + input_error++; + error_string = sformatf( + "Equation for phase %s does not balance.", + phases[i]->name); + error_msg(error_string, CONTINUE); + } + } + } + + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_pp_assemblage(void) +/* ---------------------------------------------------------------------- */ +{ + LDBLE coef; + char *ptr; +/* + * Find pointers for pure phases + */ + //std::map::iterator it; + //it = Rxn_pp_assemblage_map.begin(); + //for ( ; it != Rxn_pp_assemblage_map.end(); it++) + //{ + //for (size_t nn = 0; nn < Rxn_new_pp_assemblage.size(); nn++) + //{ + //std::map::iterator kit = Rxn_pp_assemblage_map.find(Rxn_new_pp_assemblage[nn]); + for (std::set::const_iterator nit = Rxn_new_pp_assemblage.begin(); nit != Rxn_new_pp_assemblage.end(); nit++) + { + std::map::iterator kit = Rxn_pp_assemblage_map.find(*nit); + if (kit == Rxn_pp_assemblage_map.end()) + { + assert(false); + } + //if (!kit->second.Get_new_def()) continue; + cxxPPassemblage *pp_assemblage_ptr = &(kit->second); + count_elts = 0; + paren_count = 0; + coef = 1.0; + pp_assemblage_ptr->Set_new_def(false); + + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + int k; + struct phase *phase_ptr = phase_bsearch(it->first.c_str(), &k, FALSE); + if (phase_ptr == NULL) + { + input_error++; + error_string = sformatf( "Phase not found in database, %s.", + it->first.c_str()); + error_msg(error_string, CONTINUE); + continue; + } + else + { + add_elt_list(phase_ptr->next_elt, coef); + } + if (it->second.Get_add_formula().size() > 0) + { + int first = count_elts; + phase_ptr = phase_bsearch(it->second.Get_add_formula().c_str(), &k, FALSE); + if (phase_ptr != NULL) + { + it->second.Set_add_formula(phase_ptr->formula); + } + { + char * temp_add = string_duplicate(it->second.Get_add_formula().c_str()); + ptr = temp_add; + get_elts_in_species(&ptr, coef); + free_check_null(temp_add); + } + /* check that all elements are in the database */ + for (int l = first; l < count_elts; l++) + { + if (elt_list[l].elt->master == NULL) + { + input_error++; + error_string = sformatf( + "Element \"%s\" in alternative phase for \"%s\" in EQUILIBRIUM_PHASES not found in database.", + elt_list[l].elt->name, + it->first.c_str()); + error_msg(error_string, CONTINUE); + } + } + } + } + +/* + * Store list with all elements in phases and add formulae + */ + cxxNameDouble nd = elt_list_NameDouble(); + pp_assemblage_ptr->Set_eltList(nd); +/* + * Duplicate pure phases if necessary + */ + + int n_user = pp_assemblage_ptr->Get_n_user(); + int n_user_end = pp_assemblage_ptr->Get_n_user_end(); + pp_assemblage_ptr->Set_n_user_end(n_user); + Utilities::Rxn_copies(Rxn_pp_assemblage_map, n_user, n_user_end); + + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_ss_assemblage(void) +/* ---------------------------------------------------------------------- */ +{ + struct phase *phase_ptr; + LDBLE nb, nc, n_tot, xb, xc, dnb, dnc, l_a0, l_a1; + LDBLE xb2, xb3, xb4, xc2, xc3; + LDBLE moles; +/* + * Find pointers for pure phases + */ + //std::map::iterator it; + //for (it = Rxn_ss_assemblage_map.begin(); it != Rxn_ss_assemblage_map.end(); it++) + //{ + //for (size_t nn = 0; nn < Rxn_new_ss_assemblage.size(); nn++) + //{ + //std::map::iterator it = Rxn_ss_assemblage_map.find(Rxn_new_ss_assemblage[nn]); + for (std::set::const_iterator nit = Rxn_new_ss_assemblage.begin(); nit != Rxn_new_ss_assemblage.end(); nit++) + { + std::map::iterator it = Rxn_ss_assemblage_map.find(*nit); + if (it == Rxn_ss_assemblage_map.end()) + { + assert(false); + } + //if (!it->second.Get_new_def()) continue; + count_elts = 0; + paren_count = 0; + cxxSSassemblage *ss_assemblage_ptr = &(it->second); + std::vector ss_ptrs = ss_assemblage_ptr->Vectorize(); + for (size_t j = 0; j < ss_ptrs.size(); j++) + { + cxxSS *ss_ptr = ss_ptrs[j]; + for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++) + { + cxxSScomp * comp_ptr = &(ss_ptr->Get_ss_comps()[k]); + int k1; + phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &k1, FALSE); + if (phase_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Phase not found in database, %s, assemblage %d.", + comp_ptr->Get_name().c_str(), + ss_assemblage_ptr->Get_n_user()); + error_msg(error_string, CONTINUE); + continue; + } + else + { + phase_ptr->moles_x = 0; + phase_ptr->fraction_x = 0; + } +#ifdef NPP + if (isnan(comp_ptr->Get_moles())) +#else + if (comp_ptr->Get_moles() == NAN) +#endif + { + input_error++; + error_string = sformatf( + "Moles of solid solution component not defined, %s, assemblage %d.", + comp_ptr->Get_name().c_str(), + ss_assemblage_ptr->Get_n_user()); + error_msg(error_string, CONTINUE); + continue; + } + } + + if (ss_assemblage_ptr->Get_new_def()) + { + /* + * Calculate a0 and a1 first + */ + ss_calc_a0_a1(ss_ptr); + + n_tot = 0; + for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++) + { + cxxSScomp * comp_ptr = &(ss_ptr->Get_ss_comps()[k]); + moles = comp_ptr->Get_moles(); + if (moles <= 0.0) + { + moles = MIN_TOTAL_SS; + comp_ptr->Set_initial_moles(moles); + } + n_tot += moles; + } + + for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++) + { + cxxSScomp * comp_ptr = &(ss_ptr->Get_ss_comps()[k]); + moles = comp_ptr->Get_moles(); + if (moles <= 0.0) + { + moles = MIN_TOTAL_SS; + } + comp_ptr->Set_fraction_x(moles / n_tot); + comp_ptr->Set_log10_fraction_x(log10(moles / n_tot)); + } + l_a0 = ss_ptr->Get_a0(); + l_a1 = ss_ptr->Get_a1(); + +/* + * Binary solid solution + */ + if (l_a0 != 0.0 || l_a1 != 0) + { + ss_ptr->Set_dn(1.0 / n_tot); + nc = ss_ptr->Get_ss_comps()[0].Get_moles(); + if (nc == 0) + nc = MIN_TOTAL_SS; + nb = ss_ptr->Get_ss_comps()[1].Get_moles(); + if (nb == 0) + nb = MIN_TOTAL_SS; + xc = nc / n_tot; + xb = nb / n_tot; + + /* lambdas */ + ss_ptr->Get_ss_comps()[0].Set_log10_lambda(xb * xb * (l_a0 - l_a1 * (3 - 4 * xb)) / LOG_10); + ss_ptr->Get_ss_comps()[1].Set_log10_lambda(xc * xc * (l_a0 + l_a1 * (4 * xb - 1)) / LOG_10); + + /* derivatives wrt nc and nb */ + xc2 = xc * xc; + xc3 = xc2 * xc; + xb2 = xb * xb; + xb3 = xb2 * xb; + xb4 = xb3 * xb; + + /* component 1 */ + dnb = + -2 * l_a0 * xb * xc2 - 8 * l_a1 * xb2 * xc2 + + 6 * l_a1 * xb * xc2 - 4 * l_a1 * xc * xb4 - + 8 * l_a1 * xb3 * xc2 - 4 * l_a1 * xb2 * xc3 - + 2 * l_a0 * xc * xb2 - 8 * l_a1 * xc * xb3 + + 6 * l_a1 * xc * xb2 + 1; + ss_ptr->Get_ss_comps()[0].Set_dnb(dnb / n_tot); + dnc = + 2 * l_a0 * xb3 + 2 * l_a0 * xc * xb2 + 8 * l_a1 * xb4 + + 8 * l_a1 * xc * xb3 - 2 * l_a1 * xb3 - 6 * l_a1 * xc * xb2; + ss_ptr->Get_ss_comps()[0].Set_dnc(-xb / nc + dnc / n_tot); + ss_ptr->Get_ss_comps()[0].Set_dn(1.0 / n_tot); + + /* component 2 */ + dnb = + 2 * l_a0 * xb * xc2 + 2 * l_a0 * xc3 + + 8 * l_a1 * xb2 * xc2 + 8 * l_a1 * xb * xc3 - + 2 * l_a1 * xb * xc2 - 6 * l_a1 * xc3; + ss_ptr->Get_ss_comps()[1].Set_dnb(-xc / nb + dnb / n_tot); + dnc = + -2 * l_a0 * xc * xb2 - 8 * l_a1 * xc * xb3 + + 2 * l_a1 * xc * xb2 - 2 * l_a0 * xb * xc2 - + 8 * l_a1 * xb2 * xc2 + 6 * l_a1 * xb * xc2 + 1; + ss_ptr->Get_ss_comps()[1].Set_dnc(dnc / n_tot); + ss_prep(ss_ptr->Get_tk(), ss_ptr, TRUE); + ss_ptr->Get_ss_comps()[1].Set_dn(1.0 / n_tot); +/* + * Ideal solid solution + */ + } + else + { + ss_ptr->Set_dn(1.0 / n_tot); + for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++) + { + cxxSScomp * comp_ptr = &(ss_ptr->Get_ss_comps()[k]); + comp_ptr->Set_log10_lambda(0); + moles = comp_ptr->Get_moles(); + if (moles <= 0.0) + moles = MIN_TOTAL_SS; + comp_ptr->Set_dnb((n_tot - moles) / (moles * n_tot)); + comp_ptr->Set_dn(1.0 / n_tot); + } + } + } + } + ss_assemblage_ptr->Set_new_def(false); +/* + * Duplicate ss_assemblage if necessary + */ + int n_user = ss_assemblage_ptr->Get_n_user(); + int n_user_end = ss_assemblage_ptr->Get_n_user_end(); + Utilities::Rxn_copies(Rxn_ss_assemblage_map, n_user, n_user_end); + ss_assemblage_ptr->Set_n_user_end(n_user); + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_punch(void) +/* ---------------------------------------------------------------------- */ +{ + //int i, j, l; + int punch_save; + //char token[MAX_LENGTH]; +/* + * tidy punch information + */ + std::map < int, SelectedOutput >::iterator so_it = SelectedOutput_map.begin(); + for ( ; so_it != SelectedOutput_map.end(); so_it++) + { + current_selected_output = &(so_it->second); + if (current_selected_output == NULL) + continue; + + + /* totals */ + + for (size_t i = 0; i < current_selected_output->Get_totals().size(); i++) + { + std::pair< std::string, void *> &pair_ptr = current_selected_output->Get_totals()[i]; + pair_ptr.second = master_bsearch(pair_ptr.first.c_str()); + } + + /* molalities */ + for (size_t i = 0; i < current_selected_output->Get_molalities().size(); i++) + { + std::pair< std::string, void *> &pair_ptr = current_selected_output->Get_molalities()[i]; + pair_ptr.second = s_search(pair_ptr.first.c_str()); + } + + /* log activities */ + + //for (i = 0; i < punch.count_activities; i++) + for (size_t i = 0; i < current_selected_output->Get_activities().size(); i++) + { + std::pair< std::string, void *> &pair_ptr = current_selected_output->Get_activities()[i]; + pair_ptr.second = s_search(pair_ptr.first.c_str()); + } + + /* equilibrium phases */ + + //for (i = 0; i < punch.count_pure_phases; i++) + for (size_t i = 0; i < current_selected_output->Get_pure_phases().size(); i++) + { + int j; + std::pair< std::string, void *> &pair_ptr = current_selected_output->Get_pure_phases()[i]; + pair_ptr.second = phase_bsearch(pair_ptr.first.c_str(), &j, FALSE); + } + + /* saturation indices */ + + //for (i = 0; i < punch.count_si; i++) + for (size_t i = 0; i < current_selected_output->Get_si().size(); i++) + { + int j; + std::pair< std::string, void *> &pair_ptr = current_selected_output->Get_si()[i]; + pair_ptr.second = phase_bsearch(pair_ptr.first.c_str(), &j, FALSE); + } + + /* gases */ + + //for (i = 0; i < punch.count_gases; i++) + for (size_t i = 0; i < current_selected_output->Get_gases().size(); i++) + { + int j; + std::pair< std::string, void *> &pair_ptr = current_selected_output->Get_gases()[i]; + pair_ptr.second = phase_bsearch(pair_ptr.first.c_str(), &j, FALSE); + } + } + /* + * Always write new headings when SELECTED_OUTPUT is read + */ + so_it = SelectedOutput_map.begin(); + for ( ; so_it != SelectedOutput_map.end(); so_it++) + { + current_selected_output = &(so_it->second); + if (current_selected_output == NULL || + !current_selected_output->Get_new_def()) + continue; + phrq_io->Set_punch_ostream(current_selected_output->Get_punch_ostream()); + + + int l; + if (current_selected_output->Get_high_precision() == false) + { + l = 12; + } + else + { + l = 20; + } + // UserPunch + std::map < int, UserPunch >::iterator up_it = UserPunch_map.find(current_selected_output->Get_n_user()); + current_user_punch = up_it == UserPunch_map.end() ? NULL : &(up_it->second); + + punch_save = pr.punch; + pr.punch = TRUE; + phrq_io->Set_punch_on(true); + + /* constant stuff, sim, pH, etc. */ + + if (current_selected_output->Get_sim() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "sim")); + } + if (current_selected_output->Get_state() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "state")); + } + if (current_selected_output->Get_soln() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "soln")); + } + if (current_selected_output->Get_dist() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "dist_x")); + } + if (current_selected_output->Get_time() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "time")); + } + if (current_selected_output->Get_step() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "step")); + } + if (current_selected_output->Get_ph() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "pH")); + } + if (current_selected_output->Get_pe() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "pe")); + } + if (current_selected_output->Get_rxn() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "reaction")); + } + if (current_selected_output->Get_temp() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "temp")); + } + if (current_selected_output->Get_alk() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "Alk")); + } + if (current_selected_output->Get_mu() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "mu")); + } + if (current_selected_output->Get_water() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "mass_H2O")); + } + if (current_selected_output->Get_charge_balance() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "charge")); + } + if (current_selected_output->Get_percent_error() == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "pct_err")); + } + /* totals */ + + //for (i = 0; i < punch.count_totals; i++) + for (size_t i = 0; i < current_selected_output->Get_totals().size(); i++) + { + std::pair< std::string, void *> &pair_ref = current_selected_output->Get_totals()[i]; + fpunchf_heading(sformatf("%*s\t", l, pair_ref.first.c_str())); + if (pair_ref.second == NULL) + { + error_string = sformatf( "Did not find master species," + " %s.", pair_ref.first.c_str()); + warning_msg(error_string); + } + //fpunchf_heading(sformatf("%*s\t", l, punch.totals[i].name)); + //if (punch.totals[i].master == NULL) + //{ + // error_string = sformatf( "Did not find master species," + // " %s.", punch.totals[i].name); + // warning_msg(error_string); + //} + } + + /* molalities */ + + //for (i = 0; i < punch.count_molalities; i++) + for (size_t i = 0; i < current_selected_output->Get_molalities().size(); i++) + { + std::pair< std::string, void *> &pair_ref = current_selected_output->Get_molalities()[i]; + std::string name = "m_"; + name.append(pair_ref.first); + fpunchf_heading(sformatf("%*s\t", l, name.c_str())); + if (pair_ref.second == NULL) + { + error_string = sformatf( "Did not find species," + " %s.", pair_ref.first.c_str()); + warning_msg(error_string); + } + //strcpy(token, "m_"); + //strcat(token, punch.molalities[i].name); + //fpunchf_heading(sformatf("%*s\t", l, token)); + //if (punch.molalities[i].s == NULL) + //{ + // error_string = sformatf( "Did not find species," + // " %s.", punch.molalities[i].name); + // warning_msg(error_string); + //} + } + + /* log activities */ + + //for (i = 0; i < punch.count_activities; i++) + for (size_t i = 0; i < current_selected_output->Get_activities().size(); i++) + { + std::pair< std::string, void *> &pair_ref = current_selected_output->Get_activities()[i]; + std::string name = "la_"; + name.append(pair_ref.first); + fpunchf_heading(sformatf("%*s\t", l, name.c_str())); + if (pair_ref.second == NULL) + { + error_string = sformatf( "Did not find species," + " %s.", pair_ref.first.c_str()); + warning_msg(error_string); + } + //strcpy(token, "la_"); + //strcat(token, punch.activities[i].name); + //fpunchf_heading(sformatf("%*s\t", l, token)); + //if (punch.activities[i].s == NULL) + //{ + // error_string = sformatf( "Did not find species, " + // "%s.", punch.activities[i].name); + // warning_msg(error_string); + //} + } + + /* equilibrium phases */ + + //for (i = 0; i < punch.count_pure_phases; i++) + for (size_t i = 0; i < current_selected_output->Get_pure_phases().size(); i++) + { + std::pair< std::string, void *> &pair_ref = current_selected_output->Get_pure_phases()[i]; + fpunchf_heading(sformatf("%*s\t", l, pair_ref.first.c_str())); + std::string name = "d_"; + name.append(pair_ref.first); + fpunchf_heading(sformatf("%*s\t", l, name.c_str())); + if (pair_ref.second == NULL) + { + error_string = sformatf( "Did not find phase," + " %s.", pair_ref.first.c_str()); + warning_msg(error_string); + } + //strcpy(token, "d_"); + //strcat(token, punch.pure_phases[i].name); + //fpunchf_heading(sformatf("%*s\t", l, punch.pure_phases[i].name)); + //fpunchf_heading(sformatf("%*s\t", l, token)); + //if (punch.pure_phases[i].phase == NULL) + //{ + // error_string = sformatf( "Did not find phase, " + // "%s.", punch.pure_phases[i].name); + // warning_msg(error_string); + //} + } + + /* saturation indices */ + + //for (i = 0; i < punch.count_si; i++) + for (size_t i = 0; i < current_selected_output->Get_si().size(); i++) + { + std::pair< std::string, void *> &pair_ref = current_selected_output->Get_si()[i]; + std::string name = "si_"; + name.append(pair_ref.first); + fpunchf_heading(sformatf("%*s\t", l, name.c_str())); + if (pair_ref.second == NULL) + { + error_string = sformatf( "Did not find phase," + " %s.", pair_ref.first.c_str()); + warning_msg(error_string); + } + //strcpy(token, "si_"); + //strcat(token, punch.si[i].name); + //fpunchf_heading(sformatf("%*s\t", l, token)); + //if (punch.si[i].phase == NULL) + //{ + // error_string = sformatf( "Did not find phase, " + // "%s.", punch.si[i].name); + // warning_msg(error_string); + //} + } + + /* gases */ + + //if (punch.count_gases > 0) + if (current_selected_output->Get_gases().size() > 0) + { + fpunchf_heading(sformatf("%*s\t", l, "pressure")); + fpunchf_heading(sformatf("%*s\t", l, "total mol")); + fpunchf_heading(sformatf("%*s\t", l, "volume")); + } + //for (i = 0; i < punch.count_gases; i++) + for (size_t i = 0; i < current_selected_output->Get_gases().size(); i++) + { + std::pair< std::string, void *> &pair_ref = current_selected_output->Get_gases()[i]; + std::string name = "g_"; + name.append(pair_ref.first); + fpunchf_heading(sformatf("%*s\t", l, name.c_str())); + if (pair_ref.second == NULL) + { + error_string = sformatf( "Did not find phase," + " %s.", pair_ref.first.c_str()); + warning_msg(error_string); + } + //strcpy(token, "g_"); + //strcat(token, punch.gases[i].name); + //fpunchf_heading(sformatf("%*s\t", l, token)); + //if (punch.gases[i].phase == NULL) + //{ + // error_string = sformatf( "Did not find phase, " + // "%s.", punch.gases[i].name); + // warning_msg(error_string); + //} + } + + /* kinetics */ + + //for (i = 0; i < punch.count_kinetics; i++) + for (size_t i = 0; i < current_selected_output->Get_kinetics().size(); i++) + { + std::pair< std::string, void *> &pair_ref = current_selected_output->Get_kinetics()[i]; + std::string name = "k_"; + name.append(pair_ref.first); + fpunchf_heading(sformatf("%*s\t", l, name.c_str())); + name = "dk_"; + name.append(pair_ref.first); + fpunchf_heading(sformatf("%*s\t", l, name.c_str())); + //strcpy(token, "k_"); + //strcat(token, punch.kinetics[i].name); + //fpunchf_heading(sformatf("%*s\t", l, token)); + //strcpy(token, "dk_"); + //strcat(token, punch.kinetics[i].name); + //fpunchf_heading(sformatf("%*s\t", l, token)); + } + + /* solid solutions */ + + //for (i = 0; i < punch.count_s_s; i++) + for (size_t i = 0; i < current_selected_output->Get_s_s().size(); i++) + { + std::pair< std::string, void *> &pair_ref = current_selected_output->Get_s_s()[i]; + std::string name = "s_"; + name.append(pair_ref.first); + fpunchf_heading(sformatf("%*s\t", l, name.c_str())); + //strcpy(token, "s_"); + //strcat(token, punch.s_s[i].name); + //fpunchf_heading(sformatf("%*s\t", l, token)); + } + + /* isotopes */ + + //for (i = 0; i < punch.count_isotopes; i++) + for (size_t i = 0; i < current_selected_output->Get_isotopes().size(); i++) + { + std::pair< std::string, void *> &pair_ref = current_selected_output->Get_isotopes()[i]; + if (isotope_ratio_search(pair_ref.first.c_str()) == NULL) + { + error_string = sformatf( + "Did not find isotope_ratio definition for " + "%s in -isotopes of SELECTED_OUTPUT.\n%s must be defined in ISOTOPE_RATIO data block.", + pair_ref.first.c_str(), pair_ref.first.c_str()); + warning_msg(error_string); + } + std::string name = "I_"; + name.append(pair_ref.first); + fpunchf_heading(sformatf("%*s\t", l, name.c_str())); + //if (isotope_ratio_search(punch.isotopes[i].name) == NULL) + //{ + // error_string = sformatf( + // "Did not find isotope_ratio definition for " + // "%s in -isotopes of SELECTED_OUTPUT.\n%s must be defined in ISOTOPE_RATIO data block.", + // punch.isotopes[i].name, punch.isotopes[i].name); + // warning_msg(error_string); + //} + //strcpy(token, "I_"); + //strcat(token, punch.isotopes[i].name); + //fpunchf_heading(sformatf("%*s\t", l, token)); + } + + /* calculate_values */ + + //for (i = 0; i < punch.count_calculate_values; i++) + for (size_t i = 0; i < current_selected_output->Get_calculate_values().size(); i++) + { + std::pair< std::string, void *> &pair_ref = current_selected_output->Get_calculate_values()[i]; + if (calculate_value_search(pair_ref.first.c_str()) == NULL) + { + error_string = sformatf( + "Did not find calculate_values definition for " + "%s in -calculate_values of SELECTED_OUTPUT.\n%s must be defined in CALCULATE_VALUES data block.", + pair_ref.first.c_str(), + pair_ref.first.c_str()); + warning_msg(error_string); + } + std::string name = "V_"; + name.append(pair_ref.first); + fpunchf_heading(sformatf("%*s\t", l, name.c_str())); + //if (calculate_value_search(punch.calculate_values[i].name) == NULL) + //{ + // error_string = sformatf( + // "Did not find calculate_values definition for " + // "%s in -calculate_values of SELECTED_OUTPUT.\n%s must be defined in CALCULATE_VALUES data block.", + // punch.calculate_values[i].name, + // punch.calculate_values[i].name); + // warning_msg(error_string); + //} + //strcpy(token, "V_"); + //strcat(token, punch.calculate_values[i].name); + //fpunchf_heading(sformatf("%*s\t", l, token)); + } + + /* user_punch */ + if (current_user_punch != NULL && current_selected_output->Get_user_punch()) + { + for (size_t i = 0; i < current_user_punch->Get_headings().size(); i++) + { + fpunchf_heading(sformatf("%*s\t", l, current_user_punch->Get_headings()[i].c_str())); + } + } + fpunchf_heading("\n"); + //if (punch.user_punch == TRUE) + //{ + // for (i = 0; i < user_punch_count_headings; i++) + // { + // fpunchf_heading(sformatf("%*s\t", l, user_punch_headings[i])); + // } + //} + //fpunchf_heading("\n"); + + current_selected_output->Set_new_def(false); + pr.punch = punch_save; + phrq_io->Set_punch_on(pr.punch == TRUE); + + punch_flush(); + } + + current_selected_output = NULL; + current_user_punch = NULL; + phrq_io->Set_punch_ostream(NULL); + return (OK); +} +#ifdef SKIP +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_punch(void) +/* ---------------------------------------------------------------------- */ +{ + int i, j, l; + int punch_save; + char token[MAX_LENGTH]; +/* + * tidy punch information + */ + if (punch.high_precision == FALSE) + { + l = 12; + } + else + { + l = 20; + } + if (punch.in == TRUE) + { + /* totals */ + + for (i = 0; i < punch.count_totals; i++) + { + punch.totals[i].master = master_bsearch(punch.totals[i].name); + } + + /* molalities */ + + for (i = 0; i < punch.count_molalities; i++) + { + punch.molalities[i].s = s_search(punch.molalities[i].name); + } + + /* log activities */ + + for (i = 0; i < punch.count_activities; i++) + { + punch.activities[i].s = s_search(punch.activities[i].name); + } + + /* equilibrium phases */ + + for (i = 0; i < punch.count_pure_phases; i++) + { + punch.pure_phases[i].phase = + phase_bsearch(punch.pure_phases[i].name, &j, FALSE); + } + + /* saturation indices */ + + for (i = 0; i < punch.count_si; i++) + { + punch.si[i].phase = phase_bsearch(punch.si[i].name, &j, FALSE); + } + + /* gases */ + + for (i = 0; i < punch.count_gases; i++) + { + punch.gases[i].phase = + phase_bsearch(punch.gases[i].name, &j, FALSE); + } + } + /* + * Always write new headings when SELECTED_OUTPUT is read + */ + if (punch.new_def == TRUE && punch.in == TRUE) + { + punch_save = pr.punch; + pr.punch = TRUE; + phrq_io->Set_punch_on(true); + + /* constant stuff, sim, pH, etc. */ + + if (punch.sim == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "sim")); + } + if (punch.state == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "state")); + } + if (punch.soln == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "soln")); + } + if (punch.dist == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "dist_x")); + } + if (punch.time == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "time")); + } + if (punch.step == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "step")); + } + if (punch.ph == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "pH")); + } + if (punch.pe == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "pe")); + } + if (punch.rxn == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "reaction")); + } + if (punch.temp == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "temp")); + } + if (punch.alk == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "Alk")); + } + if (punch.mu == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "mu")); + } + if (punch.water == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "mass_H2O")); + } + if (punch.charge_balance == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "charge")); + } + if (punch.percent_error == TRUE) + { + fpunchf_heading(sformatf("%*s\t", l, "pct_err")); + } + /* totals */ + + for (i = 0; i < punch.count_totals; i++) + { + fpunchf_heading(sformatf("%*s\t", l, punch.totals[i].name)); + if (punch.totals[i].master == NULL) + { + error_string = sformatf( "Did not find master species," + " %s.", punch.totals[i].name); + warning_msg(error_string); + } + } + + /* molalities */ + + for (i = 0; i < punch.count_molalities; i++) + { + strcpy(token, "m_"); + strcat(token, punch.molalities[i].name); + fpunchf_heading(sformatf("%*s\t", l, token)); + if (punch.molalities[i].s == NULL) + { + error_string = sformatf( "Did not find species," + " %s.", punch.molalities[i].name); + warning_msg(error_string); + } + } + + /* log activities */ + + for (i = 0; i < punch.count_activities; i++) + { + strcpy(token, "la_"); + strcat(token, punch.activities[i].name); + fpunchf_heading(sformatf("%*s\t", l, token)); + if (punch.activities[i].s == NULL) + { + error_string = sformatf( "Did not find species, " + "%s.", punch.activities[i].name); + warning_msg(error_string); + } + } + + /* equilibrium phases */ + + for (i = 0; i < punch.count_pure_phases; i++) + { + strcpy(token, "d_"); + strcat(token, punch.pure_phases[i].name); + fpunchf_heading(sformatf("%*s\t", l, punch.pure_phases[i].name)); + fpunchf_heading(sformatf("%*s\t", l, token)); + if (punch.pure_phases[i].phase == NULL) + { + error_string = sformatf( "Did not find phase, " + "%s.", punch.pure_phases[i].name); + warning_msg(error_string); + } + } + + /* saturation indices */ + + for (i = 0; i < punch.count_si; i++) + { + strcpy(token, "si_"); + strcat(token, punch.si[i].name); + fpunchf_heading(sformatf("%*s\t", l, token)); + if (punch.si[i].phase == NULL) + { + error_string = sformatf( "Did not find phase, " + "%s.", punch.si[i].name); + warning_msg(error_string); + } + } + + /* gases */ + + if (punch.count_gases > 0) + { + fpunchf_heading(sformatf("%*s\t", l, "pressure")); + fpunchf_heading(sformatf("%*s\t", l, "total mol")); + fpunchf_heading(sformatf("%*s\t", l, "volume")); + } + for (i = 0; i < punch.count_gases; i++) + { + strcpy(token, "g_"); + strcat(token, punch.gases[i].name); + fpunchf_heading(sformatf("%*s\t", l, token)); + if (punch.gases[i].phase == NULL) + { + error_string = sformatf( "Did not find phase, " + "%s.", punch.gases[i].name); + warning_msg(error_string); + } + } + + /* kinetics */ + + for (i = 0; i < punch.count_kinetics; i++) + { + strcpy(token, "k_"); + strcat(token, punch.kinetics[i].name); + fpunchf_heading(sformatf("%*s\t", l, token)); + strcpy(token, "dk_"); + strcat(token, punch.kinetics[i].name); + fpunchf_heading(sformatf("%*s\t", l, token)); + } + + /* solid solutions */ + + for (i = 0; i < punch.count_s_s; i++) + { + strcpy(token, "s_"); + strcat(token, punch.s_s[i].name); + fpunchf_heading(sformatf("%*s\t", l, token)); + } + + /* isotopes */ + + for (i = 0; i < punch.count_isotopes; i++) + { + if (isotope_ratio_search(punch.isotopes[i].name) == NULL) + { + error_string = sformatf( + "Did not find isotope_ratio definition for " + "%s in -isotopes of SELECTED_OUTPUT.\n%s must be defined in ISOTOPE_RATIO data block.", + punch.isotopes[i].name, punch.isotopes[i].name); + warning_msg(error_string); + } + strcpy(token, "I_"); + strcat(token, punch.isotopes[i].name); + fpunchf_heading(sformatf("%*s\t", l, token)); + } + + /* calculate_values */ + + for (i = 0; i < punch.count_calculate_values; i++) + { + if (calculate_value_search(punch.calculate_values[i].name) == NULL) + { + error_string = sformatf( + "Did not find calculate_values definition for " + "%s in -calculate_values of SELECTED_OUTPUT.\n%s must be defined in CALCULATE_VALUES data block.", + punch.calculate_values[i].name, + punch.calculate_values[i].name); + warning_msg(error_string); + } + strcpy(token, "V_"); + strcat(token, punch.calculate_values[i].name); + fpunchf_heading(sformatf("%*s\t", l, token)); + } + + /* user_punch */ + if (punch.user_punch == TRUE) + { + for (i = 0; i < user_punch_count_headings; i++) + { + fpunchf_heading(sformatf("%*s\t", l, user_punch_headings[i])); + } + } + fpunchf_heading("\n"); + + punch.new_def = FALSE; + pr.punch = punch_save; + phrq_io->Set_punch_on(pr.punch == TRUE); + } + punch_flush(); + return (OK); +} +#endif +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_species(void) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + struct master *master_ptr; + char c, *ptr; +/* + * Make sure species pointers are ok + */ + if (check_species_input() == ERROR) + { + error_msg("Calculations terminating due to input errors.", STOP); + } +/* + * Set secondary and primary pointers in species structures + */ + for (i = 0; i < count_s; i++) + { + s[i]->number = i; + s[i]->primary = NULL; + s[i]->secondary = NULL; + if (s[i]->check_equation == TRUE) + { + species_rxn_to_trxn(s[i]); + if (check_eqn(TRUE) == ERROR) + { + input_error++; + error_string = sformatf( + "Equation for species %s does not balance.", + s[i]->name); + error_msg(error_string, CONTINUE); + } + } + } + for (i = 0; i < count_master; i++) + { + char * temp_name = string_duplicate(master[i]->elt->name); + ptr = temp_name; + if (ptr[0] != '[') + { + while ((c = (int) *(++ptr)) != '\0') + { + if (isupper((int) c)) + { + input_error++; + error_string = sformatf( + "Element or valence name in SOLUTION_MASTER_SPECIES should include only one element, %s.", + master[i]->elt->name); + error_msg(error_string, CONTINUE); + break; + } + } + } + free_check_null(temp_name); + /* store sequence number in master structure */ + master[i]->number = i; + if (strcmp(master[i]->elt->name, "Alkalinity") != 0) + { + if (master[i]->primary == TRUE) + { + master[i]->s->primary = master[i]; + } + else + { + master[i]->s->secondary = master[i]; + } + } + if (strcmp(master[i]->elt->name, "C") == 0) + { + s_co3 = master[i]->s; + } + if (master[i]->gfw_formula != NULL) + { + if (compute_gfw(master[i]->gfw_formula, &master[i]->gfw) == ERROR) + { + input_error++; + error_string = sformatf( + "Calculating gfw for master species, %s, formula %s.", + master[i]->elt->name, master[i]->gfw_formula); + error_msg(error_string, CONTINUE); + } + } + } +/* + * Write equations for all master species in terms of primary + * master species, set coefficient of element in master species + */ + for (i = 0; i < count_master; i++) + { + count_trxn = 0; + if (master[i]->s->primary != NULL) + { + trxn_add(master[i]->s->rxn, 1.0, FALSE); + trxn_add(master[i]->s->rxn, -1.0, TRUE); + } + else + { + trxn_add(master[i]->s->rxn, 1.0, FALSE); + rewrite_eqn_to_primary(); + } + rxn_free(master[i]->rxn_primary); + master[i]->rxn_primary = rxn_alloc(count_trxn + 1); + trxn_copy(master[i]->rxn_primary); + master[i]->coef = coef_in_master(master[i]); + } +/* + * Rewrite all species to secondary species + */ + for (i = 0; i < count_s; i++) + { + count_trxn = 0; + if (s[i]->primary != NULL || s[i]->secondary != NULL) + { + trxn_add(s[i]->rxn, 1.0, FALSE); + trxn_add(s[i]->rxn, -1.0, TRUE); + } + else + { + trxn_add(s[i]->rxn, 1.0, FALSE); + rewrite_eqn_to_secondary(); + } + rxn_free(s[i]->rxn_s); + s[i]->rxn_s = rxn_alloc(count_trxn + 1); + trxn_copy(s[i]->rxn_s); + /* calculate alkalinity */ + s[i]->alk = calc_alk(s[i]->rxn_s); + /* set co2 coefficient */ + s[i]->co2 = 0.0; + for (j = 1; j < count_trxn; j++) + { + if (trxn.token[j].s == s_co3) + { + s[i]->co2 = trxn.token[j].coef; + break; + } + } + } +/* + * Set pointer in element to master species + */ + for (i = 0; i < count_elements; i++) + { + elements[i]->master = master_bsearch(elements[i]->name); + if (elements[i]->master == NULL) + { + input_error++; + error_string = sformatf( "No master species for element %s.", + elements[i]->name); + error_msg(error_string, CONTINUE); + } + elements[i]->primary = master_bsearch_primary(elements[i]->name); + if (elements[i]->primary == NULL) + { + input_error++; + error_string = sformatf( "No master species for element %s.", + elements[i]->name); + error_msg(error_string, CONTINUE); + } + } +/* + * Make sure all primary master species for redox elements + * are also secondary master species + */ + for (i = 0; i < count_master; i++) + { + if (master[i]->primary == FALSE) + { + master_ptr = master[i]->s->secondary->elt->primary; + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Every primary master species for a redox element\n" + "\tmust also be a secondary master species.\n" + "\tError in definitions related to %s .\n", + master[i]->s->name); + error_msg(error_string, CONTINUE); + + } + else if (master_ptr->s->secondary == NULL) + { + input_error++; + error_string = sformatf( + "Every primary master species for a redox element\n" + "\tmust also be a secondary master species.\n" + "\t%s is the primary master species for element %s.\n" + "\tAnother entry in SOLUTION_MASTER_SPECIES is needed.\n" + "\tDefine species %s as a secondary master species for a valence state.\n" + "\tFor example: \n" "\t%s(0)\t%s alk gfw", + master_ptr->s->name, master_ptr->elt->name, + master_ptr->s->name, master_ptr->elt->name, + master_ptr->s->name); + error_msg(error_string, CONTINUE); + } + } + } +/* + * Calculate H and O if alternate mass balance is given + */ + for (i = 0; i < count_s; i++) + { + if (s[i]->next_secondary != NULL) + { + s[i]->h = 0.0; + s[i]->o = 0.0; + for (j = 0; s[i]->next_secondary[j].elt != NULL; j++) + { + if (s[i]->next_secondary[j].elt->primary == NULL) + continue; + if (s[i]->next_secondary[j].elt->primary->s == s_hplus || s[i]->next_secondary[j].elt->primary->s == s_h3oplus) + { + s[i]->h += s[i]->next_secondary[j].coef; + } + else if (s[i]->next_secondary[j].elt->primary->s == s_h2o) + { + s[i]->o += s[i]->next_secondary[j].coef; + } + else if (s[i]->mole_balance != NULL) + { + master_ptr = s[i]->next_secondary[j].elt->master; + if (master_ptr != NULL) + { + if (master_ptr->primary == TRUE) + { + if (master_ptr->s->secondary != NULL) + { + master_ptr = master_ptr->s->secondary; + } + } + } + else + { + input_error++; + error_string = sformatf( + "Element in -mole_balance %s not defined for species %s.\n", s[i]->mole_balance, s[i]->name); + error_msg(error_string, CONTINUE); + continue; + } + if (master_ptr->coef != 1) + { + s[i]->next_secondary[j].coef /= master_ptr->coef; + } + } + } + if (s[i]->type == EX) + { + for (j = 0; s[i]->next_secondary[j].elt != NULL; j++) + { + if (s[i]->next_secondary[j].elt->primary->s->type == EX) + { + s[i]->equiv = s[i]->next_secondary[j].coef; + break; + } + } + } + } + if (s[i]->type == EX) + { +/* + * Find valence of cation from coefficients of reaction components + * Changed to be coefficient of exchanger + */ + LDBLE exchange_coef = 0.0; + for (j = 1; s[i]->rxn_s->token[j].s != NULL; j++) + { + if (s[i]->rxn_s->token[j].s->type == EX) + { + exchange_coef = s[i]->rxn_s->token[j].coef; + break; + } + } + if (exchange_coef == 0.0) + { + input_error++; + error_string = sformatf( + "No exchange species found in equation for %s.\n", s[i]->name); + error_msg(error_string, CONTINUE); + continue; + } + s[i]->equiv = exchange_coef; + } + if (s[i]->type == SURF) + { + LDBLE surface_coef = 0.0; + /* + * Find coefficient of surface in rxn, store in equiv + */ + for (j = 1; s[i]->rxn_s->token[j].s != NULL; j++) + { + if (s[i]->rxn_s->token[j].s->type == SURF) + { + surface_coef = s[i]->rxn_s->token[j].coef; + break; + } + } + if (surface_coef == 0.0) + { + input_error++; + error_string = sformatf( + "No surface species found in equation for %s.\n", s[i]->name); + error_msg(error_string, CONTINUE); + continue; + } + s[i]->equiv = surface_coef; + } + } + + for (i = 0; i < count_master; i++) + { + if (master[i]->gfw <= 0.0) + { + if (master[i]->type >= EMINUS) continue; + if ((strcmp(master[i]->elt->name, "E") != 0) && + (strcmp(master[i]->elt->name, "e") != 0) && + (strcmp(master[i]->elt->name, "H(1)") != 0) && + (strcmp(master[i]->elt->name, "O(-2)") != 0) + ) + { + input_error++; + error_string = sformatf( + "Gram formula wt in SOLUTION_MASTER_SPECIES should not be <= 0.0, %s.\n", master[i]->elt->name); + error_msg(error_string, CONTINUE); + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_surface(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * After all of data are read, fill in master species for surface comps + * Sort surface + */ + char *ptr1; + cxxSurface *surface_ptr; + //std::map::iterator kit; + //for (kit = Rxn_surface_map.begin(); kit != Rxn_surface_map.end(); kit++) + //{ + //for (size_t nn = 0; nn < Rxn_new_surface.size(); nn++) + //{ + //std::map::iterator kit = Rxn_surface_map.find(Rxn_new_surface[nn]); + for (std::set::const_iterator nit = Rxn_new_surface.begin(); nit != Rxn_new_surface.end(); nit++) + { + std::map::iterator kit = Rxn_surface_map.find(*nit); + if (kit == Rxn_surface_map.end()) + { + assert(false); + } + //if (!kit->second.Get_new_def()) continue; + surface_ptr = &(kit->second); + // ccm incompatible with Donnan or diffuse_layer + if (surface_ptr->Get_type() == cxxSurface::CCM) + { + if (surface_ptr->Get_dl_type() == cxxSurface::BORKOVEK_DL || surface_ptr->Get_dl_type() == cxxSurface::DONNAN_DL) + { + input_error++; + error_string = "Cannot use -diffuse_layer or -donnan calculation with Constant Capacity Model."; + error_msg(error_string, CONTINUE); + continue; + } + } + for (size_t i = 0; i < surface_ptr->Get_surface_comps().size(); i++) + { + cxxSurfaceComp *comp_ptr = &(surface_ptr->Get_surface_comps()[i]); +/* + * Find master species for each surface + */ + cxxNameDouble::iterator jit = comp_ptr->Get_totals().begin(); + for ( ; jit != comp_ptr->Get_totals().end(); jit++ ) + { + struct element *elt_ptr = element_store(jit->first.c_str()); + struct master *master_ptr = elt_ptr->master; + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Master species not in database for %s, " + "skipping element.", + elt_ptr->name); + error_msg(error_string, CONTINUE); + continue; + } + if (master_ptr->type != SURF) + continue; + comp_ptr->Set_master_element(elt_ptr->name); +/* + * Set flags + */ + cxxSurfaceCharge *charge_ptr = surface_ptr->Find_charge(comp_ptr->Get_charge_name()); + /* + * Calculate moles of sites + */ + if (surface_ptr->Get_new_def() + && surface_ptr->Get_sites_units() == cxxSurface::SITES_DENSITY + && comp_ptr->Get_phase_name().size() == 0) + { + if (charge_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Surface type is incompatible with site units for %s.", + comp_ptr->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + comp_ptr->Set_moles( + comp_ptr->Get_moles() * 1.0e18 * + charge_ptr->Get_specific_area() * + charge_ptr->Get_grams() / AVOGADRO); + /* + * Calculate totals + */ + count_elts = 0; + paren_count = 0; + { + char * temp_formula = string_duplicate(comp_ptr->Get_formula().c_str()); + ptr1 = temp_formula; + get_elts_in_species(&ptr1, comp_ptr->Get_moles()); + free_check_null(temp_formula); + } + { + cxxNameDouble nd = elt_list_NameDouble(); + comp_ptr->Set_totals(nd); + } + } + if (surface_ptr->Get_type() == cxxSurface::CD_MUSIC) + { + charge_ptr->Set_charge_balance(charge_ptr->Get_charge_balance() + + comp_ptr->Get_moles() * + comp_ptr->Get_formula_z()); + } + break; + } +#ifdef SKIP_MUSIC + /* + * If charge of formula is non zero + */ + if (surface_ptr->type == CD_MUSIC) + { + surface_ptr->comps[i].cb = + surface_ptr->comps[i].formula_z * + surface_ptr->comps[i].moles; + } +#endif + } + /* + * Check that all surface comps have a corresponding master + */ + for (size_t i = 0; i < surface_ptr->Get_surface_comps().size(); i++) + { + if (surface_ptr->Get_surface_comps()[i].Get_master_element().size() == 0) + { + input_error++; + error_string = sformatf( + "No surface master species for surface component %s, ", + surface_ptr->Get_surface_comps()[i].Get_formula().c_str()); + error_msg(error_string, CONTINUE); + } + } +/* + * Sort components + */ + std::map comp_map; + for (size_t i = 0; i < surface_ptr->Get_surface_comps().size(); i++) + { + cxxSurfaceComp *comp_ptr = &(surface_ptr->Get_surface_comps()[i]); + comp_map[comp_ptr->Get_formula()] = *comp_ptr; + } + std::map::iterator it = comp_map.begin(); + surface_ptr->Get_surface_comps().clear(); + for ( ; it != comp_map.end(); it++) + { + surface_ptr->Get_surface_comps().push_back(it->second); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_solutions(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Define n_user for any solutions read by solution_spread that + * don`t have n_user defined + */ + struct master *master_ptr; + + /* + * Calculate solution numbers + */ + if (unnumbered_solutions.size() > 0) + { + int last = 0; + std::map::iterator jit; + for (jit = Rxn_solution_map.begin(); jit != Rxn_solution_map.end(); jit++) + { + if (jit->second.Get_n_user() > last) + last = jit->second.Get_n_user(); + if (jit->second.Get_n_user_end() > last) + last = jit->second.Get_n_user_end(); + } + if (save.solution == TRUE) + { + if (save.n_solution_user > last) + last = save.n_solution_user; + if (save.n_solution_user_end > last) + last = save.n_solution_user_end; + } + // put unnumbered solutions in map + for (size_t i = 0; i < unnumbered_solutions.size(); i++) + { + if (use.Get_n_solution_user() < 0) + { + use.Set_n_solution_user(last + 1); + } + unnumbered_solutions[i].Set_n_user_both(++last); + Rxn_solution_map[last] = unnumbered_solutions[i]; + Rxn_new_solution.insert(last); + } + unnumbered_solutions.clear(); + } + /* + * Check that elements are in database + */ + //std::map::iterator it; + //for (it = Rxn_solution_map.begin(); it != Rxn_solution_map.end(); it++) + //for(size_t n = 0; n < Rxn_new_solution.size(); n++) + //{ + //std::map::iterator it = Rxn_solution_map.find(Rxn_new_solution[n]); + for (std::set::const_iterator nit = Rxn_new_solution.begin(); nit != Rxn_new_solution.end(); nit++) + { + std::map::iterator it = Rxn_solution_map.find(*nit); + if (it == Rxn_solution_map.end()) + { + assert(false); + continue; + } + cxxSolution &solution_ref = it->second; + //if (solution_ref.Get_new_def()) + { + cxxISolution *initial_data_ptr = solution_ref.Get_initial_data(); + if (initial_data_ptr != NULL) + { + std::map::iterator iit = initial_data_ptr->Get_comps().begin(); + for ( ; iit != initial_data_ptr->Get_comps().end(); iit++) + { + cxxISolutionComp &comp_ref = iit->second; + if (strcmp(comp_ref.Get_description().c_str(), "H(1)") == 0 || + strcmp(comp_ref.Get_description().c_str(), "E") == 0) + { + comp_ref.Set_moles(0.0); + continue; + } + std::string token; + std::string description = comp_ref.Get_description(); + std::string::iterator b = description.begin(); + std::string::iterator e = description.end(); + CParser::copy_token(token, b, e); + + master_ptr = master_bsearch(token.c_str()); + if (master_ptr == NULL) + { + error_string = sformatf( + "Could not find element in database, %s.\n\tConcentration is set to zero.", + comp_ref.Get_description().c_str()); + warning_msg(error_string); + comp_ref.Set_input_conc(0.0); + continue; + } + } + } + } + } + + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +species_rxn_to_trxn(struct species *s_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Copy reaction from reaction structure to + * temp reaction structure. + */ + int i; + + for (i = 0; s_ptr->rxn->token[i].s != NULL; i++) + { + trxn.token[i].name = s_ptr->rxn->token[i].s->name; + trxn.token[i].z = s_ptr->rxn->token[i].s->z; + trxn.token[i].s = s_ptr->rxn->token[i].s; + trxn.token[i].unknown = NULL; + trxn.token[i].coef = s_ptr->rxn->token[i].coef; + count_trxn = i + 1; + if (count_trxn + 1 >= max_trxn) + { + space((void **) ((void *) &(trxn.token)), count_trxn + 1, + &max_trxn, sizeof(struct rxn_token_temp)); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +phase_rxn_to_trxn(struct phase *phase_ptr, struct reaction *rxn_ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Copy reaction from reaction structure to + * temp reaction structure. + */ + int i, l; + char *ptr; + char token[MAX_LENGTH]; + LDBLE l_z; + + trxn.token[0].name = phase_ptr->formula; + /* charge */ + char * temp_formula = string_duplicate(phase_ptr->formula); + ptr = temp_formula; + get_token(&ptr, token, &l_z, &l); + free_check_null(temp_formula); + trxn.token[0].z = l_z; + trxn.token[0].s = NULL; + trxn.token[0].unknown = NULL; + /*trxn.token[0].coef = -1.0; */ + /* check for leading coefficient of 1.0 for phase did not work */ + trxn.token[0].coef = phase_ptr->rxn->token[0].coef; + for (i = 1; rxn_ptr->token[i].s != NULL; i++) + { + trxn.token[i].name = rxn_ptr->token[i].s->name; + trxn.token[i].z = rxn_ptr->token[i].s->z; + trxn.token[i].s = NULL; + trxn.token[i].unknown = NULL; + trxn.token[i].coef = rxn_ptr->token[i].coef; + count_trxn = i + 1; + if (count_trxn + 1 >= max_trxn) + { + space((void **) ((void *) &(trxn.token)), count_trxn + 1, + &max_trxn, sizeof(struct rxn_token_temp)); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_isotopes(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Isotope ratios for each element or element valence state + */ + LDBLE isotope_number; + struct master *master_ptr, *primary_ptr; + + int primary_number = 0; + primary_ptr = NULL; + std::map::iterator it; + for (it = Rxn_solution_map.begin(); it != Rxn_solution_map.end(); it++) + { + std::map new_isotopes; + cxxSolution &solution_ref = it->second; + if (!solution_ref.Get_new_def()) + continue; + if (solution_ref.Get_isotopes().size() == 0) + continue; + + std::map primary_isotopes; +/* + * Make list of primary master species for isotopes + */ + std::map < std::string, cxxSolutionIsotope >::iterator kit = solution_ref.Get_isotopes().begin(); + for ( ; kit != solution_ref.Get_isotopes().end(); kit++) + { + cxxSolutionIsotope &isotope_ref = kit->second; + master_ptr = master_bsearch_primary(isotope_ref.Get_elt_name().c_str()); + isotope_number = isotope_ref.Get_isotope_number(); + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( + "In isotope calculation: element not defined: %s.", + isotope_ref.Get_elt_name().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + std::ostringstream iso_name_str; + iso_name_str << (int) isotope_number << master_ptr->elt->name; + + std::map < std::string, cxxSolutionIsotope >::iterator jit; + jit = primary_isotopes.find(iso_name_str.str().c_str()); + if (jit == primary_isotopes.end()) + { + cxxSolutionIsotope temp_isotope; + temp_isotope.Set_isotope_name(iso_name_str.str().c_str()); + temp_isotope.Set_elt_name(master_ptr->elt->name); + temp_isotope.Set_isotope_number(isotope_number); + primary_isotopes[iso_name_str.str().c_str()] = temp_isotope; + } + } + if (get_input_errors() > 0) + return (ERROR); +/* + * Go through all redox states of the list of primary species and isotope number + */ + for (kit = primary_isotopes.begin(); kit != primary_isotopes.end(); kit++) + { + /* find index number of master species, set flag to FALSE */ + master_ptr = master_bsearch(kit->second.Get_elt_name().c_str()); + isotope_number = kit->second.Get_isotope_number(); + for (int k = 0; k < count_master; k++) + { + master[k]->isotope = FALSE; + } + primary_number = master_ptr->number; + primary_ptr = master_ptr; + + /* go through isotopes of solution and fill in master species */ + std::map < std::string, cxxSolutionIsotope >::iterator lit = solution_ref.Get_isotopes().begin(); + for ( ; lit != solution_ref.Get_isotopes().end(); lit++) + { + master_ptr = master_bsearch(lit->second.Get_elt_name().c_str()); + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( + "In isotope calculation: element not defined: %s.", + lit->second.Get_elt_name().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + + /* only fill for pertinent isotope */ + if (master_ptr->elt->primary != primary_ptr) + continue; + if (lit->second.Get_isotope_number() != isotope_number) + continue; + + /* for primary, fill in ratio for all secondary species */ + if (master_ptr->primary == TRUE && master_ptr->s->secondary != NULL) + { + for (int k = primary_number + 1; k < count_master; k++) + { + if (master[k]->elt->primary != primary_ptr) + break; + master[k]->isotope_ratio = lit->second.Get_ratio(); + master[k]->isotope_ratio_uncertainty = lit->second.Get_ratio_uncertainty(); + if (master[k]->isotope == TRUE) + { + error_string = sformatf( + "In isotope calculation: redefinition of isotope ratio for %s.", + lit->second.Get_elt_name().c_str()); + error_msg(error_string, CONTINUE); + } + master[k]->isotope = TRUE; + } + } + /* for secondary and non redox, set ratio */ + else + { + master_ptr->isotope_ratio = lit->second.Get_ratio(); + master_ptr->isotope_ratio_uncertainty = lit->second.Get_ratio_uncertainty(); + if (master_ptr->isotope == TRUE) + { + error_string = sformatf( + "In isotope calculation: redefinition of isotope ratio for %s.", + lit->second.Get_elt_name().c_str()); + error_msg(error_string, CONTINUE); + } + master_ptr->isotope = TRUE; + } + } +/* + * Write new isotope structure + */ + for (int k = 0; k < count_master; k++) + { + /* skip primary master species of redox elements */ + if (master[k]->primary == TRUE && master[k]->s->secondary != NULL) + continue; + if (master[k]->elt->primary == primary_ptr && master[k]->isotope == FALSE) + { + input_error++; + error_string = sformatf( + "Isotopic ratio not defined for element or valence state %g%s, using 0.", + (double) isotope_number, master[k]->elt->name); + warning_msg(error_string); + master[k]->isotope = TRUE; + master[k]->isotope_ratio = 0.0; + master[k]->isotope_ratio_uncertainty = 0.001; + } + if (master[k]->isotope == FALSE) + continue; + cxxSolutionIsotope temp_iso; + temp_iso.Set_isotope_number(isotope_number); + temp_iso.Set_elt_name(master[k]->elt->name); + temp_iso.Set_total(0); + temp_iso.Set_ratio(master[k]->isotope_ratio); + temp_iso.Set_ratio_uncertainty(master[k]->isotope_ratio_uncertainty); +#ifdef NPP + if (!isnan(master[k]->isotope_ratio_uncertainty)) +#else + if (master[k]->isotope_ratio_uncertainty != NAN) +#endif + { + temp_iso.Set_ratio_uncertainty_defined(true); + } + std::string token = sformatf("%d%s", (int) isotope_number, + master[k]->elt->name); + temp_iso.Set_isotope_name(token.c_str()); + new_isotopes[token] = temp_iso; + } + } + solution_ref.Set_isotopes(new_isotopes); + } + + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_kin_exchange(void) +/* ---------------------------------------------------------------------- */ +/* + * If exchanger is related to mineral, exchanger amount is + * set in proportion + */ +{ + cxxKinetics *kinetics_ptr; + char *ptr; + LDBLE conc; + + //std::map::iterator it = Rxn_exchange_map.begin(); + //for ( ; it != Rxn_exchange_map.end(); it++) + //for (size_t nn = 0; nn < Rxn_new_exchange.size(); nn++) + //{ + //std::map::iterator it = Rxn_exchange_map.find(Rxn_new_exchange[nn]); + for (std::set::const_iterator nit = Rxn_new_exchange.begin(); nit != Rxn_new_exchange.end(); nit++) + { + std::map::iterator it = Rxn_exchange_map.find(*nit); + if (it == Rxn_exchange_map.end()) + { + assert(false); + } + cxxExchange * exchange_ptr = &(it->second); + //if (!exchange_ptr->Get_new_def()) + // continue; + //if (exchange_ptr->Get_n_user() < 0) + // continue; + // check elements + for (size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++) + { + cxxExchComp & comp_ref = exchange_ptr->Get_exchange_comps()[j]; + if (comp_ref.Get_rate_name().size() == 0) + continue; + /* First find exchange master species */ + cxxNameDouble nd = comp_ref.Get_totals(); + cxxNameDouble::iterator kit = nd.begin(); + bool found_exchange = false; + for (; kit != nd.end(); kit++) + { + /* Find master species */ + struct element *elt_ptr = element_store(kit->first.c_str()); + if (elt_ptr == NULL || elt_ptr->master == NULL) + { + input_error++; + error_string = sformatf( "Master species not in database " + "for %s, skipping element.", + kit->first.c_str()); + error_msg(error_string, CONTINUE); + continue; + } + if (elt_ptr->master->type == EX) + found_exchange = true;; + } + if (!found_exchange) + { + input_error++; + error_string = sformatf( + "Exchange formula does not contain an exchange master species, %s", + comp_ref.Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + + /* Now find associated kinetic reaction ... */ + if ((kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, exchange_ptr->Get_n_user())) == NULL) + { + input_error++; + error_string = sformatf( + "Kinetics %d must be defined to use exchange related to kinetic reaction, %s", + exchange_ptr->Get_n_user(), comp_ref.Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + size_t k; + for (k = 0; k < kinetics_ptr->Get_kinetics_comps().size(); k++) + { + if (strcmp_nocase + (comp_ref.Get_rate_name().c_str(), + kinetics_ptr->Get_kinetics_comps()[k].Get_rate_name().c_str()) == 0) + { + break; + } + } + if (k == kinetics_ptr->Get_kinetics_comps().size()) + { + input_error++; + error_string = sformatf( + "Kinetic reaction, %s, related to exchanger, %s, not found in KINETICS %d", + comp_ref.Get_rate_name().c_str(), comp_ref.Get_formula().c_str(), exchange_ptr->Get_n_user()); + error_msg(error_string, CONTINUE); + continue; + } + + /* use database name for phase */ + comp_ref.Set_rate_name(kinetics_ptr->Get_kinetics_comps()[k].Get_rate_name().c_str()); + + /* make exchanger concentration proportional to mineral ... */ + conc = kinetics_ptr->Get_kinetics_comps()[k].Get_m() * comp_ref.Get_phase_proportion(); + + count_elts = 0; + paren_count = 0; + { + char * temp_formula = string_duplicate(comp_ref.Get_formula().c_str()); + ptr = temp_formula; + get_elts_in_species(&ptr, conc); + free_check_null(temp_formula); + } + comp_ref.Set_totals(elt_list_NameDouble()); +/* + * No check on availability of exchange elements + */ + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_min_exchange(void) +/* ---------------------------------------------------------------------- */ +/* + * If exchanger is related to mineral, exchanger amount is + * set in proportion + */ +{ + int n, jj; + char *ptr; + LDBLE conc; + + //std::map::iterator it = Rxn_exchange_map.begin(); + //for ( ; it != Rxn_exchange_map.end(); it++) + //{ + //for (size_t nn = 0; nn < Rxn_new_exchange.size(); nn++) + //{ + //std::map::iterator it = Rxn_exchange_map.find(Rxn_new_exchange[nn]); + for (std::set::const_iterator nit = Rxn_new_exchange.begin(); nit != Rxn_new_exchange.end(); nit++) + { + std::map::iterator it = Rxn_exchange_map.find(*nit); + if (it == Rxn_exchange_map.end()) + { + assert(false); + } + cxxExchange * exchange_ptr = &(it->second); + //if (!exchange_ptr->Get_new_def()) + // continue; + //if (exchange_ptr->Get_n_user() < 0) + // continue; + n = exchange_ptr->Get_n_user(); + // check elements + for (size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++) + { + cxxExchComp & comp_ref = exchange_ptr->Get_exchange_comps()[j]; + if (comp_ref.Get_phase_name().size() == 0) + continue; + /* First find exchange master species */ + cxxNameDouble nd = comp_ref.Get_totals(); + cxxNameDouble::iterator kit = nd.begin(); + bool found_exchange = false; + for (; kit != nd.end(); kit++) + { + /* Find master species */ + struct element *elt_ptr = element_store(kit->first.c_str()); + if (elt_ptr == NULL || elt_ptr->master == NULL) + { + input_error++; + error_string = sformatf( "Master species not in database " + "for %s, skipping element.", + kit->first.c_str()); + error_msg(error_string, CONTINUE); + continue; + } + if (elt_ptr->master->type == EX) + { + found_exchange = true;; + } + } + if (!found_exchange) + { + input_error++; + error_string = sformatf( + "Exchange formula does not contain an exchange master species, %s", + comp_ref.Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + cxxPPassemblage *pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, n); + /* Now find the mineral on which exchanger depends... */ + if (pp_assemblage_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Equilibrium_phases %d must be defined to use exchange related to mineral phase, %s", + n, comp_ref.Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + std::map::iterator jit; + jit = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; jit != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); jit++) + { + if (strcmp_nocase(comp_ref.Get_phase_name().c_str(), jit->first.c_str()) == 0) + { + break; + } + } + if (jit == pp_assemblage_ptr->Get_pp_assemblage_comps().end() ) + { + input_error++; + error_string = sformatf( + "Mineral, %s, related to exchanger, %s, not found in Equilibrium_Phases %d", + comp_ref.Get_phase_name().c_str(), comp_ref.Get_formula().c_str(), n); + error_msg(error_string, CONTINUE); + continue; + } + /* use database name for phase */ + comp_ref.Set_phase_name(jit->first.c_str()); + /* make exchanger concentration proportional to mineral ... */ + conc = jit->second.Get_moles() * comp_ref.Get_phase_proportion(); + count_elts = 0; + paren_count = 0; + { + char * temp_formula = string_duplicate(comp_ref.Get_formula().c_str()); + ptr = temp_formula; + get_elts_in_species(&ptr, conc); + free_check_null(temp_formula); + } + comp_ref.Set_totals(elt_list_NameDouble()); +/* + * make sure exchange elements are in phase + */ + count_elts = 0; + paren_count = 0; + { + char * temp_formula = string_duplicate(comp_ref.Get_formula().c_str()); + ptr = temp_formula; + get_elts_in_species(&ptr, -comp_ref.Get_phase_proportion()); + free_check_null(temp_formula); + } + int l; + struct phase *phase_ptr = phase_bsearch(jit->first.c_str(), &l, FALSE); + if (phase_ptr != NULL) + { + char * temp_formula = string_duplicate(phase_ptr->formula); + ptr = temp_formula; + get_elts_in_species(&ptr, 1.0); + free_check_null(temp_formula); + } + else + { + input_error++; + error_string = sformatf( + "Mineral, %s, related to exchanger, %s, not found in Equilibrium_Phases %d", + comp_ref.Get_phase_name().c_str(), comp_ref.Get_formula().c_str(), n); + error_msg(error_string, CONTINUE); + continue; + } + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + for (jj = 0; jj < count_elts; jj++) + { + if (elt_list[jj].elt->primary->s->type != EX + && elt_list[jj].coef < 0) + { + input_error++; + error_string = sformatf( + "Stoichiometry of exchanger, %s * %g mol sites/mol phase,\n\tmust be a subset of the related phase %s, %s.", + comp_ref.Get_formula().c_str(), + (double) comp_ref.Get_phase_proportion(), + phase_ptr->name, + phase_ptr->formula); + error_msg(error_string, CONTINUE); + break; + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_min_surface(void) +/* ---------------------------------------------------------------------- */ +/* + * If surface is related to mineral, surface amount is + * set in proportion + */ +{ + //std::map::iterator kit; + //for (kit = Rxn_surface_map.begin(); kit != Rxn_surface_map.end(); kit++) + //{ + //for (size_t nn = 0; nn < Rxn_new_surface.size(); nn++) + //{ + // std::map::iterator kit = Rxn_surface_map.find(Rxn_new_surface[nn]); + for (std::set::const_iterator nit = Rxn_new_surface.begin(); nit != Rxn_new_surface.end(); nit++) + { + std::map::iterator kit = Rxn_surface_map.find(*nit); + if (kit == Rxn_surface_map.end()) + { + assert(false); + } + cxxSurface *surface_ptr = &(kit->second); + if (!surface_ptr->Get_new_def()) continue; + if (!surface_ptr->Get_new_def()) + continue; + if (surface_ptr->Get_n_user() < 0) + continue; + for (size_t j = 0; j < surface_ptr->Get_surface_comps().size(); j++) + { + cxxSurfaceComp *surface_comp_ptr = &(surface_ptr->Get_surface_comps()[j]); + cxxSurfaceCharge *surface_charge_ptr = surface_ptr->Find_charge(surface_comp_ptr->Get_charge_name()); + if (surface_comp_ptr->Get_phase_name().size() == 0) + continue; + int n = surface_ptr->Get_n_user(); + + /* First find surface master species */ + + cxxNameDouble::iterator it; + for (it = surface_comp_ptr->Get_totals().begin(); it != surface_comp_ptr->Get_totals().end(); it++) + { + /* Find master species */ + struct element *elt_ptr = element_store(it->first.c_str()); + struct master *master_ptr = elt_ptr->master; + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( "Master species not in database " + "for %s, skipping element.", + elt_ptr->name); + error_msg(error_string, CONTINUE); + continue; + } + if (master_ptr->type != SURF) + continue; + surface_comp_ptr->Set_master_element(elt_ptr->name); + break; + } + if (surface_comp_ptr->Get_master_element().size() == 0) + { + input_error++; + error_string = sformatf( + "Surface formula does not contain a surface master species, %s", + surface_comp_ptr->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + + /* Now find the mineral on which surface depends... */ + cxxPPassemblage * pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, n); + if (pp_assemblage_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Equilibrium_phases %d must be defined to use surface related to mineral phase, %s", + n, surface_comp_ptr->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + std::map::iterator jit; + jit = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for ( ; jit != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); jit++) + { + if (strcmp_nocase(surface_comp_ptr->Get_phase_name().c_str(), + jit->first.c_str()) == 0) + { + break; + } + } + if (jit == pp_assemblage_ptr->Get_pp_assemblage_comps().end()) + { + input_error++; + error_string = sformatf( + "Mineral, %s, related to surface, %s, not found in Equilibrium_Phases %d", + surface_comp_ptr->Get_phase_name().c_str(), surface_comp_ptr->Get_formula().c_str(), n); + error_msg(error_string, CONTINUE); + continue; + } + int l; + struct phase *phase_ptr = phase_bsearch(jit->first.c_str(), &l, FALSE); + if (phase_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Mineral, %s, related to surface, %s, not found in database.", + jit->first.c_str(), surface_comp_ptr->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + /* use database name for phase */ + surface_comp_ptr->Set_phase_name(phase_ptr->name); + /* make surface concentration proportional to mineral ... */ + LDBLE conc = jit->second.Get_moles() * surface_comp_ptr->Get_phase_proportion(); +#ifdef SKIP_MUSIC + comp_ptr->cb = conc * comp_ptr->formula_z; +#endif +/* if (conc < MIN_RELATED_SURFACE) conc = 0.0; */ + { + char * temp_formula = string_duplicate(surface_comp_ptr->Get_formula().c_str()); + char *ptr = temp_formula; + count_elts = 0; + paren_count = 0; + get_elts_in_species(&ptr, conc); + free_check_null(temp_formula); + } + { + if (surface_ptr->Get_new_def()) + { + cxxNameDouble nd = elt_list_NameDouble(); + surface_comp_ptr->Set_totals(nd); + } + else + { + surface_comp_ptr->Get_totals()[surface_comp_ptr->Get_master_element()] = conc; + } + } + + /* area */ + if (surface_charge_ptr) + { + surface_charge_ptr->Set_grams(jit->second.Get_moles()); + } +/* + * make sure surface elements are in phase + * logically necessary for mass balance and to avoid negative concentrations when dissolving phase + */ + count_elts = 0; + paren_count = 0; + { + char * temp_formula = string_duplicate(phase_ptr->formula); + char * ptr = temp_formula; + get_elts_in_species(&ptr, 1.0); + free_check_null(temp_formula); + } + // Revise logic for surface related to mineral + for (size_t jj = 0; jj < surface_ptr->Get_surface_comps().size(); jj++) + { + cxxSurfaceComp *comp_jj_ptr = &(surface_ptr->Get_surface_comps()[jj]); + // Use formula for all types of surfaces + { + char * temp_formula = string_duplicate(comp_jj_ptr->Get_formula().c_str()); + char *ptr = temp_formula; + get_elts_in_species(&ptr, + -comp_jj_ptr->Get_phase_proportion()); + + if (surface_ptr->Get_type() != cxxSurface::CD_MUSIC) + { + + // Warn if not master species and charge balanced + struct element *elt_ptr = element_store(comp_jj_ptr->Get_master_element().c_str()); + if (elt_ptr->master == NULL) + { + input_error++; + error_string = sformatf("Unknown element definition in SURFACE \n\t for surface related to equilibrium_phase: SURFACE %d.", + surface_ptr->Get_n_user()); + error_msg(error_string); + free_check_null(temp_formula); + continue; + } + if (elt_ptr->master->s == NULL || elt_ptr->master->s->name == NULL) + { + input_error++; + error_string = sformatf("Unknown master species definition in SURFACE \n\t for surface related to equilibrium_phase: SURFACE %d.", + surface_ptr->Get_n_user()); + error_msg(error_string); + free_check_null(temp_formula); + continue; + } + if (strcmp(elt_ptr->master->s->name, temp_formula) != 0) + { + error_string = sformatf("Suggest using master species formula in SURFACE \n\t for surface related to equilibrium_phase: %s.", + elt_ptr->master->s->name); + warning_msg(error_string); + } + if (elt_ptr->master->s->z != 0.0) + { + error_string = sformatf( + "Suggest master species of surface, %s, be uncharged for surface related to equilibrium_phase.", + elt_ptr->master->s->name); + warning_msg(error_string); + } + } + free_check_null(temp_formula); + } + } + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + /* Makes no sense: sorbed species need not be in mineral structure... */ + /* But elements that can desorb into solution must be in mineral */ + /* If you precipitate Ca-Mont, and make SurfMg (assuming this is the + formula in SURFACE), where does the Mg come from? + Further, if you precipitate Ca-Mont, make SurfCa, desorb + all the Ca, then dissolve the "Ca-Mont", you must remove SurfCa, or you + will end up with Ca in solution. H and O are excluded */ + for (int jj = 0; jj < count_elts; jj++) + { + if (elt_list[jj].elt->primary->s->type != SURF + && elt_list[jj].coef < 0 + //&& elt_list[jj].elt->primary->s != s_hplus + //&& elt_list[jj].elt->primary->s != s_h2o + ) + { + struct element *elt_ptr = element_store(surface_comp_ptr->Get_master_element().c_str()); + error_string = sformatf( + "Element %s in sum of surface sites,\n" + "\t including %s * %g mol sites/mol phase,\n" + "\t exceeds stoichiometry in the related phase %s, %s.", + elt_list[jj].elt->name, + elt_ptr->master->s->name, + (double) surface_comp_ptr->Get_phase_proportion(), + phase_ptr->name, + phase_ptr->formula); + warning_msg(error_string); + warning_msg("The mismatch in stoichiometry may cause mass-balance errors or unwanted redox reactions."); + break; + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_kin_surface(void) +/* ---------------------------------------------------------------------- */ +/* + * If surface is related to mineral, surface amount is + * set in proportion + */ +{ + cxxKinetics *kinetics_ptr; + struct phase *phase_ptr; + struct elt_list *elt_list_kinetics; + int count_elts_kinetics; + + //std::map::iterator it; + //for (it = Rxn_surface_map.begin(); it != Rxn_surface_map.end(); it++) + //{ + //for (size_t nn = 0; nn < Rxn_new_surface.size(); nn++) + //{ + // std::map::iterator it = Rxn_surface_map.find(Rxn_new_surface[nn]); + for (std::set::const_iterator nit = Rxn_new_surface.begin(); nit != Rxn_new_surface.end(); nit++) + { + std::map::iterator it = Rxn_surface_map.find(*nit); + if (it == Rxn_surface_map.end()) + { + assert(false); + } + cxxSurface *surface_ptr = &(it->second); + if (!surface_ptr->Get_new_def()) + continue; + if (surface_ptr->Get_n_user() < 0) + continue; + int n = surface_ptr->Get_n_user(); + for (size_t j = 0; j < surface_ptr->Get_surface_comps().size(); j++) + { + cxxSurfaceComp *comp_ptr = &(surface_ptr->Get_surface_comps()[j]); + if (comp_ptr->Get_rate_name().size() == 0) + continue; + comp_ptr->Set_master_element(""); + + /* First find surface master species */ + int k; + cxxNameDouble::iterator kit; + for (kit = comp_ptr->Get_totals().begin(); kit != comp_ptr->Get_totals().end(); kit++) + { + /* Find master species */ + struct element *elt_ptr = element_store(kit->first.c_str()); + struct master *master_ptr = elt_ptr->master; + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( "Master species not in database " + "for %s, skipping element.", + elt_ptr->name); + error_msg(error_string, CONTINUE); + continue; + } + if (master_ptr->type != SURF) + continue; + comp_ptr->Set_master_element(elt_ptr->name); + break; + } + if (comp_ptr->Get_master_element().size() == 0) + { + input_error++; + error_string = sformatf( + "Surface formula does not contain a surface master species, %s", + comp_ptr->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + + /* Now find the kinetic reaction on which surface depends... */ + if ((kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, n)) == NULL) + { + input_error++; + error_string = sformatf( + "Kinetics %d must be defined to use surface related to kinetic reaction, %s", + n, comp_ptr->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + for (k = 0; k < (int) kinetics_ptr->Get_kinetics_comps().size(); k++) + { + cxxKineticsComp *kin_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[k]); + if (strcmp_nocase + (comp_ptr->Get_rate_name().c_str(), + kin_comp_ptr->Get_rate_name().c_str()) == 0) + { + break; + } + } + if (k == (int) kinetics_ptr->Get_kinetics_comps().size()) + { + input_error++; + error_string = sformatf( + "Kinetic reaction, %s, related to surface, %s, not found in Kinetics %d", + comp_ptr->Get_rate_name().c_str(), comp_ptr->Get_formula().c_str(), n); + error_msg(error_string, CONTINUE); + continue; + } + cxxKineticsComp *kin_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[k]); + /* use database name for phase */ + comp_ptr->Set_rate_name(kin_comp_ptr->Get_rate_name().c_str()); + + /* make surface concentration proportional to mineral ... */ + LDBLE conc = kin_comp_ptr->Get_m() * comp_ptr->Get_phase_proportion(); + +/* if (conc < MIN_RELATED_SURFACE) conc = 0.0; */ + { + char * temp_formula = string_duplicate(comp_ptr->Get_formula().c_str()); + char *ptr = temp_formula; + count_elts = 0; + paren_count = 0; + get_elts_in_species(&ptr, conc); + free_check_null(temp_formula); + } + { + if (surface_ptr->Get_new_def()) + { + cxxNameDouble nd = elt_list_NameDouble(); + comp_ptr->Set_totals(nd); + } + else + { + comp_ptr->Get_totals()[comp_ptr->Get_master_element()] = conc; + } + } + + /* area */ + + cxxSurfaceCharge *charge_ptr = surface_ptr->Find_charge(comp_ptr->Get_charge_name()); + charge_ptr->Set_grams(kin_comp_ptr->Get_m()); + } +/* + * check on elements + */ + /* Go through each kinetic reaction, add all related surface compositions + * check for negative values + */ + if (!surface_ptr->Get_related_rate()) + continue; + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, n); + if (kinetics_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Error in SURFACE related to KINETICS. "); + error_msg(error_string, CONTINUE); + continue; + } + for (size_t k = 0; k < kinetics_ptr->Get_kinetics_comps().size(); k++) + { + cxxKineticsComp *kin_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[k]); + count_elts = 0; + paren_count = 0; + + /* added in kinetics formula */ + cxxNameDouble::iterator jit = kin_comp_ptr->Get_namecoef().begin(); + for (; jit != kin_comp_ptr->Get_namecoef().end(); jit++) + { + std::string name = jit->first; + LDBLE coef = jit->second; + phase_ptr = NULL; + int jj; + phase_ptr = phase_bsearch(name.c_str(), &jj, FALSE); + if (phase_ptr != NULL) + { + add_elt_list(phase_ptr->next_elt, 1.0); + } + else + { + char * temp_name = string_duplicate(name.c_str()); + char * ptr = temp_name; + get_elts_in_species(&ptr, coef); + free_check_null(temp_name); + } + } + /* save kinetics formula */ + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + } + elt_list_kinetics = elt_list_save(); + count_elts_kinetics = count_elts; + + /* get surface formulas */ + count_elts = 0; + paren_count = 0; + cxxSurfaceComp *comp_ptr_save = NULL; + for (size_t j = 0; j < surface_ptr->Get_surface_comps().size(); j++) + { + cxxSurfaceComp *comp_ptr = &(surface_ptr->Get_surface_comps()[j]); + if (comp_ptr->Get_rate_name().size() == 0) + continue; + comp_ptr_save = comp_ptr; + if (strcmp_nocase + (comp_ptr->Get_rate_name().c_str(), + kin_comp_ptr->Get_rate_name().c_str()) == 0) + { + char * temp_formula = string_duplicate( comp_ptr->Get_formula().c_str()); + char *ptr = temp_formula; + get_elts_in_species(&ptr, -1 * comp_ptr->Get_phase_proportion()); + free_check_null(temp_formula); + } + } + if (count_elts > 0) + { + qsort(elt_list, (size_t) count_elts, + (size_t) sizeof(struct elt_list), elt_list_compare); + elt_list_combine(); + } + for (int j = 0; j < count_elts; j++) + { + if (elt_list[j].elt == NULL) + { + input_error++; + error_string = sformatf( + "Cannot identify elements in kinetics component %s.", + comp_ptr_save->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + if (elt_list[j].elt->primary == NULL ) + { + input_error++; + error_string = sformatf( + "Cannot identify primary element in kinetics component %s.", + comp_ptr_save->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + if (elt_list[j].elt->primary->s == NULL) + { + input_error++; + error_string = sformatf( + "Cannot identify primary species for an element in kinetics component %s.", + comp_ptr_save->Get_formula().c_str()); + error_msg(error_string, CONTINUE); + continue; + } + + if (elt_list[j].elt->primary->s->type <= H2O) + { + int l; + for (l = 0; l < count_elts_kinetics; l++) + { + if (elt_list[j].elt == elt_list_kinetics[l].elt) + { + break; + } + } + if (l == count_elts_kinetics) + { + input_error++; + error_string = sformatf( + "Stoichiometry of surface, %s * %g mol sites/mol reactant,\n\tmust be a subset of the formula defined for the related reactant %s.\n\tElement %s is not present in reactant formula.", + comp_ptr_save->Get_formula().c_str(), + (double) comp_ptr_save->Get_phase_proportion(), + comp_ptr_save->Get_rate_name().c_str(), elt_list[j].elt->name); + error_msg(error_string, CONTINUE); + } + else if (fabs(elt_list[j].coef) > + fabs(elt_list_kinetics[l].coef)) + { + input_error++; + error_string = sformatf( + "Stoichiometry of surface, %s * %g mol sites/mol reactant,\n\tmust be a subset of the formula defined for the related reactant %s.\n\tCoefficient of element %s in surface exceeds amount present in reactant formula.", + comp_ptr_save->Get_formula().c_str(), + (double) comp_ptr_save->Get_phase_proportion(), + comp_ptr_save->Get_rate_name().c_str(), elt_list[j].elt->name); + error_msg(error_string, CONTINUE); + } + } + } + elt_list_kinetics = + (struct elt_list *) free_check_null(elt_list_kinetics); + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +ss_prep(LDBLE t, cxxSS *ss_ptr, int print) +/* ---------------------------------------------------------------------- */ +{ + int i, j, k, converged, divisions; + LDBLE r, rt, ag0, ag1, crit_pt; + LDBLE xc, tc; + LDBLE l_x, x0, x1, xsm1, xsm2, xb1, xb2; + LDBLE xc1, xc2; + LDBLE facb1, faca1, spim1, xblm1, acrae, acrael, xliapt, xliapm; + LDBLE xaly, xaly1, xaly2; + LDBLE faca, facb, spialy, facal, facbl; + LDBLE tol; + + if (pr.ss_assemblage == FALSE) + print = FALSE; + tol = 1e-6; + r = R_KJ_DEG_MOL; + rt = r * t; + a0 = ss_ptr->Get_ag0() / rt; + a1 = ss_ptr->Get_ag1() / rt; + ss_ptr->Set_a0(a0); + ss_ptr->Set_a1(a1); + ag0 = a0 * rt; + ag1 = a1 * rt; + cxxSScomp *comp0_ptr = &(ss_ptr->Get_ss_comps()[0]); + cxxSScomp *comp1_ptr = &(ss_ptr->Get_ss_comps()[1]); + struct phase *phase0_ptr = phase_bsearch(comp0_ptr->Get_name().c_str(), &k, FALSE); + struct phase *phase1_ptr = phase_bsearch(comp1_ptr->Get_name().c_str(), &k, FALSE); + kc = exp(k_calc(phase0_ptr->rxn->logk, t, REF_PRES_PASCAL) * LOG_10); + kb = exp(k_calc(phase1_ptr->rxn->logk, t, REF_PRES_PASCAL) * LOG_10); + crit_pt = fabs(a0) + fabs(a1); +/* + * Default, no miscibility or spinodal gaps + */ + ss_ptr->Set_miscibility(false); + ss_ptr->Set_spinodal(false); + xsm1 = 0.5; + xsm2 = 0.5; + xb1 = 0.5; + xb2 = 0.5; + xc1 = 0; + xc2 = 0; + + if (crit_pt >= tol) + { +/* + * Miscibility gap information + */ + if (fabs(a1) < tol) + { + xc = 0.5; + tc = ag0 / (2 * r); + } + else + { + xc = 0.5 + (pow((ag0 * ag0 + 27 * ag1 * ag1), (LDBLE) 0.5) - + ag0) / (18 * ag1); + tc = (12 * ag1 * xc - 6 * ag1 + 2 * ag0) * (xc - xc * xc) / r; + } + if (print == TRUE) + { + error_string = sformatf( "Description of Solid Solution %s", + ss_ptr->Get_name().c_str()); + dup_print(error_string, TRUE); + } + if (print == TRUE) + { + output_msg(sformatf( + "\t Temperature: %g kelvin\n", + (double) t)); + output_msg(sformatf( + "\t A0 (dimensionless): %g\n", + (double) a0)); + output_msg(sformatf( + "\t A1 (dimensionless): %g\n", + (double) a1)); + output_msg(sformatf( + "\t A0 (kJ/mol): %g\n", + (double) ag0)); + output_msg(sformatf( + "\t A1 (kJ/mol): %g\n\n", + (double) ag1)); + } + if (xc < 0 || xc > 1) + { + if (print == TRUE) + output_msg(sformatf( + "No miscibility gap above 0 degrees kelvin.\n")); + } + else + { + if (print == TRUE) + { + output_msg(sformatf( + "\t Critical mole-fraction of component 2: %g\n", + (double) xc)); + output_msg(sformatf( + "\t Critical temperature: %g kelvin\n", + (double) tc)); + output_msg(sformatf( + "\n(The critical temperature calculation assumes that the Guggenheim model\ndefined at %g kelvin is valid at the critical temperature.)\n\n\n", + (double) t)); + } + } +/* + * Calculate miscibility and spinodal gaps + */ + if (tc >= t) + { + + /* search for sign changes */ + x0 = 0; + x1 = 1; + if (scan(f_spinodal, &x0, &x1) == TRUE) + { + + /* find first spinodal pt */ + xsm1 = halve(f_spinodal, x0, x1, tol); + xsm1 = halve(f_spinodal, x0, x1, tol); + ss_ptr->Set_spinodal(true); + + /* find second spinodal pt */ + x0 = x1; + x1 = 1; + if (scan(f_spinodal, &x0, &x1) == TRUE) + { + xsm2 = halve(f_spinodal, x0, x1, tol); + } + else + { + error_msg("Failed to find second spinodal point.", STOP); + } + } + } + } +/* + * Now find Miscibility gap + */ + if (ss_ptr->Get_spinodal()) + { + if (print == TRUE) + output_msg(sformatf( + "\t Spinodal-gap mole fractions, component 2: %g\t%g\n", + (double) xsm1, (double) xsm2)); + converged = FALSE; + if (converged == FALSE) + { + for (i = 1; i < 3; i++) + { + divisions = (int) pow(10., i); + for (j = 0; j < divisions; j++) + { + for (k = divisions; k > 0; k--) + { + xc1 = (LDBLE) j / divisions + 0.001; + xc2 = (LDBLE) k / divisions; + converged = solve_misc(&xc1, &xc2, tol); + if (converged == TRUE) + break; + } + if (converged == TRUE) + break; + } + if (converged == TRUE) + break; + } + } + if (converged == FALSE) + { + error_msg("Failed to find miscibility gap.", STOP); + } + ss_ptr->Set_miscibility(true); + if (xc1 < xc2) + { + xb1 = 1 - xc2; + xb2 = 1 - xc1; + xc1 = 1 - xb1; + xc2 = 1 - xb2; + } + else + { + xb1 = 1 - xc1; + xb2 = 1 - xc2; + } + facb1 = kb * xb1 * exp(xc1 * xc1 * (a0 + a1 * (4 * xb1 - 1))); + faca1 = kc * xc1 * exp(xb1 * xb1 * (a0 - a1 * (3 - 4 * xb1))); + spim1 = log10(faca1 + facb1); + xblm1 = 1. / (1. + faca1 / facb1); + acrae = facb1 / faca1; + acrael = log10(acrae); + xliapt = log10(facb1); + xliapm = log10(faca1); + + if (print == TRUE) + { + output_msg(sformatf( + "\t Miscibility-gap fractions, component 2: %g\t%g\n", + (double) xb1, (double) xb2)); + output_msg(sformatf( + "\n\t\t\tEutectic Point Calculations\n\n")); + output_msg(sformatf( + "\t Aqueous activity ratio (comp2/comp1): %g\n", + (double) acrae)); + output_msg(sformatf( + "\t Log aqueous activity ratio (comp2/comp1): %g\n", + (double) acrael)); + output_msg(sformatf( + "\t Aqueous activity fraction of component 2: %g\n", + (double) xblm1)); + output_msg(sformatf( + "\t Log IAP (component 2): %g\n", + (double) xliapt)); + output_msg(sformatf( + "\t Log IAP (component 1): %g\n", + (double) xliapm)); + output_msg(sformatf( + "\t Log Sum Pi: %g\n", + (double) spim1)); + } + ss_ptr->Set_tk(t); + ss_ptr->Set_xb1(xb1); + ss_ptr->Set_xb2(xb2); + } +/* + * Alyotropic point calculation + */ + xaly = -1.0; + l_x = a0 * a0 + 3 * a1 * a1 + 6 * a1 * log(kb / kc); + if (l_x > 0) + { + if (fabs(l_x - a0 * a0) >= tol) + { + xaly1 = (-(a0 - 3 * a1) + pow(l_x, (LDBLE) 0.5)) / (6 * a1); + xaly2 = (-(a0 - 3 * a1) - pow(l_x, (LDBLE) 0.5)) / (6 * a1); + if (xaly1 >= 0 && xaly1 <= 1) + { + xaly = xaly1; + } + if (xaly2 >= 0 && xaly2 <= 1) + { + xaly = xaly2; + } + } + else + { + xaly = 0.5 + log(kb / kc) / (2 * a0); + } + if (xaly > 0 && xaly < 1) + { + faca = + kc * (1 - + xaly) * exp(xaly * xaly * (a0 - a1 * (3 - 4 * xaly))); + facb = + kb * xaly * exp((1 - xaly) * (1 - xaly) * + (a0 + a1 * (4 * xaly - 1.0))); + spialy = log10(faca + facb); + facal = log10(faca); + facbl = log10(facb); + if (xaly > xb1 && xaly < xb2) + { + if (print == TRUE) + output_msg(sformatf( + "\nLocal minimum in the solidus curve coresponding to a maximum\nin the minimum stoichiometric saturation curve.\n\n")); + } + else + { + if (print == TRUE) + output_msg(sformatf( + "\n\t\t\tAlyotropic Point\n\n")); + } + if (print == TRUE) + { + output_msg(sformatf( + "\t Solid mole fraction of component 2: %g\n", + (double) xaly)); + output_msg(sformatf( + "\t Log IAP (component 2): %g\n", + (double) facbl)); + output_msg(sformatf( + "\t Log IAP (component 1): %g\n", + (double) facal)); + output_msg(sformatf( + "\t Log Sum Pi: %g\n", + (double) spialy)); + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +halve(LDBLE f(LDBLE x, void *), LDBLE x0, LDBLE x1, LDBLE tol) +/* ---------------------------------------------------------------------- */ +{ + int i; + LDBLE l_x, y, y0, dx; + + y0 = f(x0, this); + dx = (x1 - x0); +/* + * Loop for interval halving + */ + for (i = 0; i < 100; i++) + { + dx *= 0.5; + l_x = x0 + dx; + y = f(l_x, this); + if (dx < tol || y == 0) + { + break; + } + if (y0 * y >= 0) + { + x0 = l_x; + y0 = y; + } + } + return (x0 + dx); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +scan(LDBLE f(LDBLE x, void *), LDBLE * xx0, LDBLE * xx1) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + LDBLE fx0, fx1, divisions; + LDBLE x0, x1, diff; + + x0 = *xx0; + x1 = *xx1; + diff = x1 - x0; + for (j = 0; j < 3; j++) + { + fx0 = f(x0, this); + divisions = (int) pow((LDBLE) 10, (LDBLE) j); + for (i = 1; i < divisions; i++) + { + x1 = *xx0 + diff * (LDBLE) i / divisions; + fx1 = f(x1, this); + if (fx0 * fx1 <= 0) + { + *xx0 = x0; + *xx1 = x1; + return (TRUE); + } + x0 = x1; + fx0 = fx1; + } + } + return (FALSE); +} + + +/* ---------------------------------------------------------------------- */ + LDBLE Phreeqc:: +f_spinodal(LDBLE x, void * cookie) +/* ---------------------------------------------------------------------- */ +{ + LDBLE fx; + Phreeqc * pThis; + pThis = (Phreeqc *) cookie; + fx = -12 * pThis->a1 * x * x * x + (18 * pThis->a1 - + 2 * pThis->a0) * x * x + (2 * pThis->a0 - + 6 * pThis->a1) * x - 1.0; + return (fx); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +slnq(int n, LDBLE * a, LDBLE * l_delta, int ncols, int print) +/* ---------------------------------------------------------------------- */ +{ + int i, j, k, m; +/* debug + */ + int row; + + LDBLE b; +/* Debug +*/ + if (print == TRUE) + { + output_msg(sformatf( "\nArray in slnq: \n\n")); + for (i = 0; i < ncols - 1; i++) + { + row = i * (n + 1); + for (j = 0; j < ncols; j++) + { + output_msg(sformatf( "%10.2e", (double) a[row + j])); + } + output_msg(sformatf( "\n")); + } + output_msg(sformatf( "\n")); + } + + if (n == 0) + return (OK); +/* Trivial case */ + if (n == 1) + { + if (fabs(a[0]) < ZERO_TOL) + goto slnq_error; + l_delta[0] = a[1] / a[0]; + return (OK); + } + +/* Reduction loop */ + for (i = 0; i < n - 1; i++) + { + b = fabs(a[i * ncols + i]); + m = i; + +/* Find maximum value in column */ + for (j = i + 1; j < n; j++) + { + if (fabs(a[j * ncols + i]) > b) + { + b = fabs(a[j * ncols + i]); + m = j; + } + } + +/* Check for singularity */ + if (b < ZERO_TOL) + goto slnq_error; + +/* Exchange rows if necessary */ + if (m != i) + { + for (j = i; j <= n; j++) + { + b = a[i * ncols + j]; + a[i * ncols + j] = a[m * ncols + j]; + a[m * ncols + j] = b; + } + } + +/* Make a[i][i]=1.0 */ + for (j = n; j >= i; j--) + { + a[i * ncols + j] /= a[i * ncols + i]; + } + +/* Reduction step */ + for (j = i + 1; j < n; j++) + { + if (a[j * ncols + i] == 0.0) + continue; + b = -a[j * ncols + i]; + for (k = i + 1; k <= n; k++) + { + a[j * ncols + k] += b * a[i * ncols + k]; + } + } + } + +/* Calculation of l_delta[n] */ + if (fabs(a[(n - 1) * ncols + n - 1]) > ZERO_TOL) + { + l_delta[n - 1] = a[(n - 1) * ncols + n] / a[(n - 1) * ncols + n - 1]; + } + else + { + output_msg(sformatf( "Error: Divide by zero in slnq.\n")); + l_delta[n] = 0.0; + goto slnq_error; + } + +/* Back substitution for other delta values */ + for (i = n - 2; i >= 0; i--) + { + l_delta[i] = a[i * ncols + n]; + for (j = i + 1; j < n; j++) + { + l_delta[i] -= a[i * ncols + j] * l_delta[j]; + } + } + if (print == TRUE) + { + output_msg(sformatf( "\nResults from slnq: \n\n")); + for (i = 0; i < n; i++) + { + output_msg(sformatf( "%10.2e", (double) l_delta[i])); + } + output_msg(sformatf( "\n")); + } + return (OK); + + slnq_error:{ + error_string = sformatf( + "Error: Singular matrix in subroutine slnq. \n"); + warning_msg(error_string); + } + return (ERROR); +} + +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +solve_misc(LDBLE * xxc1, LDBLE * xxc2, LDBLE tol) +/* ---------------------------------------------------------------------- */ +{ + int i, repeat, converged, max_iter; + LDBLE x1, x2, xb1, xb2; + LDBLE xc1, xc1_2, xc1_3, xc2, xc2_2, xc2_3; + LDBLE lc1, lc2, lb1, lb2; + LDBLE a[6], d[2]; + LDBLE t; + + d[0] = d[1] = 0; + xc1 = *xxc1; + xc2 = *xxc2; + x1 = 0; + x2 = 0; + converged = TRUE; + max_iter = 25; + for (i = 0; i < max_iter; i++) + { + /* + output_msg(sformatf( "%d xc1: %g\txc2 %g\n", i, xc1, xc2)); + */ + xb1 = 1 - xc1; + xb2 = 1 - xc2; + xc1_2 = xc1 * xc1; + xc1_3 = xc1_2 * xc1; + xc2_2 = xc2 * xc2; + xc2_3 = xc2_2 * xc2; + + lc1 = exp(xb1 * xb1 * (a0 - a1 * (3 - 4 * xb1))); + lb1 = exp(xc1 * xc1 * (a0 + a1 * (4 * xb1 - 1))); + lc2 = exp(xb2 * xb2 * (a0 - a1 * (3 - 4 * xb2))); + lb2 = exp(xc2 * xc2 * (a0 + a1 * (4 * xb2 - 1))); + + /* -fb */ + a[2] = -(xb1 * lb1 - xb2 * lb2); + + /* -fc */ + a[5] = -(xc1 * lc1 - xc2 * lc2); + + if (fabs(a[2]) < tol && fabs(a[5]) < tol) + break; + + /* dfb/dxc1 */ + t = exp(a0 * xc1_2 - 4 * a1 * xc1_3 + 3 * a1 * xc1_2); + a[0] = + (2 * a0 * xc1 + 6 * a1 * xc1 - 2 * a0 * xc1_2 + 12 * a1 * xc1_3 - + 18 * a1 * xc1_2 - 1) * t; + + /* dfb/dxc2 */ + t = exp(a0 * xc2_2 - 4 * a1 * xc2_3 + 3 * a1 * xc2_2); + a[1] = + (2 * a0 * xc2_2 - 12 * a1 * xc2_3 - 2 * a0 * xc2 + + 18 * a1 * xc2_2 - 6 * a1 * xc2 + 1) * t; + + + /* dfc/dxc1 */ + t = exp(a0 * xc1_2 - 2 * a0 * xc1 + a0 - 4 * a1 * xc1_3 + + 9 * a1 * xc1_2 - 6 * a1 * xc1 + a1); + a[3] = + (2 * a0 * xc1_2 - 2 * a0 * xc1 - 12 * a1 * xc1_3 + + 18 * a1 * xc1_2 - 6 * a1 * xc1 + 1) * t; + + /* dfc/dxc2 */ + t = exp(a0 * xc2_2 - 2 * a0 * xc2 + a0 - 4 * a1 * xc2_3 + + 9 * a1 * xc2_2 - 6 * a1 * xc2 + a1); + a[4] = + (-2 * a0 * xc2_2 + 2 * a0 * xc2 + 12 * a1 * xc2_3 - + 18 * a1 * xc2_2 + 6 * a1 * xc2 - 1) * t; + + + /* solve for dxc1 and dxc2 */ + slnq(2, a, d, 3, FALSE); + + repeat = TRUE; + while (repeat == TRUE) + { + x1 = xc1 + d[0]; + x2 = xc2 + d[1]; + if (x1 > 1 || x1 < 0 || x2 > 1 || x2 < 0) + { + d[0] *= 0.5; + d[1] *= 0.5; + } + else + { + repeat = FALSE; + } + }; + xc1 = x1; + xc2 = x2; + + if (fabs(xc1 - xc2) < .01) + { + converged = FALSE; + break; + } + } + if (i == max_iter) + converged = FALSE; + *xxc1 = xc1; + *xxc2 = xc2; + return (converged); +} +/* ---------------------------------------------------------------------- */ + int Phreeqc:: +ss_calc_a0_a1(cxxSS *ss_ptr) +/* ---------------------------------------------------------------------- */ +{ + int i, done; + LDBLE r, rt; + std::vector p; + LDBLE q1, q2, xbq1, xbq2, xb1, xb2, xc1, xc2; + LDBLE r1, r2, pa1, pb1, pa2, pb2, xsm1, xsm2; + LDBLE pn9, pn10, c5, c6, pl9, pl10, pj9, pj10; + LDBLE xc, tc; + LDBLE spialy, azero, phi1, phi2, test; + LDBLE dq1, dq2, denom, ratio, dr1, dr2, x21, x22, x61, x62; + LDBLE l_a0, l_a1, ag0, ag1; + LDBLE wg2, wg1, alpha2, alpha3; + LDBLE l_kc, l_kb; + LDBLE xaly, xcaly, alpha0, alpha1, fx, fx1; + LDBLE tol; + + tol = 1e-6; + rt = ss_ptr->Get_tk() * R_KJ_DEG_MOL; + if (ss_ptr->Get_ss_comps().size() < 2) + { + input_error++; + error_string = sformatf( + "Two components not defined for solid solution ", + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + return (ERROR); + } + cxxSScomp *comp0_ptr = &(ss_ptr->Get_ss_comps()[0]); + cxxSScomp *comp1_ptr = &(ss_ptr->Get_ss_comps()[1]); + int k; + struct phase *phase0_ptr = phase_bsearch(comp0_ptr->Get_name().c_str(), &k, FALSE); + struct phase *phase1_ptr = phase_bsearch(comp1_ptr->Get_name().c_str(), &k, FALSE); + if (phase0_ptr == NULL || phase1_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Two components were not defined for %s solid solution", + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + return (ERROR); + } + l_kc = exp(k_calc(phase0_ptr->rxn->logk, ss_ptr->Get_tk(), REF_PRES_PASCAL) * + LOG_10); + l_kb = exp(k_calc(phase1_ptr->rxn->logk, ss_ptr->Get_tk(), REF_PRES_PASCAL) * + LOG_10); + + p = ss_ptr->Get_p(); + + l_a0 = 0; + l_a1 = 0; + ag0 = 0; + ag1 = 0; + dq2 = 0; + switch (ss_ptr->Get_input_case()) + { + /* + * dimensionless a0 and a1 + */ + case cxxSS::SS_PARM_A0_A1: + l_a0 = p[0]; + l_a1 = p[1]; + ag0 = l_a0 * rt; + ag1 = l_a1 * rt; + break; + /* + * two activity coefficients + * q1, q2, xbq1, xbq2 + */ + case cxxSS::SS_PARM_GAMMAS: + q1 = p[0]; + q2 = p[1]; + xbq1 = p[2]; + xbq2 = p[3]; + done = FALSE; + if (fabs(1 - xbq1) > 0 && q1 > 0) + { + dq1 = log(q1) / ((1 - xbq1) * (1 - xbq1)); + if (xbq2 <= 0 || xbq2 > 1) + { + l_a0 = dq1; + l_a1 = 0; + done = TRUE; + } + } + if (done == FALSE) + { + if (fabs(xbq2) < 0 || q2 <= 0) + { + input_error++; + error_string = sformatf( + "No solution possible for A0 and A1 calculation from two activity coefficients, %s.\n", + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + done = TRUE; + } + } + if (done == FALSE) + { + dq2 = log(q2) / (xbq2 * xbq2); + if (xbq1 < 0. || xbq2 > 1.) + { + l_a0 = dq2; + l_a1 = 0; + done = TRUE; + } + } + if (done == FALSE) + { + denom = 4 * (xbq1 - xbq2) + 2; + if (fabs(denom) >= tol) + { + if (fabs(1 - xbq1) > 0 && q1 > 0) + { + dq1 = log(q1) / ((1 - xbq1) * (1 - xbq1)); + l_a0 = (dq1 * (3 - 4 * xbq2) + + dq2 * (4 * xbq1 - 1)) / denom; + l_a1 = (dq1 - dq2) / denom; + done = TRUE; + } + } + } + if (done == FALSE) + { + input_error++; + error_string = sformatf( + "No solution possible for A0 and A1 calculation from two activity coefficients, %s.\n", + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + } + /* io = 1 */ + ag0 = l_a0 * rt; + ag1 = l_a1 * rt; + break; + /* + * two distribution coefficients + * q1, q2, xbq1, xbq2 + */ + case cxxSS::SS_PARM_DIST_COEF: + q1 = p[0]; + q2 = p[1]; + xbq1 = p[2]; + xbq2 = p[3]; + ratio = l_kc / l_kb; + dr1 = log(q1 / ratio); + x21 = 2 * xbq1 - 1; + if (fabs(xbq1 - xbq2) < tol || xbq2 < 0) + { + l_a0 = dr1 / x21; + l_a1 = 0; + } + else + { + dr2 = log(q2 / ratio); + x22 = 2 * xbq2 - 1; + if (xbq1 < 0.) + { + l_a0 = dr2 / x22; + l_a1 = 0; + } + else + { + x61 = 6 * xbq1 * xbq1 - 6 * xbq1 + 1; + x62 = 6 * xbq2 * xbq2 - 6 * xbq2 + 1; + if (fabs(x22 * x61 - x21 * x62) < tol) + { + input_error++; + error_string = sformatf( + "No solution possible for A0 and A1 calculation from two distribution coefficients, %s.\n", + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + } + l_a0 = (x61 * dr2 - x62 * dr1) / (x22 * x61 - x21 * x62); + l_a1 = (x21 * dr2 - x22 * dr1) / (x21 * x62 - x22 * x61); + } + } + + /* io = 1 */ + ag0 = l_a0 * rt; + ag1 = l_a1 * rt; + break; + /* + * from miscibility gap fractions + * q1, q2 + */ + case cxxSS::SS_PARM_MISCIBILITY: + q1 = p[0]; + q2 = p[1]; + xb1 = q1; + xb2 = q2; + xc1 = 1 - xb1; + xc2 = 1 - xb2; + r1 = log(xb1 / xb2); + r2 = log(xc1 / xc2); + pa1 = xc2 * xc2 - xc1 * xc1; + pb1 = + 3 * (xc2 * xc2 - xc1 * xc1) - 4 * (xc2 * xc2 * xc2 - + xc1 * xc1 * xc1); + pa2 = xb2 * xb2 - xb1 * xb1; + pb2 = + -(3 * (xb2 * xb2 - xb1 * xb1) - + 4 * (xb2 * xb2 * xb2 - xb1 * xb1 * xb1)); + l_a0 = (r1 - pb1 / pb2 * r2) / (pa1 - pa2 * pb1 / pb2); + l_a1 = (r1 - pa1 / pa2 * r2) / (pb1 - pb2 * pa1 / pa2); + + /* io = 1 */ + ag0 = l_a0 * rt; + ag1 = l_a1 * rt; + break; + /* + * from spinodal gap fractions + * q1, q2 + */ + case cxxSS::SS_PARM_SPINODAL: + q1 = p[0]; + q2 = p[1]; + xsm1 = q1; + xsm2 = q2; + pn9 = 1 / xsm1; + pn10 = 1 / xsm2; + c5 = 1 - xsm1; + c6 = 1 - xsm2; + pl9 = 6 * c5 - 12 * c5 * c5; + pl10 = 6 * c6 - 12 * c6 * c6; + pj9 = 2 * c5; + pj10 = 2 * c6; + l_a0 = (pn9 - pl9 / pl10 * pn10) / (pj9 - pl9 / pl10 * pj10); + l_a1 = (pn9 - pj9 / pj10 * pn10) / (pl9 - pj9 / pj10 * pl10); + + /* io = 1 */ + ag0 = l_a0 * rt; + ag1 = l_a1 * rt; + break; + /* + * from critical point + * q1, q2 + */ + case cxxSS::SS_PARM_CRITICAL: + xc = p[0]; + tc = p[1]; + r = R_KJ_DEG_MOL; + ag1 = r * tc * (2 * xc - 1) / (12 * xc * xc * (1 - xc) * (1 - xc)); + ag0 = (r * tc / (xc * (1 - xc)) - (12 * xc - 6) * ag1) / 2; + + /* io = 0 */ + l_a0 = ag0 / rt; + l_a1 = ag1 / rt; + break; + /* + * from alyotropic point + * q1, q2 + */ + case cxxSS::SS_PARM_ALYOTROPIC: + q1 = p[0]; + q2 = p[1]; + xaly = q1; + r = log(l_kb / l_kc); + alpha0 = 2 * xaly - 1; + alpha1 = 6 * xaly * (xaly - 1) + 1; + spialy = pow((LDBLE) 10., q2); + l_a0 = -999.; + l_a1 = -999.; + if (fabs(alpha0) < tol) + { + input_error++; + error_string = sformatf( + "No solution possible for A0 and A1 calculation from alyotropic point, %s.\n", + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + } + else + { + azero = 1; + if (fabs(alpha0) > tol) + azero = r / alpha0; + xcaly = 1 - xaly; +/* + * Solve for a0 by Newton's method + */ + for (i = 0; i < 50; i++) + { + phi1 = + xcaly * xcaly * (azero + + (r - azero * alpha0) * (4 * xaly - + 1) / alpha1); + phi2 = + xaly * xaly * (azero + + (3 - 4 * xaly) * (azero * alpha0 - + r) / alpha1); + phi1 = xaly * l_kb * exp(phi1); + phi2 = xcaly * l_kc * exp(phi2); + fx = phi1 + phi2 - spialy; + fx1 = + xcaly * xcaly * (1 - + alpha0 * (4 * xaly - + 1) / alpha1) * phi1 + + xaly * xaly * (1 + + alpha0 * (3 - 4 * xaly) / alpha1) * phi2; + if (fabs(fx1) < 1e-10) + { + input_error++; + error_string = sformatf( + "Could not find A0 and A1 calculation from alyotropic point, %s.\n", + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + break; + } + l_a0 = azero - fx / fx1; + test = fabs(l_a0 - azero) + fabs(fx); + azero = l_a0; + if (test < tol) + break; + } + if (i == 50) + { + input_error++; + error_string = sformatf( + "Too many iterations, could not find A0 and A1 calculation from alyotropic point, %s.\n", + ss_ptr->Get_name().c_str()); + error_msg(error_string, CONTINUE); + } + else + { + l_a1 = (r - l_a0 * alpha0) / alpha1; + + /* io = 0 */ + ag0 = l_a0 * rt; + ag1 = l_a1 * rt; + } + + } + break; + /* + * dimensional (kJ/mol) Guggenheim parameters + * ag0, ag1 + */ + case cxxSS::SS_PARM_DIM_GUGG: + ag0 = p[0]; + ag1 = p[1]; + l_a0 = ag0 / rt; + l_a1 = ag1 / rt; + break; + /* + * Waldbaum-Thompson + * wg2, wg1 + */ + case cxxSS::SS_PARM_WALDBAUM: + wg2 = p[0]; + wg1 = p[1]; + ag0 = (wg2 + wg1) / 2; + ag1 = (wg2 - wg1) / 2; + l_a0 = ag0 / rt; + l_a1 = ag1 / rt; + break; + /* + * Margules + * alpha2, alpha3 + */ + case cxxSS::SS_PARM_MARGULES: + alpha2 = p[0]; + alpha3 = p[1]; + l_a0 = alpha2 + 3 * alpha3 / 4; + l_a1 = alpha3 / 4; + ag0 = l_a0 * rt; + ag1 = l_a1 * rt; + break; + case cxxSS::SS_PARM_NONE: + break; + } + ss_ptr->Set_ag0(ag0); + ss_ptr->Set_ag1(ag1); + ss_ptr->Set_a0(l_a0); + ss_ptr->Set_a1(l_a1); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_master_isotope(void) +/* ---------------------------------------------------------------------- */ +{ + int i; + struct master *master_ptr; + + for (i = 0; i < count_master_isotope; i++) + { + /* + * Mark master species list as minor isotope + */ + if (master_isotope[i]->minor_isotope == TRUE) + { + master_ptr = master_bsearch(master_isotope[i]->name); + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( + "Did not find master species for isotope, %s", + master_isotope[i]->name); + error_msg(error_string, CONTINUE); + master_isotope[i]->master = NULL; + continue; + } + else + { + master_isotope[i]->master = master_ptr; + } + master_ptr->minor_isotope = TRUE; + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_isotope_ratios(void) +/* ---------------------------------------------------------------------- */ +{ + int i; + struct master *master_ptr; + struct master_isotope *master_isotope_ptr; + struct calculate_value *calculate_value_ptr; + + for (i = 0; i < count_isotope_ratio; i++) + { + /* + * Mark master species list as minor isotope + */ + master_isotope_ptr = + master_isotope_search(isotope_ratio[i]->isotope_name); + if (master_isotope_ptr == NULL) + { + input_error++; + error_string = sformatf( + "For ISOTOPE_RATIO %s, did not find ISOTOPE definition for this isotope, %s", + isotope_ratio[i]->name, isotope_ratio[i]->isotope_name); + error_msg(error_string, CONTINUE); + } + master_ptr = master_bsearch(isotope_ratio[i]->isotope_name); + if (master_ptr == NULL) + { + input_error++; + error_string = sformatf( + "For ISOTOPE_RATIO %s, did not find SOLUTION_MASTER_SPECIES for isotope, %s", + isotope_ratio[i]->name, isotope_ratio[i]->isotope_name); + error_msg(error_string, CONTINUE); + } + calculate_value_ptr = calculate_value_search(isotope_ratio[i]->name); + if (calculate_value_ptr == NULL) + { + input_error++; + error_string = sformatf( + "For ISOTOPE_RATIOS %s, did not find corresponding CALCULATE_VALUE definition", + isotope_ratio[i]->name); + error_msg(error_string, CONTINUE); + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_isotope_alphas(void) +/* ---------------------------------------------------------------------- */ +{ + int i; + struct calculate_value *calculate_value_ptr; + struct logk *logk_ptr; + + for (i = 0; i < count_isotope_alpha; i++) + { + /* + * Mark master species list as minor isotope + */ + calculate_value_ptr = calculate_value_search(isotope_alpha[i]->name); + if (calculate_value_ptr == NULL) + { + input_error++; + error_string = sformatf( + "For ISOTOPE_ALPHAS %s, did not find corresponding CALCULATE_VALUE definition", + isotope_alpha[i]->name); + error_msg(error_string, CONTINUE); + } + if (isotope_alpha[i]->named_logk != NULL) + { + logk_ptr = logk_search(isotope_alpha[i]->named_logk); + if (logk_ptr == NULL) + { + input_error++; + error_string = sformatf( + "For ISOTOPE_ALPHAS %s, did not find corresponding NAMED_EXPRESSION definition %s.", + isotope_alpha[i]->name, isotope_alpha[i]->named_logk); + error_msg(error_string, CONTINUE); + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +reset_last_model(void) +/* ---------------------------------------------------------------------- */ +{ +/* + * Initialize model + */ + last_model.force_prep = TRUE; + last_model.count_exchange = 0; + last_model.exchange = + (struct master **) free_check_null(last_model.exchange); + last_model.count_gas_phase = 0; + last_model.gas_phase = + (struct phase **) free_check_null(last_model.gas_phase); + last_model.count_ss_assemblage = 0; + last_model.ss_assemblage = + (const char **) free_check_null(last_model.ss_assemblage); + last_model.count_pp_assemblage = 0; + last_model.pp_assemblage = + (struct phase **) free_check_null(last_model.pp_assemblage); + last_model.add_formula = + (const char **) free_check_null(last_model.add_formula); + last_model.si = (LDBLE *) free_check_null(last_model.si); + last_model.dl_type = cxxSurface::NO_DL; + last_model.count_surface_comp = 0; + last_model.surface_comp = + (const char **) free_check_null(last_model.surface_comp); + last_model.count_surface_charge = 0; + last_model.surface_charge = + (const char **) free_check_null(last_model.surface_charge); + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +tidy_exchange(void) +/* ---------------------------------------------------------------------- */ +/* + * If exchanger is related to mineral, exchanger amount is + * set in proportion + */ +{ + //std::map::iterator it = Rxn_exchange_map.begin(); + //for ( ; it != Rxn_exchange_map.end(); it++) + //for (size_t nn = 0; nn < Rxn_new_exchange.size(); nn++) + //{ + // std::map::iterator it = Rxn_exchange_map.find(Rxn_new_exchange[nn]); + for (std::set::const_iterator nit = Rxn_new_exchange.begin(); nit != Rxn_new_exchange.end(); nit++) + { + std::map::iterator it = Rxn_exchange_map.find(*nit); + if (it == Rxn_exchange_map.end()) + { + assert(false); + } + //std::map::iterator it = Rxn_exchange_map.begin(); + cxxExchange * exchange_ptr = &(it->second); + //if (!exchange_ptr->Get_new_def()) + // continue; + //if (exchange_ptr->Get_n_user() < 0) + // continue; + // check elements + for (size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++) + { + cxxExchComp & comp_ref = exchange_ptr->Get_exchange_comps()[j]; + if (comp_ref.Get_phase_name().size() > 0) + continue; + if (comp_ref.Get_rate_name().size() > 0) + continue; + + /* Check elements */ + cxxNameDouble nd = comp_ref.Get_totals(); + cxxNameDouble::iterator kit = nd.begin(); + for (; kit != nd.end(); kit++) + { + /* Find master species */ + struct element *elt_ptr = element_store(kit->first.c_str()); + if (elt_ptr == NULL || elt_ptr->master == NULL) + { + input_error++; + error_string = sformatf( "Master species not in database " + "for %s, skipping element.", + kit->first.c_str()); + error_msg(error_string, CONTINUE); + break; + } + } + } + } + return (OK); +} + diff --git a/phreeqcpp/transport.cpp b/phreeqcpp/transport.cpp new file mode 100644 index 00000000..5cdfa574 --- /dev/null +++ b/phreeqcpp/transport.cpp @@ -0,0 +1,4720 @@ +#include "Utils.h" +#include "Phreeqc.h" +#include "phqalloc.h" +#include "Exchange.h" +#include "GasPhase.h" +#include "PPassemblage.h" +#include "SSassemblage.h" +#include "cxxKinetics.h" +#include "Solution.h" + +LDBLE F_Re3 = F_C_MOL / (R_KJ_DEG_MOL * 1e3); +LDBLE tk_x2; // average tx_x of icell and jcell +LDBLE dV_dcell; // difference in Volt among icell and jcell +int find_current; +struct CURRENT_CELLS +{ + LDBLE dif, ele, R; // diffusive and electric components, relative cell resistance +} *current_cells; +LDBLE sum_R, sum_Rd; // sum of R, sum of (current_cells[0].dif - current_cells[i].dif) * R +struct V_M // For calculating Vinograd and McBain's zero-charge, diffusive tranfer of individual solutes +{ + LDBLE grad, D, z, c, zc, Dz, Dzc; + LDBLE b_ij; // harmonic mean of cell properties, with EDL enrichment +}; +struct CT /* summed parts of V_M and mcd transfer in a timestep for all cells, for free + DL water */ +{ + LDBLE dl_s, Dz2c, Dz2c_dl, visc1, visc2, J_ij_sum; + LDBLE A_ij_il, Dz2c_il, mixf_il; + int J_ij_count_spec, J_ij_il_count_spec; + struct V_M *v_m, *v_m_il; + struct J_ij *J_ij, *J_ij_il; +} *ct = NULL; +struct MOLES_ADDED /* total moles added to balance negative conc's */ +{ + char *name; + LDBLE moles; +} *moles_added; +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +transport(void) +/* ---------------------------------------------------------------------- */ +{ + int i, j, k, n; + int j_imm; + LDBLE b, f, mix_f_m, mix_f_imm; + LDBLE water_m, water_imm; + int first_c, last_c, b_c; + int max_iter; + char token[MAX_LENGTH]; + LDBLE kin_time, stagkin_time, kin_time_save; + + int punch_boolean = 0; + LDBLE step_fraction; + + state = TRANSPORT; + diffc_tr = diffc; + diffc_max = 0.0; + transp_surf = warn_fixed_Surf = warn_MCD_X = 0; + dV_dcell = current_A = 0.0; + current_cells = NULL; + + /* mass_water_switch = TRUE; */ + /* + * Check existence of solutions + */ + j = -1; + /* check column solutions */ + for (i = 1; i <= count_cells; i++) + { + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, i)); + if (use.Get_solution_ptr() == NULL) + { + input_error++; + error_string = sformatf( + "Solution %d is needed for transport, but is not defined.", + i); + error_msg(error_string, CONTINUE); + } + else + { + cell_data[i].temp = use.Get_solution_ptr()->Get_tc(); + } + } + + if (multi_Dflag) + { + sol_D = (struct sol_D *) PHRQ_malloc((size_t) (all_cells)* sizeof(struct sol_D)); + if (sol_D == NULL) + malloc_error(); + //sol_D_dbg = sol_D; + + ct = (struct CT *) PHRQ_malloc((size_t) (all_cells)* sizeof(struct CT)); + if (ct == NULL) + malloc_error(); + { + for (int i = 0; i < all_cells; i++) + { + ct[i].dl_s = 0.0; + ct[i].Dz2c = 0.0; + ct[i].Dz2c_dl = 0.0; + ct[i].visc1 = 0.0; + ct[i].visc2 = 0.0; + ct[i].J_ij_sum = 0.0; + ct[i].A_ij_il = 0.0; + ct[i].Dz2c_il = 0.0; + ct[i].mixf_il = 0.0; + ct[i].J_ij_count_spec = 0; + ct[i].J_ij_il_count_spec = 0; + ct[i].v_m = NULL; + ct[i].v_m_il = NULL; + ct[i].J_ij = NULL; + ct[i].J_ij_il = NULL; + } + } + + moles_added = (struct MOLES_ADDED *) PHRQ_malloc((size_t) (count_elements)* sizeof(struct MOLES_ADDED)); + if (moles_added == NULL) + malloc_error(); + + for (i = 0; i < all_cells; i++) + { + sol_D[i].count_spec = 0; + sol_D[i].count_exch_spec = 0; + sol_D[i].exch_total = 0; + sol_D[i].x_max = 0; + sol_D[i].spec = NULL; + } + for (i = 0; i < count_elements; i++) + { + moles_added[i].name = NULL; + moles_added[i].moles = 0; + } + } + try + { + /* check solution 0 */ + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, 0)); + if (!use.Get_solution_ptr()) + { + if (ishift == 1) + { + input_error++; + error_string = sformatf( + "Solution 0 is needed for transport, but is not defined."); + error_msg(error_string, CONTINUE); + } + else + Utilities::Rxn_copy(Rxn_solution_map, 1, 0); + } + else + { + if ((cell_data[0].potV = use.Get_solution_ptr()->Get_potV())) + dV_dcell = 1; + } + + /* check solution count_cells */ + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, count_cells + 1)); + if (!use.Get_solution_ptr()) + { + if (ishift == -1) + { + input_error++; + error_string = sformatf( + "Solution %d is needed for transport, but is not defined.", + count_cells + 1); + error_msg(error_string, CONTINUE); + } + else + Utilities::Rxn_copy(Rxn_solution_map, count_cells, count_cells + 1); + } + else + { + if ((cell_data[count_cells + 1].potV = use.Get_solution_ptr()->Get_potV())) + dV_dcell = 1; + } + /* + * Initialize temperature in stagnant cells ... + */ + for (n = 1; n <= stag_data->count_stag; n++) + { + for (i = 1; i <= count_cells; i++) + { + k = i + 1 + n * count_cells; + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, k)); + if (use.Get_solution_ptr() != NULL) + cell_data[k].temp = use.Get_solution_ptr()->Get_tc(); + } + } + + if (fix_current && !dV_dcell) + { + warning_msg("fix_current (A) was defined, but potential in a boundary cell was not.\n\tUsing 1e-8 V in cell 0 for calculating the potentials."); + cell_data[0].potV = 1e-8; + dV_dcell = 1; + } + if (dV_dcell) + { + if (ishift) + { + input_error++; + error_string = sformatf( + "Electro-diffusion cannot be combined with advective transport."); + error_msg(error_string, CONTINUE); + free_check_null(sol_D); + } + if (!multi_Dflag) + { + input_error++; + error_string = sformatf( + "Electrical Field (potential) was defined, but needs -multi_D."); + error_msg(error_string, CONTINUE); + free_check_null(sol_D); + } + else + { + if (bcon_first != 1) + { + bcon_first = 1; + error_string = sformatf( + "Electrical Field (potential) was defined, assuming constant boundary condition for first cell."); + warning_msg(error_string); + } + if (bcon_last != 1) + { + bcon_last = 1; + error_string = sformatf( + "Electrical Field (potential) was defined, assuming constant boundary condition for last cell."); + warning_msg(error_string); + } + current_cells = (struct CURRENT_CELLS *) PHRQ_malloc((size_t) + (count_cells + 1) * sizeof(struct CURRENT_CELLS)); + if (current_cells == NULL) + malloc_error(); + for (int i = 0; i < count_cells + 1; i++) + { + current_cells[i].dif = 0.0; + current_cells[i].ele = 0.0; + current_cells[i].R = 0.0; + } + } + } + /* + * First equilibrate solutions + */ + dup_print("Equilibrating initial solutions", TRUE); + transport_step = 0; + for (i = 0; i <= count_cells + 1; i++) + { + if ((bcon_first == 2 && i == 0) || + (bcon_last == 2 && i == count_cells + 1)) + continue; + set_initial_moles(i); + cell_no = i; + set_and_run_wrapper(i, NOMIX, FALSE, i, 0.0); + if (use.Get_surface_ptr() != NULL && use.Get_surface_ptr()->Get_transport()) + transp_surf = TRUE; + if (transp_surf && !multi_Dflag) + { + error_string = sformatf( + "-multi_d must be defined for surface transport"); + error_msg(error_string, CONTINUE); + } + if (multi_Dflag == TRUE) + { + fill_spec(cell_no); + } + print_punch(i, true); + + /* if (i > 0 && i <= count_cells)*/ + saver(); + } + /* + * Also stagnant cells + */ + for (n = 1; n <= stag_data->count_stag; n++) + { + for (i = 1; i <= count_cells; i++) + { + k = i + 1 + n * count_cells; + cell_no = k; + if (Utilities::Rxn_find(Rxn_solution_map, k) != 0) + { + set_initial_moles(k); + set_and_run_wrapper(k, NOMIX, FALSE, k, 0.0); + if (multi_Dflag == TRUE) + { + fill_spec(cell_no); + } + print_punch(k, true); + saver(); + } + } + } + /* + * Initialize mixing factors, define kinetics times + * for multicomponent diffusion, limit mixing by diffc_max (usually from H+) + */ + if (multi_Dflag == TRUE) + diffc_tr = diffc_max; + if ((stag_data->exch_f > 0) && (stag_data->count_stag == 1)) + { + /* multi_D calc's are OK if all cells have the same amount of water */ + /* if (multi_Dflag == TRUE) + { + sprintf(token, "multi_D calc's and stagnant: define MIXing factors explicitly, or \n\t give all cells the same amount of water."); + warning_msg(token); + } + */ + + Rxn_mix_map.clear(); + + /* + * stagnant mix factors go in mix[0 .. count_cells] + */ + } + /* + * mix[] is extended in init_mix(), to accommodate column mix factors + */ + nmix = init_mix(); + heat_nmix = init_heat_mix(nmix); + if (nmix < 2) + stagkin_time = timest; + else + stagkin_time = timest / nmix; + if (ishift != 0) + kin_time = timest / (1 + nmix); + else + kin_time = stagkin_time; + kin_time_save = kin_time; + + /* Reaction defined for a shift... */ + if (!ishift) + { + if (nmix < 2) + step_fraction = 1.0; + else + step_fraction = 1.0 / nmix; + } + else + step_fraction = 1.0 / (1.0 + nmix); + /* + * Set boundary conditions, transport direction + */ + last_model.force_prep = TRUE; + if ((ishift == 0) || (bcon_first == 1) || (bcon_last == 1)) + b_c = 1; + else + b_c = 0; + if (ishift >= 0) + { + last_c = count_cells; + first_c = 1; + } + else + { + last_c = 1; + first_c = count_cells; + } + /* + * Define stagnant/mobile mix structure, if not read explicitly. + * + * With count_stag = 1, mix factors are calculated from exchange factor alpha + * (= exch_f), mobile th_m and immobile th_im porosity. + * These variables are read under keyword TRANSPORT, after stagnant, in + * structure stag_data. + * MIX 'cell_no' in input file can be an alternative for the calculation here. + */ + if ((stag_data->exch_f > 0) && (stag_data->count_stag == 1)) + { + b = stag_data->th_m / (stag_data->th_m + stag_data->th_im); + f = exp(-stag_data->exch_f * stagkin_time / (b * stag_data->th_im)); + mix_f_imm = b - b * f; + mix_f_m = mix_f_imm * stag_data->th_im / stag_data->th_m; + for (j = 1; j <= count_cells; j++) + { + j_imm = j + (1 + count_cells); + if (Utilities::Rxn_find(Rxn_solution_map, j) == NULL) + error_msg + ("Could not find mobile cell solution in TRANSPORT.", + STOP); + if (Utilities::Rxn_find(Rxn_solution_map, j_imm) == NULL) + //error_msg + //("Could not find immobile cell solution in TRANSPORT.", + //STOP); + continue; + water_m = Utilities::Rxn_find(Rxn_solution_map, j)->Get_mass_water(); + water_imm = Utilities::Rxn_find(Rxn_solution_map, j_imm)->Get_mass_water(); + /* + * Define C_m = (1 - mix_f_m) * C_m0 + mix_f_m) * C_im0 + */ + { + cxxMix temp_mix; + temp_mix.Set_n_user(j); + temp_mix.Set_n_user_end(j); + temp_mix.Add(j, 1 - mix_f_m); + temp_mix.Add(j_imm, mix_f_m * water_m / water_imm); + Rxn_mix_map[j] = temp_mix; + } + /* + * Define C_im = mix_f_imm * C_m0 + (1 - mix_f_imm) * C_im0, or... + */ + { + cxxMix temp_mix; + temp_mix.Set_n_user(j_imm); + temp_mix.Set_n_user_end(j_imm); + temp_mix.Add(j_imm, 1 - mix_f_imm); + temp_mix.Add(j, mix_f_imm * water_imm / water_m); + Rxn_mix_map[j_imm] = temp_mix; + } + } + + if (heat_nmix > 0) + { + /* + * Assumption: D_e used for calculating exch_f in input file equals diffc + */ + f = stag_data->exch_f * (heat_diffc - diffc) / diffc / tempr; + f = exp(-f * stagkin_time / (b * stag_data->th_im)); + heat_mix_f_imm = b - b * f; + heat_mix_f_m = + heat_mix_f_imm * stag_data->th_im / stag_data->th_m; + } + } + /* + * Stop if error + */ + if (get_input_errors() > 0) + { + error_msg("Program terminating due to input errors.", STOP); + } + /* + * Now transport + */ + sprintf(token, "\nCalculating transport: %d cells, %d shifts, %d mixruns...\n\n", + count_cells, count_shifts - transport_start + 1, nmix); + screen_msg(token); + max_iter = 0; + for (transport_step = transport_start; transport_step <= count_shifts; + transport_step++) + { + /* + * Set initial moles of phases + */ + for (i = 0; i <= count_cells + 1; i++) + { + if (!dV_dcell && (i == 0 || i == count_cells + 1)) + continue; + set_initial_moles(i); + } + /* + * Also stagnant cells + */ + for (n = 1; n <= stag_data->count_stag; n++) + { + for (i = 1; i <= count_cells; i++) + { + k = i + 1 + n * count_cells; + cell_no = k; + if (Utilities::Rxn_find(Rxn_solution_map, k) != 0) + set_initial_moles(k); + } + } + /* + * Start diffusing if boundary cond = 1, (fixed c, or closed) + */ + if (b_c == 1) + { + /* For half of mixing steps */ + for (j = 1; j <= floor((LDBLE)nmix / 2); j++) + { + rate_sim_time_start = + (transport_step - 1) * timest + (j - 1) * kin_time; + rate_sim_time = rate_sim_time_start + kin_time; + + mixrun = j; + if (multi_Dflag && j == floor((LDBLE)nmix / 2)) + { + //sprintf(token, + // "Transport step %3d. Multicomponent diffusion run %3d.", + // transport_step, j); + //dup_print(token, FALSE); + } + else if (!multi_Dflag) + { + sprintf(token, "Transport step %3d. Mixrun %3d.", + transport_step, j); + dup_print(token, FALSE); + } + + if (heat_nmix > 0) + { + heat_mix(heat_nmix); + /* equilibrate again ... */ + for (i = 1; i <= count_cells; i++) + { + cell_no = i; + set_and_run_wrapper(i, NOMIX, FALSE, i, 0.0); + if (multi_Dflag) + fill_spec(i); + saver(); + } + } + /* Go through cells */ + if (transp_surf) + { + if (disp_surf(stagkin_time) == ERROR) + error_msg("Error in surface transport, stopping.", + STOP); + } + if (multi_Dflag) + multi_D(stagkin_time, 1, FALSE); + + for (i = 0; i <= count_cells + 1; i++) + { + if (!dV_dcell && (i == 0 || i == count_cells + 1)) + continue; + if (iterations > max_iter) + max_iter = iterations; + cell_no = i; + mixrun = j; + if (multi_Dflag) + sprintf(token, + "Transport step %3d. MCDrun %3d. Cell %3d. (Max. iter %3d)", + transport_step, j, i, max_iter); + else + sprintf(token, + "Transport step %3d. Mixrun %3d. Cell %3d. (Max. iter %3d)", + transport_step, j, i, max_iter); + status(0, token); + + if (i == 0 || i == count_cells + 1) + run_reactions(i, kin_time, NOMIX, step_fraction); // nsaver = i + else + run_reactions(i, kin_time, DISP, step_fraction); // nsaver = -2 + if (multi_Dflag) + fill_spec(i); + + /* punch and output file */ + if (ishift == 0 && j == nmix && stag_data->count_stag == 0) + print_punch(i, true); + if (i > 1) + Utilities::Rxn_copy(Rxn_solution_map, -2, i - 1); + saver(); + + /* maybe sorb a surface component... */ + if (ishift == 0 && j == nmix && (stag_data->count_stag == 0 + || Utilities::Rxn_find(Rxn_solution_map, i + 1 + count_cells) == 0)) + { + if (change_surf_count > 0) + { + for (k = 0; k < change_surf_count; k++) + { + if (change_surf[k].cell_no != i) + break; + reformat_surf(change_surf[k].comp_name, + change_surf[k].fraction, + change_surf[k].new_comp_name, + change_surf[k].new_Dw, + change_surf[k].cell_no); + change_surf[k].cell_no = -99; + } + change_surf_count = 0; + } + } + } + + if (!dV_dcell) + Utilities::Rxn_copy(Rxn_solution_map, -2, count_cells); + /* Stagnant zone mixing after completion of each + diffusive/dispersive step ... */ + rate_sim_time_start = + (transport_step - 1) * timest + (j - 1) * stagkin_time; + rate_sim_time = rate_sim_time_start + stagkin_time; + + if (stag_data->count_stag > 0) + { + if (ishift == 0 && j == nmix) + punch_boolean = TRUE; + else + punch_boolean = FALSE; + for (i = 0; i <= count_cells + 1; i++) // allow for stagnant cell mixing with boundary cells + { + mix_stag(i, stagkin_time, punch_boolean, step_fraction); + } + } + if (ishift == 0 && j == nmix && stag_data->count_stag > 0) + { + for (n = 1; n <= stag_data->count_stag; n++) + { + for (i = 1; i <= count_cells; i++) + { + k = i + 1 + n * count_cells; + if (Utilities::Rxn_find(Rxn_solution_map, k) == NULL) + continue; + print_punch(k, false); + } + } + } + } + } + /* + * Advective transport + */ + if (ishift != 0) + { + sprintf(token, "Transport step %3d.", transport_step); + dup_print(token, FALSE); + if (b_c == 1) + rate_sim_time_start = + (transport_step - 1) * timest + (j - 1) * kin_time; + else + rate_sim_time_start = (transport_step - 1) * timest; + rate_sim_time = rate_sim_time_start + kin_time; + + /* halftime kinetics for resident water in first cell ... */ + if (Utilities::Rxn_find(Rxn_kinetics_map, first_c) != NULL && count_cells > 1) + { + cell_no = first_c; + kin_time = kin_time_save / 2; + run_reactions(first_c, kin_time, NOMIX, 0.0); + saver(); + kin_time = kin_time_save; + } + + /* for each cell in column */ + for (i = last_c; i != (first_c - ishift); i -= ishift) + Utilities::Rxn_copy(Rxn_solution_map, i - ishift, i); + + /* if boundary_solutions must be flushed by the flux from the column... + if (ishift == 1 && bcon_last == 3) + solution_duplicate (last_c, last_c + 1); + else if (ishift == -1 && bcon_first == 3) + solution_duplicate (last_c, last_c - 1); + */ + if (transp_surf) + { + for (i = last_c + ishift; i != (first_c - ishift); + i -= ishift) + { + if ((ishift == 1 && i == last_c + 1 && bcon_last != 3) || + (ishift == -1 && i == last_c - 1 && bcon_first != 3)) + continue; + cxxSurface * surface_ptr = Utilities::Rxn_find(Rxn_surface_map, i - ishift); + if (surface_ptr == NULL) + { + if ((Utilities::Rxn_find(Rxn_surface_map, i) != NULL) && + ((i == 0 && bcon_first == 3) + || (i == count_cells + 1 && bcon_last == 3))) + { + Rxn_surface_map.erase(i); + } + continue; + } + if (surface_ptr->Get_transport()) + { + cxxSurface * surface_ptr1 = Utilities::Rxn_find(Rxn_surface_map, i); + if (surface_ptr1 == NULL) + { + cxxSurface surf; + surf.Set_n_user(i); + surf.Set_n_user_end(i); + Rxn_surface_map[i] = surf; + } + if (i == first_c) + { + Rxn_surface_map[i] = mobile_surface_copy(surface_ptr, i, false); + } + else + { + Rxn_surface_map[i] = mobile_surface_copy(surface_ptr, i, true); + } + } + } + } + + /* + * thermal diffusion when nmix = 0... + */ + if ((nmix == 0) && (heat_nmix > 0)) + { + heat_mix(heat_nmix); + /* equilibrate again ... */ + for (i = 1; i <= count_cells; i++) + { + cell_no = i; + set_and_run_wrapper(i, NOMIX, FALSE, i, 0.0); + if (multi_Dflag) + fill_spec(i); + saver(); + } + } + + for (i = 1; i <= count_cells; i++) + { + if (i == first_c && count_cells > 1) + kin_time /= 2; + cell_no = i; + mixrun = 0; + if (multi_Dflag) + sprintf(token, + "Transport step %3d. MCDrun %3d. Cell %3d. (Max. iter %3d)", + transport_step, 0, i, max_iter); + else + sprintf(token, + "Transport step %3d. Mixrun %3d. Cell %3d. (Max. iter %3d)", + transport_step, 0, i, max_iter); + status(0, token); + run_reactions(i, kin_time, NOMIX, step_fraction); + if (multi_Dflag == TRUE) + fill_spec(i); + if (iterations > max_iter) + max_iter = iterations; + if (nmix == 0 && stag_data->count_stag == 0) + print_punch(i, true); + if (i == first_c && count_cells > 1) + kin_time = kin_time_save; + saver(); + + /* maybe sorb a surface component... */ + if (nmix == 0 && (stag_data->count_stag == 0 || + (Utilities::Rxn_find(Rxn_solution_map, i + 1 + count_cells) == 0))) + { + if (change_surf_count > 0) + { + for (k = 0; k < change_surf_count; k++) + { + if (change_surf[k].cell_no != i) + break; + reformat_surf(change_surf[k].comp_name, + change_surf[k].fraction, + change_surf[k].new_comp_name, + change_surf[k].new_Dw, + change_surf[k].cell_no); + change_surf[k].cell_no = -99; + } + change_surf_count = 0; + } + } + + /* If nmix is zero, stagnant zone mixing after + advective step ... */ + if ((nmix == 0) && (stag_data->count_stag > 0)) + { + mix_stag(i, stagkin_time, TRUE, step_fraction); + } + } + if (nmix == 0 && stag_data->count_stag > 0) + { + for (n = 1; n <= stag_data->count_stag; n++) + { + for (i = 1; i <= count_cells; i++) + { + k = i + 1 + n * count_cells; + if (Utilities::Rxn_find(Rxn_solution_map, k) == NULL) + continue; + print_punch(k, false); + } + } + } + } + /* + * Further dispersive and diffusive transport + */ + if (b_c != 1) + j = 1; + for (j = j; j <= nmix; j++) + { + if (multi_Dflag && j == nmix && (transport_step % print_modulus == 0)) + { + sprintf(token, + "Transport step %3d. Multicomponent diffusion run %3d.", + transport_step, j); + dup_print(token, FALSE); + } + else if (!multi_Dflag) + { + sprintf(token, "Transport step %3d. Mixrun %3d.", + transport_step, j); + dup_print(token, FALSE); + } + rate_sim_time_start = + (transport_step - 1) * timest + (j - 1) * kin_time; + if (ishift != 0) + rate_sim_time_start += kin_time; + rate_sim_time = rate_sim_time_start + kin_time; + + if (heat_nmix > 0) + { + heat_mix(heat_nmix); + /* equilibrate again ... */ + for (i = 1; i <= count_cells; i++) + { + cell_no = i; + set_and_run_wrapper(i, NOMIX, FALSE, i, 0.0); + if (multi_Dflag) + fill_spec(i); + saver(); + } + } + if (transp_surf) + { + if (disp_surf(stagkin_time) == ERROR) + error_msg("Error in surface transport, stopping.", STOP); + } + + if (multi_Dflag == TRUE) + multi_D(stagkin_time, 1, FALSE); + + /* for each cell in column */ + for (i = 0; i <= count_cells + 1; i++) + { + if (!dV_dcell && (i == 0 || i == count_cells + 1)) + continue; + if (iterations > max_iter) + max_iter = iterations; + cell_no = i; + mixrun = j; + if (multi_Dflag) + sprintf(token, + "Transport step %3d. MCDrun %3d. Cell %3d. (Max. iter %3d)", + transport_step, j, i, max_iter); + else + sprintf(token, + "Transport step %3d. Mixrun %3d. Cell %3d. (Max. iter %3d)", + transport_step, j, i, max_iter); + status(0, token); + + if (i == 0 || i == count_cells + 1) + run_reactions(i, kin_time, NOMIX, step_fraction); + else + run_reactions(i, kin_time, DISP, step_fraction); + if (multi_Dflag == TRUE) + fill_spec(i); + if (j == nmix && stag_data->count_stag == 0) + print_punch(i, true); + if (i > 1) + Utilities::Rxn_copy(Rxn_solution_map, -2, i - 1); + saver(); + + /* maybe sorb a surface component... */ + if ((j == nmix) && ((stag_data->count_stag == 0) + || (Utilities::Rxn_find(Rxn_solution_map, i + 1 + count_cells) == 0))) + { + if (change_surf_count > 0) + { + for (k = 0; k < change_surf_count; k++) + { + if (change_surf[k].cell_no != i) + break; + reformat_surf(change_surf[k].comp_name, + change_surf[k].fraction, + change_surf[k].new_comp_name, + change_surf[k].new_Dw, + change_surf[k].cell_no); + change_surf[k].cell_no = -99; + } + change_surf_count = 0; + } + } + } + if (!dV_dcell) + Utilities::Rxn_copy(Rxn_solution_map, -2, count_cells); + /* Stagnant zone mixing after completion of each + diffusive/dispersive step ... */ + rate_sim_time_start = + (transport_step - 1) * timest + (j - 1) * stagkin_time; + rate_sim_time = rate_sim_time_start + stagkin_time; + + if (stag_data->count_stag > 0) + { + if (j == nmix) + punch_boolean = TRUE; + else + punch_boolean = FALSE; + for (i = 0; i <= count_cells + 1; i++) // allow for stagnant cell mixing with boundary cells + mix_stag(i, stagkin_time, punch_boolean, step_fraction); + } + if (j == nmix && stag_data->count_stag > 0) + { + for (n = 1; n <= stag_data->count_stag; n++) + { + for (i = 1; i <= count_cells; i++) + { + k = i + 1 + n * count_cells; + if (Utilities::Rxn_find(Rxn_solution_map, k) == NULL) + continue; + cell_no = k; + print_punch(k, false); + } + } + } + } + if (dump_modulus != 0 && (transport_step % dump_modulus) == 0) + dump(); + } + screen_msg("\n"); + + if (multi_Dflag && moles_added[0].moles > 0) + { + sprintf(token, + "\nFor balancing negative concentrations in MCD, added in total to the system:"); + if (phrq_io) + phrq_io->warning_msg(token); + for (i = 0; i < count_elements; i++) + { + if (!moles_added[i].moles) + break; + sprintf(token, + "\t %.4e moles %s.", + (double)moles_added[i].moles, moles_added[i].name); + if (phrq_io) + phrq_io->warning_msg(token); + } + } + } + catch (...) + { + transport_cleanup(); + throw; + } + transport_cleanup(); + initial_total_time += rate_sim_time; + rate_sim_time = 0; + mass_water_switch = FALSE; + return (OK); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +transport_cleanup(void) +/* ---------------------------------------------------------------------- */ +{ + int i; + /* + * free mix structures + */ + Dispersion_mix_map.clear(); + if ((stag_data->exch_f > 0) && (stag_data->count_stag == 1)) + { + Rxn_mix_map.clear(); + } + + if (heat_nmix > 0) + { + heat_mix_array = (LDBLE *)free_check_null(heat_mix_array); + temp1 = (LDBLE *)free_check_null(temp1); + temp2 = (LDBLE *)free_check_null(temp2); + } + if (multi_Dflag) + { + for (i = 0; i < all_cells; i++) + { + sol_D[i].spec = (struct spec *) free_check_null(sol_D[i].spec); + } + sol_D = (struct sol_D *) free_check_null(sol_D); + for (int i = 0; i < all_cells; i++) + { + ct[i].v_m = (struct V_M *) free_check_null(ct[i].v_m); + ct[i].v_m_il = (struct V_M *) free_check_null(ct[i].v_m_il); + ct[i].J_ij = (struct J_ij *) free_check_null(ct[i].J_ij); + ct[i].J_ij_il = (struct J_ij *) free_check_null(ct[i].J_ij_il); + } + ct = (struct CT *) free_check_null(ct); + for (int i = 0; i < count_elements; i++) + { + moles_added[i].name = (char *)free_check_null(moles_added[i].name); + } + moles_added = (struct MOLES_ADDED *) free_check_null(moles_added); + } + current_cells = (struct CURRENT_CELLS *) free_check_null(current_cells); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +print_punch(int i, boolean active) +/* ---------------------------------------------------------------------- */ +{ + if ((!(cell_data[i].punch && (transport_step % punch_modulus == 0)) && + !(cell_data[i].print && (transport_step % print_modulus == 0))) || + (bcon_first == 2 && i == 0) || + (bcon_last == 2 && i == count_cells + 1)) + return; + if (!active) + run_reactions(i, 0, NOMIX, 0); + cell_no = i; + use.Set_kinetics_ptr(Utilities::Rxn_find(Rxn_kinetics_map, i)); + if (use.Get_kinetics_ptr() != NULL) + { + use.Set_n_kinetics_user(i); + use.Set_kinetics_in(true); + } + if (cell_data[i].punch && (transport_step % punch_modulus == 0)) + punch_all(); + if (cell_data[i].print && (transport_step % print_modulus == 0)) + print_all(); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +init_mix(void) +/* ---------------------------------------------------------------------- */ +{ + LDBLE lav, dav = 0.0, mf12, maxmix, corr_disp, diffc_here, mD; + bool warning = false; + int i, l_nmix; + LDBLE *m, *m1; + m = (LDBLE *)PHRQ_malloc((count_cells + 1) * sizeof(LDBLE)); + if (m == NULL) + malloc_error(); + m1 = (LDBLE *)PHRQ_malloc((count_cells + 1) * sizeof(LDBLE)); + if (m1 == NULL) + malloc_error(); + for (i = 0; i < count_cells + 1; i++) + { + m[i] = 0; + m1[i] = 0; + } + corr_disp = 1.; + if (correct_disp == TRUE && ishift != 0) + { + if (bcon_first == 3) + corr_disp += 1. / count_cells; + if (bcon_last == 3) + corr_disp += 1. / count_cells; + } + maxmix = 0.0; + if (multi_Dflag) + { + if (dV_dcell) + dV_dcell = (cell_data[count_cells + 1].potV - cell_data[0].potV) / count_cells; + for (i = 1; i <= count_cells; i++) + { + // estimate the number of mixes + if (i < count_cells) + { + lav = (cell_data[i + 1].length + cell_data[i].length) / 2; + mD = diffc_max * timest / (lav * lav); + if (mD > maxmix) + maxmix = mD; + } + if (ishift != 0) + { + /* define dispersive mixing (diffusion is done in multi_D), + with harmonic mean of cell properties (manual eqn 49): + M1 = -a1 * (v1 * timest / 2) * A1 * (c12 - c1) / (h1 / 2) + a1 is dispersivity in cell 1, A1 = V1 / h1, v1 * timest / 2 = h1 / 2. + All cells have equal V in advective transport, delta(c) = M1 / V1. + */ + if (i < count_cells) + { + if (cell_data[i].disp) + dav = cell_data[i].length / cell_data[i].disp; + if (cell_data[i + 1].disp) + dav += cell_data[i + 1].length / cell_data[i + 1].disp; + if (dav) + m1[i] = 2 * corr_disp / dav; /* m1[i] has mixf with higher cell */ + } + if (i > 1) + { + if (cell_data[i].disp) + dav = cell_data[i].length / cell_data[i].disp; + if (cell_data[i - 1].disp) + dav += cell_data[i - 1].length / cell_data[i - 1].disp; + if (dav) + m[i] = 2 * corr_disp / dav; /* m[i] has mixf with lower cell */ + } + mf12 = m[i] + m1[i]; + if (mf12 > maxmix) + maxmix = mf12; + } + } + /* + * Also for boundary cells + */ + if (bcon_first == 1) + { + mD = 2 * diffc_max * timest / (cell_data[1].length * cell_data[1].length); + if (mD > maxmix) + maxmix = mD; + if (ishift != 0) + { + m[1] = 2 * cell_data[1].disp / cell_data[1].length * corr_disp; + mf12 = m[1] + m1[1]; + if (mf12 > maxmix) + maxmix = mf12; + } + } + if (bcon_last == 1) + { + mD = 2 * diffc_max * timest / (cell_data[count_cells].length * cell_data[count_cells].length); + if (mD > maxmix) + maxmix = mD; + if (ishift != 0) + { + m1[count_cells] = 2 * cell_data[count_cells].disp / cell_data[count_cells].length * corr_disp; + mf12 = m[count_cells] + m1[count_cells]; + if (mf12 > maxmix) + maxmix = mf12; + } + } + /* + * Find number of mixes + */ + if (maxmix == 0) + { + l_nmix = 0; + if (mcd_substeps > 1 && stag_data->count_stag > 0) + l_nmix = (int) ceil(mcd_substeps); + } + else + { + if (bcon_first == 1 || bcon_last == 1) + l_nmix = 1 + (int) floor(2.25 * maxmix); + else + l_nmix = 1 + (int) floor(1.5 * maxmix); + + if (ishift != 0 && (bcon_first == 1 || bcon_last == 1)) + { + if (l_nmix < 2) + l_nmix = 2; + } + if (mcd_substeps > 1) + l_nmix = (int) ceil(l_nmix * mcd_substeps); + + for (i = 1; i <= count_cells; i++) + { + m[i] /= l_nmix; + m1[i] /= l_nmix; + /* + * Fill mix structure + */ + cxxMix temp_mix; + temp_mix.Set_n_user(i); + temp_mix.Set_n_user_end(i); + + temp_mix.Add(i - 1, m[i]); + temp_mix.Add(i + 1, m1[i]); + temp_mix.Add(i, 1.0 - m[i] - m1[i]); + Dispersion_mix_map[i] = temp_mix; + } + } + m = (LDBLE *)free_check_null(m); + m1 = (LDBLE *)free_check_null(m1); + return l_nmix; + } + else // multi_D false + { + diffc_here = 2 * diffc_tr * timest; + /* + * Define mixing factors among inner cells + */ + for (i = 1; i <= count_cells; i++) + { + if (i < count_cells) + { + // find mix with higher numbered cell... + if (ishift != 0) + { + if (cell_data[i].disp) + dav = cell_data[i].length / cell_data[i].disp; + if (cell_data[i + 1].disp) + dav += cell_data[i + 1].length / cell_data[i + 1].disp; + if (dav) + m1[i] = 2 / dav; + } + // add diffusive mixf... + m1[i] += diffc_here / + (cell_data[i].length * cell_data[i].length + cell_data[i].length * cell_data[i + 1].length); + m1[i] *= corr_disp; /* m1[i] has mixf with higher cell */ + } + if (i > 1) + { + // and with lower numbered cell... + if (ishift != 0) + { + if (cell_data[i].disp) + dav = cell_data[i].length / cell_data[i].disp; + if (cell_data[i - 1].disp) + dav += cell_data[i - 1].length / cell_data[i - 1].disp; + if (dav) + m[i] = 2 / dav; + } + // add diffusive mixf... + m[i] += diffc_here / + (cell_data[i].length * cell_data[i].length + cell_data[i].length * cell_data[i - 1].length); + m[i] *= corr_disp; /* m[i] has mixf with lower numbered cell */ + if (m[i] != m1[i - 1] && !warning && (!dav || m[i] / (2 / dav) > 1.00001)) + { + warning_msg("Unequal cell-lengths may give mass-balance error, consider using -multi_D"); + warning = true; + } + } + mf12 = m[i] + m1[i]; + if (mf12 > maxmix) + maxmix = mf12; + } + /* + * Also for boundary cells + */ + if (bcon_first == 1) + { + m[1] = diffc_here / (cell_data[1].length * cell_data[1].length); + if (ishift != 0) + m[1] += cell_data[1].disp / cell_data[1].length; + mf12 = m[1] + m1[1]; + if (mf12 > maxmix) + maxmix = mf12; + } + if (bcon_last == 1) + { + m1[count_cells] = diffc_here / (cell_data[count_cells].length * cell_data[count_cells].length); + if (ishift != 0) + m1[count_cells] += cell_data[count_cells].disp / cell_data[count_cells].length; + mf12 = m[count_cells] + m1[count_cells]; + if (mf12 > maxmix) + maxmix = mf12; + } + /* + * Find number of mixes + */ + if (maxmix == 0) + l_nmix = 0; + else + { + l_nmix = 1 + (int) floor(1.5 * maxmix); + + if ((ishift != 0) && ((bcon_first == 1) || (bcon_last == 1))) + { + if (l_nmix < 2) + l_nmix = 2; + } + + for (i = 1; i <= count_cells; i++) + { + m[i] /= l_nmix; + m1[i] /= l_nmix; + /* + * Fill mix structure + */ + cxxMix temp_mix; + temp_mix.Set_n_user(i); + temp_mix.Set_n_user_end(i); + + temp_mix.Add(i - 1, m[i]); + temp_mix.Add(i + 1, m1[i]); + temp_mix.Add(i, 1.0 - m[i] - m1[i]); + Dispersion_mix_map[i] = temp_mix; + } + } + m = (LDBLE *)free_check_null(m); + m1 = (LDBLE *)free_check_null(m1); + return (l_nmix); + } +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +mix_stag(int i, LDBLE kin_time, int l_punch, LDBLE step_fraction) +/* ---------------------------------------------------------------------- */ +{ + int j, n, k; + LDBLE t_imm; + cxxSolution *ptr_imm, *ptr_m; + k = -1000; // compiler says k may be undefined + ptr_imm = NULL; + boolean done_mixing = false; + /* + * Kinetics in transport cell is done while transporting + */ + for (n = 1; n <= stag_data->count_stag; n++) + { + if (i == 0 || i == count_cells + 1) + { + use.Set_mix_ptr(NULL); + use.Set_mix_in(false); + use.Set_mix_ptr(Utilities::Rxn_find(Rxn_mix_map, i)); + if (use.Get_mix_ptr()) + { + for (std::map < int, LDBLE >::const_iterator it = use.Get_mix_ptr()->Get_mixComps().begin(); + it != use.Get_mix_ptr()->Get_mixComps().end(); it++) + { + if (it->first > i && it->first < all_cells) + { + k = it->first; + ptr_imm = Utilities::Rxn_find(Rxn_solution_map, k); + break; + } + } + } + } + else + { + k = i + 1 + n * count_cells; + if (k < all_cells) + ptr_imm = Utilities::Rxn_find(Rxn_solution_map, k); + } + if (ptr_imm != NULL) + { + if (n == 1) + { + if (heat_nmix > 0) + { + ptr_m = Utilities::Rxn_find(Rxn_solution_map, i); + t_imm = + heat_mix_f_imm * ptr_m->Get_tc() + (1 - heat_mix_f_imm) * ptr_imm->Get_tc(); + ptr_m->Set_tc(heat_mix_f_m * ptr_imm->Get_tc() + (1 - heat_mix_f_m) * ptr_m->Get_tc()); + cell_data[i].temp = ptr_m->Get_tc(); + cell_data[k].temp = t_imm = ptr_imm->Get_tc(); + /* equilibrate again ... */ + cell_no = i; + set_and_run_wrapper(i, NOMIX, FALSE, i, 0.0); + if (multi_Dflag == TRUE) + fill_spec(cell_no); + saver(); + cell_no = k; + set_and_run_wrapper(k, NOMIX, FALSE, k, 0.0); + if (multi_Dflag == TRUE) + fill_spec(cell_no); + saver(); + } + /* + * Mobile cell, kinetics already done ... + */ + cell_no = i; + if (transp_surf) + { + if (diff_stag_surf(i) == ERROR) + error_msg("Error in surface transport, stopping.", + STOP); + } + if (multi_Dflag == TRUE) + multi_D(1.0, i, TRUE); + set_and_run_wrapper(i, STAG, FALSE, -2, 0.0); + if (multi_Dflag == TRUE) + fill_spec(cell_no); + saver(); // save solution i in -2, original can be used in other stagnant mixes + if (l_punch) + print_punch(i, true); + + /* maybe sorb a surface component... */ + if (l_punch && change_surf_count) + { + for (j = 0; j < change_surf_count; j++) + { + if (change_surf[j].cell_no != i) + break; + reformat_surf(change_surf[j].comp_name, + change_surf[j].fraction, + change_surf[j].new_comp_name, + change_surf[j].new_Dw, + change_surf[j].cell_no); + change_surf[j].cell_no = -99; + } + change_surf_count = 0; + } + } + + cell_no = k; + run_reactions(k, kin_time, STAG, step_fraction); + if (multi_Dflag == TRUE) + fill_spec(cell_no); + saver(); // save solution k in -2 - k, original k can be used in other stagnant mixes + + /* maybe sorb a surface component... */ + if (l_punch && change_surf_count) + { + for (j = 0; j < change_surf_count; j++) + { + if (change_surf[j].cell_no != k) + break; + reformat_surf(change_surf[j].comp_name, + change_surf[j].fraction, + change_surf[j].new_comp_name, + change_surf[j].new_Dw, + change_surf[j].cell_no); + change_surf[j].cell_no = -99; + } + change_surf_count = 0; + } + + done_mixing = true; + } + else if (n == 1 && l_punch) + print_punch(i, false); + } + + if (done_mixing) // after all mixing is done, the temporal solution becomes the original for the next timestep + { + for (n = 1; n <= stag_data->count_stag; n++) + { + k = i + 1 + n * count_cells; + if (Utilities::Rxn_find(Rxn_solution_map, k) != 0) + { + Utilities::Rxn_copy(Rxn_solution_map, -2 - k, k); + if (n == 1) + Utilities::Rxn_copy(Rxn_solution_map, -2, i); + } + } + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +init_heat_mix(int l_nmix) +/* ---------------------------------------------------------------------- */ +{ + LDBLE lav, mixf, maxmix, corr_disp; + int i, k, n; + int l_heat_nmix; + LDBLE t0; + /* + * Check for need to model thermal diffusion... + */ + if (heat_diffc <= diffc) + return (0); + if (count_cells < 2) + return (0); + + l_heat_nmix = 0; + t0 = Utilities::Rxn_find(Rxn_solution_map, 0)->Get_tc(); + for (i = 1; i <= count_cells; i++) + { + if (fabs(cell_data[i].temp - t0) > 1.0) + { + l_heat_nmix = 1; + break; + } + } + if (l_heat_nmix == 0) + { + if (fabs(Utilities::Rxn_find(Rxn_solution_map, count_cells + 1)->Get_tc() - t0) > 1.0) + l_heat_nmix = 1; + for (n = 1; n <= stag_data->count_stag; n++) + { + for (i = 1; i < count_cells; i++) + { + k = i + 1 + n * count_cells; + if (Utilities::Rxn_find(Rxn_solution_map, k) != 0) + { + if (fabs(cell_data[k].temp - t0) > 1.0) + { + l_heat_nmix = 1; + break; + } + } + } + } + } + if (l_heat_nmix == 0) + return (0); + /* + * Initialize arrays... + */ + heat_mix_array = (LDBLE *)PHRQ_malloc((count_cells + 2) * sizeof(LDBLE)); + if (heat_mix_array == NULL) + malloc_error(); + + temp1 = (LDBLE *)PHRQ_malloc((count_cells + 2) * sizeof(LDBLE)); + if (temp1 == NULL) + malloc_error(); + + temp2 = (LDBLE *)PHRQ_malloc((count_cells + 2) * sizeof(LDBLE)); + if (temp2 == NULL) + malloc_error(); + /* + * Define mixing factors among inner cells... + */ + corr_disp = 1.; + if (correct_disp == TRUE && ishift != 0) + { + if (bcon_first == 3) + corr_disp += 1. / count_cells; + if (bcon_last == 3) + corr_disp += 1. / count_cells; + } + if (l_nmix > 0) + corr_disp /= l_nmix; + maxmix = 0.0; + for (i = 1; i < count_cells; i++) + { + lav = (cell_data[i + 1].length + cell_data[i].length) / 2; + mixf = + (heat_diffc - + diffc_tr) * timest * corr_disp / tempr / (lav * lav); + if (mixf > maxmix) + maxmix = mixf; + heat_mix_array[i + 1] = mixf; /* m[i] has mixf with lower cell */ + } + /* + * Also for boundary cells + */ + if (bcon_first == 1) + { + lav = cell_data[1].length; + mixf = + (heat_diffc - + diffc_tr) * timest * corr_disp / tempr / (lav * lav); + if (2 * mixf > maxmix) + maxmix = 2 * mixf; + heat_mix_array[1] = 2 * mixf; + } + else + heat_mix_array[1] = 0; + + if (bcon_last == 1) + { + lav = cell_data[count_cells].length; + mixf = + (heat_diffc - + diffc_tr) * timest * corr_disp / tempr / (lav * lav); + if (2 * mixf > maxmix) + maxmix = 2 * mixf; + heat_mix_array[count_cells + 1] = 2 * mixf; + } + else + heat_mix_array[count_cells + 1] = 0; + /* + * Find number of mixes + */ + if (maxmix == 0) + l_heat_nmix = 0; + else + { + l_heat_nmix = 1 + (int) floor(3.0 * maxmix); + for (i = 1; i <= count_cells + 1; i++) + heat_mix_array[i] /= l_heat_nmix; + } + + return (l_heat_nmix); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +heat_mix(int l_heat_nmix) +/* ---------------------------------------------------------------------- */ +{ + int i, j; + + for (i = 1; i <= count_cells; i++) + temp1[i] = Utilities::Rxn_find(Rxn_solution_map, i)->Get_tc(); + temp1[0] = Utilities::Rxn_find(Rxn_solution_map, 0)->Get_tc(); + temp1[count_cells + 1] = + Utilities::Rxn_find(Rxn_solution_map, (count_cells + 1))->Get_tc(); + + for (i = 1; i <= l_heat_nmix; i++) + { + for (j = 1; j <= count_cells; j++) + temp2[j] = + heat_mix_array[j] * temp1[j - 1] + heat_mix_array[j + 1] * + temp1[j + 1] + (1 - heat_mix_array[j] - + heat_mix_array[j + 1]) * temp1[j]; + for (j = 1; j <= count_cells; j++) + temp1[j] = temp2[j]; + } + + for (i = 1; i <= count_cells; i++) + { + cell_data[i].temp = temp1[i]; + Utilities::Rxn_find(Rxn_solution_map, i)->Set_tc(temp1[i]); + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +set_initial_moles(int i) +/* ---------------------------------------------------------------------- */ +{ + cxxKinetics *kinetics_ptr; + char token[MAX_LENGTH], token1[MAX_LENGTH], *ptr; + int j, k, l; + /* + * Pure phase assemblage + */ + { + cxxPPassemblage * pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, i); + if (pp_assemblage_ptr != NULL) + { + std::map::iterator it; + it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); + for (; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) + { + it->second.Set_initial_moles(it->second.Get_moles()); + if (it->second.Get_initial_moles() < 0) + it->second.Set_initial_moles(0.0); + } + } + } + /* + * Gas phase + */ + { + cxxGasPhase * gas_phase_ptr = Utilities::Rxn_find(Rxn_gas_phase_map, i); + if (gas_phase_ptr != NULL) + { + std::vector gc = gas_phase_ptr->Get_gas_comps(); + for (size_t l = 0; l < gc.size(); l++) + { + gc[l].Set_initial_moles(gc[l].Get_moles()); + } + gas_phase_ptr->Set_gas_comps(gc); + } + } + /* + * Kinetics + */ + kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, i); + if (kinetics_ptr != NULL) + { + for (j = 0; j < (int) kinetics_ptr->Get_kinetics_comps().size(); j++) + { + cxxKineticsComp *kinetics_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[j]); + kinetics_comp_ptr->Set_initial_moles(kinetics_comp_ptr->Get_m()); + } + } + /* + * Solid solutions + */ + { + cxxSSassemblage *ss_assemblage_ptr = Utilities::Rxn_find(Rxn_ss_assemblage_map, i); + if (ss_assemblage_ptr != NULL) + { + std::vector ss_ptrs = ss_assemblage_ptr->Vectorize(); + for (k = 0; k < (int) ss_ptrs.size(); k++) + { + cxxSS * ss_ptr = ss_ptrs[k]; + for (j = 0; j < (int) ss_ptr->Get_ss_comps().size(); j++) + { + cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[j]); + comp_ptr->Set_init_moles(comp_ptr->Get_moles()); + } + } + } + } + /* + * For interlayer diffusion: add tiny bit of exchanger if absent + */ + cxxExchange * exchange_ptr = Utilities::Rxn_find(Rxn_exchange_map, i); + if (interlayer_Dflag && exchange_ptr == NULL) + { + cxxExchange temp_exchange; + temp_exchange.Set_n_user_both(i); + temp_exchange.Set_description("Interlayer diffusion: added 2e-10 moles X-"); + use.Set_exchange_in(true); + use.Set_n_exchange_user(i); + + temp_exchange.Set_new_def(true); + temp_exchange.Set_solution_equilibria(true); + temp_exchange.Set_n_solution(i); + + cxxExchComp comp; + count_elts = 0; + paren_count = 0; + strcpy(token, "X"); + ptr = token; + get_elts_in_species(&ptr, 2e-10); + ptr = token; + LDBLE z; + get_token(&ptr, token1, &z, &l); + comp.Set_formula(token1); + comp.Set_formula_z(z); + comp.Set_totals(elt_list_NameDouble()); + comp.Set_charge_balance(0.0); + temp_exchange.Get_exchange_comps().push_back(comp); + Rxn_exchange_map[i] = temp_exchange; + + state = INITIAL_EXCHANGE; + initial_exchangers(TRUE); + state = TRANSPORT; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +fill_spec(int l_cell_no) +/* ---------------------------------------------------------------------- */ +{ + /* copy species activities into sol_D.spec... */ + + int i, i2, count_spec, count_exch_spec; + char token[MAX_LENGTH]; + const char * name; + struct species *s_ptr, *s_ptr2; + struct master *master_ptr; + LDBLE dum, dum2; + LDBLE lm; + LDBLE por, por_il, viscos_f, viscos_il_f, viscos; + bool x_max_done = false; + + s_ptr2 = NULL; + + sol_D[l_cell_no].spec = + (struct spec *) free_check_null(sol_D[l_cell_no].spec); + sol_D[l_cell_no].spec = + (struct spec *) PHRQ_malloc((size_t) count_species_list * + sizeof(struct spec)); + if (sol_D[l_cell_no].spec == NULL) + malloc_error(); + sol_D[l_cell_no].spec_size = count_species_list; + { + for (int i = 0; i < count_species_list; i++) + { + sol_D[l_cell_no].spec[i].name = NULL; + sol_D[l_cell_no].spec[i].aq_name = NULL; + sol_D[l_cell_no].spec[i].type = -1; + sol_D[l_cell_no].spec[i].a = 0.0; + sol_D[l_cell_no].spec[i].lm = 0.0; + sol_D[l_cell_no].spec[i].lg = 0.0; + sol_D[l_cell_no].spec[i].c = 0.0; + sol_D[l_cell_no].spec[i].z = 0.0; + sol_D[l_cell_no].spec[i].Dwt = 0.0; + sol_D[l_cell_no].spec[i].dw_t = 0.0; + sol_D[l_cell_no].spec[i].erm_ddl = 0.0; + } + } + + sol_D[l_cell_no].tk_x = tk_x; + + viscos_f = viscos_il_f = 1.0; + if (l_cell_no == 0) + { + por = cell_data[1].por; + por_il = cell_data[1].por_il; + } + else if (l_cell_no == count_cells + 1) + { + por = cell_data[count_cells].por; + por_il = cell_data[count_cells].por_il; + } + else + { + por = cell_data[l_cell_no].por; + por_il = cell_data[l_cell_no].por_il; + } + if (por < multi_Dpor_lim) + por = viscos_f = 0.0; + + if (por_il < interlayer_Dpor_lim) + por_il = viscos_il_f = 0.0; + /* + * correct diffusion coefficient for temperature and viscosity, D_T = D_298 * Tk * viscos_298 / (298 * viscos) + * modify viscosity effect: Dw(TK) = Dw(298.15) * exp(dw_t / TK - dw_t / 298.15), SC data from Robinson and Stokes, 1959 + */ + viscos = viscos_0; + /* + * put temperature factor in por_factor which corrects for porous medium... + */ + viscos_f *= tk_x * viscos_0_25 / (298.15 * viscos); + viscos_il_f *= tk_x * viscos_0_25 / (298.15 * viscos); + sol_D[l_cell_no].viscos_f = tk_x * viscos_0_25 / (298.15 * viscos); + + count_spec = count_exch_spec = 0; + /* + * sort species by name... + */ + if (count_species_list > 0) + qsort(&species_list[0], (size_t) count_species_list, + (size_t) sizeof(struct species_list), sort_species_name); + + for (i = 0; i < count_species_list; i++) + { + /* + * copy species data + */ + s_ptr = species_list[i].s; + + if (s_ptr->type == EX && !interlayer_Dflag) + continue; + if (s_ptr->type == SURF) + continue; + if (i > 0 && strcmp(s_ptr->name, species_list[i - 1].s->name) == 0) + continue; + if (s_ptr == s_h2o) + continue; + + if (s_ptr->type == EX) + { + if (s_ptr->moles > 1e-30) + { + /* find exchanger's name, use only master exchanger 'X' */ + if (species_list[i].master_s->secondary != NULL) + master_ptr = species_list[i].master_s->secondary; + else + master_ptr = species_list[i].master_s->primary; + if (s_ptr->equiv != 0.0) + dum = fabs(s_ptr->equiv) / master_ptr->total; + else + { + if (species_list[i].master_s->z == 0) + dum = 1 / master_ptr->total; + else + dum = 1; + } + name = master_ptr->elt->name; + if (strcmp(name, "X") != 0) + { + if (!warn_MCD_X) + { + sprintf(token, + "MCD found more than 1 exchanger, uses X for interlayer diffusion."); + warning_msg(token); + warn_MCD_X = 1; + } + continue; + } + dum2 = s_ptr->moles * dum; /* equivalent fraction */ + sol_D[l_cell_no].spec[count_spec].name = s_ptr->name; + //string_hsave(s_ptr->name); + sol_D[l_cell_no].spec[count_spec].type = EX; + sol_D[l_cell_no].spec[count_spec].c = dum2; + sol_D[l_cell_no].spec[count_spec].lg = s_ptr->lg - log10(dum); + sol_D[l_cell_no].spec[count_spec].a = dum2 * pow(10, sol_D[l_cell_no].spec[count_spec].lg); + sol_D[l_cell_no].exch_total = master_ptr->total; + if (transport_step == 0 && !x_max_done) + { + x_max_done = true; + dum = master_ptr->total / Utilities::Rxn_find(Rxn_solution_map, l_cell_no)->Get_mass_water(); + if (dum > sol_D[1].x_max) + sol_D[1].x_max = dum; + } + + /* find the aqueous species in the exchange reaction... */ + for (i2 = 0; (s_ptr->rxn->token[i2].s != NULL); i2++) + { + if ((s_ptr2 = s_ptr->rxn->token[i2].s)->type == AQ) + break; + } + /* copy its name and Dw and charge... */ + sol_D[l_cell_no].spec[count_spec].aq_name = s_ptr2->name; + //string_hsave(s_ptr2->name); + sol_D[l_cell_no].spec[count_spec].z = s_ptr2->z; + if (s_ptr2->dw == 0) + sol_D[l_cell_no].spec[count_spec].Dwt = + default_Dw * viscos_il_f; + else + { + if (s_ptr2->dw_t) + { + sol_D[l_cell_no].spec[count_spec].Dwt = s_ptr2->dw * + exp(s_ptr2->dw_t / 298.15 - s_ptr2->dw_t / tk_x) * viscos_il_f; + sol_D[l_cell_no].spec[count_spec].dw_t = s_ptr2->dw_t; + } + else + sol_D[l_cell_no].spec[count_spec].Dwt = s_ptr2->dw * viscos_il_f; + } + count_exch_spec++; + count_spec++; + } + continue; + } + + lm = s_ptr->lm; + if (lm > MIN_LM) + { + sol_D[l_cell_no].spec[count_spec].name = s_ptr->name; + sol_D[l_cell_no].spec[count_spec].type = AQ; + sol_D[l_cell_no].spec[count_spec].c = + s_ptr->moles / mass_water_aq_x; + sol_D[l_cell_no].spec[count_spec].a = under(lm + s_ptr->lg); + sol_D[l_cell_no].spec[count_spec].lm = lm; + sol_D[l_cell_no].spec[count_spec].lg = s_ptr->lg; + sol_D[l_cell_no].spec[count_spec].z = s_ptr->z; + if (s_ptr->dw == 0) + sol_D[l_cell_no].spec[count_spec].Dwt = default_Dw * viscos_f; + else + { + if (s_ptr->dw_t) + { + sol_D[l_cell_no].spec[count_spec].Dwt = s_ptr->dw * + exp(s_ptr->dw_t / tk_x - s_ptr->dw_t / 298.15) * viscos_f; + sol_D[l_cell_no].spec[count_spec].dw_t = s_ptr->dw_t; + } + else + sol_D[l_cell_no].spec[count_spec].Dwt = s_ptr->dw * viscos_f; + } + if (correct_Dw) + { + calc_SC(); // note that neutral species are corrected as if z = 1, but is viscosity-dependent + sol_D[l_cell_no].spec[count_spec].Dwt = s_ptr->dw_corr * viscos_f; + } + if (l_cell_no <= count_cells + 1 && sol_D[l_cell_no].spec[count_spec].Dwt * pow(por, multi_Dn) > diffc_max) + diffc_max = sol_D[l_cell_no].spec[count_spec].Dwt * pow(por, multi_Dn); + sol_D[l_cell_no].spec[count_spec].erm_ddl = s_ptr->erm_ddl; + + count_spec++; + } + } + sol_D[l_cell_no].spec = + (struct spec *) PHRQ_realloc(sol_D[l_cell_no].spec, + (size_t) count_spec * + sizeof(struct spec)); + + if (sol_D[l_cell_no].spec == NULL) + malloc_error(); + { + if (count_spec > sol_D[l_cell_no].spec_size) + { + for (int i = sol_D[l_cell_no].spec_size; i < count_spec; i++) + { + sol_D[l_cell_no].spec[i].name = NULL; + sol_D[l_cell_no].spec[i].aq_name = NULL; + sol_D[l_cell_no].spec[i].type = -1; + sol_D[l_cell_no].spec[i].a = 0.0; + sol_D[l_cell_no].spec[i].lm = 0.0; + sol_D[l_cell_no].spec[i].lg = 0.0; + sol_D[l_cell_no].spec[i].c = 0.0; + sol_D[l_cell_no].spec[i].z = 0.0; + sol_D[l_cell_no].spec[i].Dwt = 0.0; + sol_D[l_cell_no].spec[i].dw_t = 0.0; + sol_D[l_cell_no].spec[i].erm_ddl = 0.0; + } + } + sol_D[l_cell_no].spec_size = count_spec; + } + sol_D[l_cell_no].count_spec = count_spec; + sol_D[l_cell_no].count_exch_spec = count_exch_spec; + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +sort_species_name(const void *ptr1, const void *ptr2) +/* ---------------------------------------------------------------------- */ +{ + const struct species_list *nptr1, *nptr2; + + nptr1 = (const struct species_list *) ptr1; + nptr2 = (const struct species_list *) ptr2; + + return (strcmp(nptr1->s->name, nptr2->s->name)); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +multi_D(LDBLE DDt, int mobile_cell, int stagnant) +/* ---------------------------------------------------------------------- */ +{ + /* + * 1. determine mole transfer (mol/s) of solute species for the interface between 2 cells. + * 2. sum up as mole transfer of master_species + * 3. add moles of master_species to the 2 cells + * NOTE. Define the water content of stagnant cells relative to the + * mobile cell (with, for example, 1 kg water) + * Define properties of each interface only 1 time with MIX. + * If an electrical field is applied (dV_dcell != 0), the currents j_i = current_cells[i].ele + dif (C * s) + are calculated for all cells. Then the ele part from cell 0 -> 1 is calculated: + current_x = (j_0d + j_0e) = j_0 = j_1 = ... = j_i + j_0e * (R0 + R1 + ...) + (j_0d - j_1d) * R1 + ... + (j_0d - j_id) * Ri = Vtot + or + j_0e * Sum_R + Sum_Rd = Vtot. + Ri = dV_dcell / j_ie, the relative cell resistance. + Solve j_0e, find (V1 - V0) = j_0e * R0. j_1e = current_x - j_1d, find (V2 - V1) = j_1e * R1, etc. + */ + int icell, jcell, i, l, n, length, length2, il_calcs; + int i1, loop_f_c; + int first_c, last_c, last_c2 = 0; + char token[MAX_LENGTH]; + LDBLE mixf, temp; + LDBLE dVtemp = 0.0; + if (dV_dcell && stagnant) + { + dVtemp = dV_dcell; + dV_dcell = 0; + } + + icell = jcell = -1; + first_c = last_c = -1; + il_calcs = -1; + + current_x = sum_R = sum_Rd = 0.0; + if (dV_dcell) + find_current = loop_f_c = 1; // calculate J_ij once for all cells, loop to dV_dcell2. + else + find_current = loop_f_c = 0; + + for (int f_c = 0; f_c <= loop_f_c; f_c++) + { + for (n = 0; n <= (stagnant ? stag_data->count_stag : 0); n++) // allow for stagnant cell mixing with higher cells in the layer + { + icell = mobile_cell + 1 + n * count_cells; + if (stagnant) + { + if (n == 0) + icell -= 1; + else if (mobile_cell == 0 || mobile_cell == count_cells + 1) + continue; + /* + * find the mix ptr for icell and go along the cells that mix with it + */ + use.Set_mix_ptr(Utilities::Rxn_find(Rxn_mix_map, icell)); + if (use.Get_mix_ptr() == NULL) + { + if (first_c < 0) + first_c = icell; + if (last_c2 < icell) + last_c2 = icell; + continue; + } + first_c = 0; + last_c = (int) (use.Get_mix_ptr()->Get_mixComps().size() - 1); + } + else + { /* regular column... */ + if (bcon_first == 1) + first_c = 0; + else + first_c = 1; + if (bcon_last == 1) + last_c = count_cells; + else + last_c = count_cells - 1; + last_c2 = (dV_dcell ? count_cells + 1: count_cells); + } + + for (i = first_c; i <= last_c; i++) + { + if (stagnant) + { + std::vector n_solution; + std::vector fraction; + (use.Get_mix_ptr())->Vectorize(n_solution, fraction); + jcell = n_solution[i]; + if (jcell <= icell) + continue; + if (jcell >= all_cells || jcell < 0) + { + error_string = sformatf( + "Multi_D asked for diffusion from cell %d to %d, %d is beyond the number of cells", icell, jcell, jcell); + error_msg(error_string, CONTINUE); + } + if (jcell > last_c2) + last_c2 = jcell; + mixf = fraction[i] / nmix; + } + else + { /* regular column... */ + icell = i; + jcell = i + 1; + mixf = 1.0; + } + if (dV_dcell) + { + tk_x2 = (sol_D[icell].tk_x + sol_D[jcell].tk_x) / 2; + } + /* + * 1. obtain J_ij... + */ + il_calcs = find_J(icell, jcell, mixf, DDt, stagnant); + if (find_current) + { + if (i < last_c) + continue; + else + { + LDBLE dVc, j_0e; + // distribute dV_dcell according to relative resistance, calculate current_x if not fixed + j_0e = (dV_dcell * count_cells - sum_Rd) / sum_R; + current_x = j_0e + current_cells[0].dif; + if (fix_current) + { + int sign = (current_x >= 0 ? 1 : -1); + current_x = sign * fix_current / F_C_MOL; + j_0e = current_x - current_cells[0].dif; + } + dVc = j_0e * current_cells[0].R; + cell_data[1].potV = cell_data[0].potV + dVc; + for (i1 = 1; i1 < count_cells; i1++) + { + dVc = current_cells[i1].R * (current_x - current_cells[i1].dif); + cell_data[i1 + 1].potV = cell_data[i1].potV + dVc; + } + if (fix_current) + { + dVc = current_cells[i1].R * (current_x - current_cells[i1].dif); + cell_data[i1 + 1].potV = cell_data[i1].potV + dVc; + } + find_current = 0; + continue; + } + } + + /* + * 2. sum up the primary or secondary master_species + */ + if (!il_calcs) + { + tot1_h = tot1_o = tot2_h = tot2_o = 0.0; + m_s = (struct M_S *) free_check_null(m_s); + count_m_s = (ct[icell].J_ij_count_spec < count_elements ? + ct[icell].J_ij_count_spec : count_elements); + m_s = (struct M_S *) PHRQ_malloc((size_t) count_m_s * + sizeof(struct M_S)); + if (m_s == NULL) + malloc_error(); + for (i1 = 0; i1 < count_m_s; i1++) + { + m_s[i1].name = NULL; + m_s[i1].tot1 = 0; + m_s[i1].tot2 = 0; + } + count_m_s = 0; + } + fill_m_s(ct[icell].J_ij, ct[icell].J_ij_count_spec); + + /* + * 3. find the solutions, add or subtract the moles... + */ + if (dV_dcell || (icell != 0 && icell != count_cells + 1)) + { + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, icell)); + use.Get_solution_ptr()->Set_total_h(use.Get_solution_ptr()->Get_total_h() - tot1_h); + use.Get_solution_ptr()->Set_total_o(use.Get_solution_ptr()->Get_total_o() - tot1_o); + if (dV_dcell && (icell > 0 || fix_current)) + { + use.Get_solution_ptr()->Set_potV(cell_data[icell].potV); + } + for (l = 0; l < count_m_s; l++) + { + length = (int) strlen(m_s[l].name); + cxxNameDouble::iterator it; + for (it = use.Get_solution_ptr()->Get_totals().begin(); + it != use.Get_solution_ptr()->Get_totals().end(); it++) + { + length2 = + (int) (size_t) strcspn(it->first.c_str(), "("); + if (strncmp(m_s[l].name, it->first.c_str(), length) == 0 && length == length2) + { + it->second -= m_s[l].tot1; + break; + } + } + if (it == use.Get_solution_ptr()->Get_totals().end()) + { + use.Get_solution_ptr()->Get_totals()[m_s[l].name] = -m_s[l].tot1; + } + } + } + if (dV_dcell || jcell != count_cells + 1) + { + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, jcell)); + dummy = use.Get_solution_ptr()->Get_total_h(); + use.Get_solution_ptr()->Set_total_h(dummy + tot2_h); + dummy = use.Get_solution_ptr()->Get_total_o(); + use.Get_solution_ptr()->Set_total_o(dummy + tot2_o); + if (icell == count_cells && fix_current && !stagnant) + { + use.Get_solution_ptr()->Set_potV(cell_data[jcell].potV); + } + for (l = 0; l < count_m_s; l++) + { + length = (int) strlen(m_s[l].name); + cxxNameDouble::iterator it; + for (it = use.Get_solution_ptr()->Get_totals().begin(); + it != use.Get_solution_ptr()->Get_totals().end(); it++) + { + length2 = (int) (size_t) strcspn(it->first.c_str(), "("); + if (strncmp(m_s[l].name, it->first.c_str(), length) == 0 && length == length2) + { + it->second += m_s[l].tot2; + break; + } + } + if (it == use.Get_solution_ptr()->Get_totals().end()) + { + use.Get_solution_ptr()->Get_totals()[m_s[l].name] = m_s[l].tot2; + } + } + } + } + } + } + // check for negative conc's... + //if (stagnant) + // first_c = mobile_cell; // allow for stagnant cell mixing with boundary cell 0 + for (i = first_c; i <= last_c2; i++) + { + if (stagnant && i > first_c && i <= count_cells + first_c) + continue; + + use.Set_solution_ptr(Utilities::Rxn_find(Rxn_solution_map, i)); + if (!use.Get_solution_ptr()) + continue; + cxxNameDouble::iterator it; + for (it = use.Get_solution_ptr()->Get_totals().begin(); + it != use.Get_solution_ptr()->Get_totals().end(); it++) + { + if (strcmp(it->first.c_str(), "H(0)") == 0) + continue; + if (strcmp(it->first.c_str(), "O(0)") == 0) + continue; + LDBLE moles = it->second; + if (moles < 0 && ct[i].dl_s) + { + use.Set_surface_ptr(Utilities::Rxn_find(Rxn_surface_map, i)); + cxxSurface * s_ptr = use.Get_surface_ptr(); + cxxSurfaceCharge * charge_ptr = NULL; + cxxNameDouble::iterator jit; + for (size_t j = 0; j < s_ptr->Get_surface_charges().size(); j++) + { + if (s_ptr->Get_dl_type() == cxxSurface::DONNAN_DL) + { + charge_ptr = &(s_ptr->Get_surface_charges()[j]); + for (jit = charge_ptr->Get_diffuse_layer_totals().begin(); jit != charge_ptr->Get_diffuse_layer_totals().end(); jit++) + { + if (strcmp(jit->first.c_str(), "H") == 0 || strcmp(jit->first.c_str(), "O") == 0) + continue; + if (strcmp(jit->first.c_str(), it->first.c_str()) == 0) + { + moles += jit->second; + it->second += jit->second; + jit->second = 0; + } + } + } + } + } + if (moles < 0) + { + temp = moles; + it->second = 0; + /* see if other redox states have more moles... */ + length = (int) strlen(it->first.c_str()); + cxxNameDouble::iterator kit; + for (kit = use.Get_solution_ptr()->Get_totals().begin(); + kit != use.Get_solution_ptr()->Get_totals().end(); kit++) + { + length2 = (int) (size_t) strcspn(kit->first.c_str(), "("); + if (!strncmp(it->first.c_str(), kit->first.c_str(), length2)) + { + temp += kit->second; + if (temp < 0) + { + kit->second = 0; + } + else + { + kit->second = temp; + break; + } + } + } + if (temp < -1e-12) + { + sprintf(token, + "Negative concentration in MCD: added %.4e moles %s in cell %d", + (double)-temp, it->first.c_str(), i); + warning_msg(token); + for (i1 = 0; i1 < count_elements; i1++) + { + if (moles_added[i1].name && !strcmp(moles_added[i1].name, it->first.c_str())) + { + moles_added[i1].moles -= temp; + break; + } + else if (!moles_added[i1].moles) + { + moles_added[i1].name = string_duplicate(it->first.c_str()); + moles_added[i1].moles -= temp; + break; + } + } + } + } + } + } + + m_s = (struct M_S *) free_check_null(m_s); + + for (i = first_c; i < last_c2; i++) + { + if (stagnant && i > first_c && i <= count_cells + first_c) + continue; + ct[i].J_ij = (struct J_ij *) free_check_null(ct[i].J_ij); + if (il_calcs) + ct[i].J_ij_il = (struct J_ij *) free_check_null(ct[i].J_ij_il); + ct[i].v_m = (struct V_M *) free_check_null(ct[i].v_m); + } + if (dVtemp && stagnant) + { + dV_dcell = dVtemp; + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +fill_m_s(struct J_ij *l_J_ij, int l_J_ij_count_spec) +/* ---------------------------------------------------------------------- */ +{ + /* sum up the primary or secondary master_species from solute species + * H and O go in tot1&2_h and tot1&2_o + */ + int j, k, l; + char *ptr; + + for (j = 0; j < l_J_ij_count_spec; j++) + { + { + char * temp_name = string_duplicate(l_J_ij[j].name); + ptr = temp_name; + count_elts = 0; + get_elts_in_species(&ptr, 1); + free_check_null(temp_name); + } + for (k = 0; k < count_elts; k++) + { + if (strcmp(elt_list[k].elt->name, "X") == 0) + continue; + if (strcmp(elt_list[k].elt->name, "H") == 0) + { + tot1_h += elt_list[k].coef * l_J_ij[j].tot1; + tot2_h += elt_list[k].coef * l_J_ij[j].tot2; + } + else if (strcmp(elt_list[k].elt->name, "O") == 0) + { + tot1_o += elt_list[k].coef * l_J_ij[j].tot1; + tot2_o += elt_list[k].coef * l_J_ij[j].tot2; + } + else + { + for (l = 0; l < count_m_s; l++) + { + if (strcmp(m_s[l].name, elt_list[k].elt->name) == 0) + { + m_s[l].tot1 += elt_list[k].coef * l_J_ij[j].tot1; + m_s[l].tot2 += elt_list[k].coef * l_J_ij[j].tot2; + break; + } + } + if (l == count_m_s) + { + //m_s[l].name = string_hsave(elt_list[k].elt->name); + m_s[l].name = elt_list[k].elt->name; + m_s[l].tot1 = elt_list[k].coef * l_J_ij[j].tot1; + m_s[l].tot2 = elt_list[k].coef * l_J_ij[j].tot2; + count_m_s++; + } + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +find_J(int icell, int jcell, LDBLE mixf, LDBLE DDt, int stagnant) +/* ---------------------------------------------------------------------- */ +{ + /* mole transfer of the individual master_species: + * Eqn 1: + * J_ij = DDt * (A_ij / lav) * (-D_i*grad(c) + D_i*z_i*c_i * SUM(D_i*z_i*grad(c)) / SUM(D_i*(z_i)^2*c_i)) + * regular column, stagnant FALSE: + * D_i = temperature-corrected Dw + * A_ij = A_icell * A_jcell + * A_icell = (L porewater in i_cell / length_icell) / tort_f_icell / + * (length_icell / 2) + * lav = A_icell + A_jcell + * grad(c) is concentration difference in icell and jcell (dx is in lav), + for activity corrections see Appelo & Wersin, 2007. + ** dec. 28, 2015** + included aq_dl in the harmonic mean: + J_ij = - b_i * b_j / (b_i + b_j) * (c_j - c_i) in mol/s (see ex 21 in the manual 3). + b_i = A1 / (G_i * h_i / 2) * Dw for a pore without EDL. A1 = aq1 / h_i (m^2). + with EDL (no aq_il_i in A1, for now): + t_aq1 = aq1 + aq_dl_i. A1 = t_aq1 / h_i. f_free_i = aq1 / t_aq1. + b_i_cat = A1 / (G_i * h_i / 2) * Dw * {f_free + (1 - f_free) * Bm}. Bm = Boltzmann enrichment in EDL = g_dl. + b_i_ani = A1 / (G_i * h_i / 2) * Dw * {f_free + (1 - f_free) / Bm)}. + 22/2/18: now calculates diffusion through EDL's of multiple, differently charged surfaces + * stagnant TRUE: + * same eqn for J_ij, but multplies with 2 * mixf. (times 2, because mixf = A / (G_i * h_i)) + * mixf_ij = mixf / (Dw / init_tort_f) / new_tort_f * new_por / init_por + * mixf is defined in MIX; Dw is default multicomponent diffusion coefficient; + * init_tort_f equals multi_Dpor^(-multi_Dn); new_pf = new tortuosity factor. + * Interlayer diffusion (IL) takes the gradient in the equivalent concentrations on X-. + surface area A for IL: + stagnant: ct[icell].mixf_il is mixf * por_il / por. + por_il = interlayer porosity, from -interlayer_D true 'por_il'. + por = free + DL porewater porosity, from -multi_D true 'multi_Dpor'. + in regular column, A is calc'd from (free + DL porewater) and cell-length. + for IL: A * por_il / por. + + por_il should be entered for the cell with the maximal cec. + IL water is related to X-, thus the cec (eq/L IL water) is the same for all cells if X is difined. + IL-water = (free + DL porewater) * por_il / por. + for IL: A * aq_il / t_aq. + */ + int i, i_max, j, j_max, k, k_il, only_counter, il_calcs; + int i1; + LDBLE A1 = 0.0, A2 = 0.0, ddlm, aq1, aq2, t_aq1, t_aq2, f_free_i, f_free_j; + LDBLE dl_aq1, dl_aq2, c_dl, dum, dum1, dum2, tort1, tort2, b_i, b_j; + LDBLE Sum_zM, aq_il1, aq_il2; + LDBLE por_il1, por_il2, por_il12 = 0.0; + LDBLE cec1, cec2, cec12 = 0.0, rc1 = 0.0, rc2 = 0.0; + LDBLE dV, c1, c2; + cxxSurface *s_ptr1, *s_ptr2; + LDBLE g_i, g_j; + //char token[MAX_LENGTH], token1[MAX_LENGTH]; + + std::vector s_charge_p; + std::vector s_charge_p1; + std::vector s_charge_p2; + std::vector::iterator it_sc; + std::vector s_com_p; + + il_calcs = (interlayer_Dflag ? 1 : 0); + ct[icell].dl_s = dl_aq1 = dl_aq2 = 0.0; + + if (dV_dcell && !find_current) + goto dV_dcell2; + + /* check for immediate return and interlayer diffusion calcs... */ + ct[icell].J_ij_sum = 0.0; + ct[icell].J_ij_count_spec = 0; + if (!il_calcs) + { + if (stagnant) + { + if (cell_data[icell].por < multi_Dpor_lim + || cell_data[jcell].por < multi_Dpor_lim) + return (OK); + } + else + { /* regular column... */ + if ((icell == 0 && cell_data[1].por < multi_Dpor_lim) + || (icell == count_cells && cell_data[count_cells].por < multi_Dpor_lim) + || (icell != 0 && icell != count_cells && (cell_data[icell].por < multi_Dpor_lim + || cell_data[jcell].por < multi_Dpor_lim))) + { + if (dV_dcell) + { + current_cells[icell].R = -1e15; + current_cells[icell].ele = dV_dcell / current_cells[icell].R; + current_cells[icell].dif = 0; + sum_R += current_cells[icell].R; + sum_Rd += current_cells[0].dif * current_cells[icell].R; + } + return (OK); + } + } + } + + /* do the calcs */ + aq1 = Utilities::Rxn_find(Rxn_solution_map, icell)->Get_mass_water(); + aq2 = Utilities::Rxn_find(Rxn_solution_map, jcell)->Get_mass_water(); + /* + * check if DL calculations must be made, find amounts of water... + */ + s_ptr1 = s_ptr2 = NULL; + ct[icell].visc1 = ct[icell].visc2 = 1.0; + only_counter = FALSE; + + s_ptr1 = Utilities::Rxn_find(Rxn_surface_map, icell); + if (s_ptr1 != NULL) + { + if (s_ptr1->Get_dl_type() != cxxSurface::NO_DL) + { + s_charge_p.assign(s_ptr1->Get_surface_charges().begin(), s_ptr1->Get_surface_charges().end()); + s_com_p.assign(s_ptr1->Get_surface_comps().begin(), s_ptr1->Get_surface_comps().end()); + + if (s_ptr1->Get_only_counter_ions()) + only_counter = TRUE; + + ct[icell].visc1 = s_ptr1->Get_DDL_viscosity(); + /* find the immobile surface charges with DL... */ + for (i = 0; i < (int)s_charge_p.size(); i++) + { + for (i1 = 0; i1 < (int)s_com_p.size(); i1++) + { + if (!(s_charge_p[i].Get_name().compare(s_com_p[i1].Get_charge_name())) && !s_com_p[i1].Get_Dw()) + { + dl_aq1 += s_charge_p[i].Get_mass_water(); + s_charge_p1.push_back(s_charge_p[i]); + break; + } + } + } + } + } + s_ptr2 = Utilities::Rxn_find(Rxn_surface_map, jcell); + if (s_ptr2 != NULL) + { + if (s_ptr2->Get_dl_type() != cxxSurface::NO_DL) + { + s_charge_p.assign(s_ptr2->Get_surface_charges().begin(), s_ptr2->Get_surface_charges().end()); + s_com_p.assign(s_ptr2->Get_surface_comps().begin(), s_ptr2->Get_surface_comps().end()); + + if (s_ptr2->Get_only_counter_ions()) + only_counter = TRUE; + + ct[icell].visc2 = s_ptr2->Get_DDL_viscosity(); + + for (i = 0; i < (int)s_charge_p.size(); i++) + { + for (i1 = 0; i1 < (int)s_com_p.size(); i1++) + { + if (!(s_charge_p[i].Get_name().compare(s_com_p[i1].Get_charge_name())) && !s_com_p[i1].Get_Dw()) + { + dl_aq2 += s_charge_p[i].Get_mass_water(); + s_charge_p2.push_back(s_charge_p[i]); + break; + } + } + } + } + } + if (!stagnant) + { + if (icell == 0) + ct[icell].visc1 = ct[icell].visc2; + else if (icell == count_cells) + ct[icell].visc2 = ct[icell].visc1; + } + //LDBLE d_damper = Utilities::Rxn_find(Rxn_solution_map, jcell)->Get_mu() / + // Utilities::Rxn_find(Rxn_solution_map, icell)->Get_mu(); + //d_damper = pow(d_damper, 0.3); + //if (d_damper > 1) ct[icell].visc1 *= d_damper; else ct[icell].visc2 *= d_damper; + + /* in each cell: DL surface = mass_water_DL / (cell_length) + free pore surface = mass_water_free / (cell_length) + determine DL surface as a fraction of the total pore surface... */ + t_aq1 = aq1 + dl_aq1; + t_aq2 = aq2 + dl_aq2; + f_free_i = aq1 / t_aq1; + f_free_j = aq2 / t_aq2; + if (dl_aq1 > 0) + ct[icell].dl_s = dl_aq1 / t_aq1; + if (dl_aq2 > 0) + ct[icell].dl_s = dl_aq2 / t_aq2; + + if (il_calcs) + { + /* find interlayer porosity por_il, + make it relative to exchange capacity (mol X/L), highest X in sol_D[1].x_max (mol X / L). + Find amounts of IL water and cec. */ + por_il1 = por_il2 = por_il12 = 0.0; + cec1 = cec2 = cec12 = rc1 = rc2 = 0.0; + if (icell == 0) + { + por_il1 = sol_D[0].exch_total / aq1 / sol_D[1].x_max * + cell_data[1].por_il; + por_il2 = sol_D[1].exch_total / aq2 / sol_D[1].x_max * + cell_data[1].por_il; + if (sol_D[0].exch_total > 3e-10 && sol_D[1].exch_total > 3e-10) + /* take the harmonic mean... */ + por_il12 = 2 * por_il1 * por_il2 / (por_il1 + por_il2); + else + /* at column ends, take the clay... */ + por_il12 = (por_il1 >= por_il2 ? por_il1 : por_il2); + + aq_il2 = t_aq2 * por_il12 / cell_data[1].por; + aq_il1 = t_aq2 * por_il12 / cell_data[1].por; + } + else if (icell == count_cells) + { + por_il1 = sol_D[count_cells].exch_total / aq1 / sol_D[1].x_max * + cell_data[count_cells].por_il; + por_il2 = sol_D[count_cells + 1].exch_total / aq2 / sol_D[1].x_max * + cell_data[count_cells].por_il; + if (sol_D[count_cells].exch_total > 3e-10 && sol_D[count_cells + 1].exch_total > 3e-10) + por_il12 = 2 * por_il1 * por_il2 / (por_il1 + por_il2); + else + por_il12 = (por_il1 >= por_il2 ? por_il1 : por_il2); + + aq_il1 = t_aq1 * por_il12 / cell_data[count_cells].por; + aq_il2 = t_aq2 * por_il12 / cell_data[count_cells].por; + } + else + { + por_il1 = sol_D[icell].exch_total / aq1 / sol_D[1].x_max * + cell_data[icell].por_il; + por_il2 = sol_D[jcell].exch_total / aq2 / sol_D[1].x_max * + cell_data[jcell].por_il; + + if (sol_D[icell].exch_total > 3e-10 && sol_D[jcell].exch_total > 3e-10) + por_il12 = 2 * por_il1 * por_il2 / (por_il1 + por_il2); + else + por_il12 = (por_il1 >= por_il2 ? por_il1 : por_il2); + + aq_il1 = t_aq1 * por_il12 / cell_data[icell].por; + aq_il2 = t_aq2 * por_il12 / cell_data[jcell].por; + } + if (por_il12 < interlayer_Dpor_lim) + il_calcs = 0; + else + { + dum = sol_D[icell].exch_total; + dum2 = sol_D[jcell].exch_total; + // the rc's are for distributing the mole transfer (later on) over X and solution. + rc1 = (dum2 > dum ? dum / dum2 : 1); + rc2 = (dum > dum2 ? dum2 / dum : 1); + if (sol_D[icell].exch_total > 3e-10) + cec1 = dum / aq_il1; + else + cec1 = 2e-10; + if (sol_D[jcell].exch_total > 3e-10) + cec2 = dum2 / aq_il2; + else + cec2 = 2e-10; + /* and the largest for calculating the mass transfer since it is usually at the boundary... */ + cec12 = (cec1 > cec2 ? cec1 : cec2); + } + } + + /* Find ct[icell].mixf_il for IL diffusion. + In stagnant calc's, correct mixf by default values, A = por / tort. + In regular column, A = surface area / (0.5 * x * tort)*/ + + tort1 = tort2 = 1.0; + ct[icell].A_ij_il = ct[icell].mixf_il = 0.0; + if (stagnant) + { + mixf /= (default_Dw * pow(multi_Dpor, multi_Dn) * multi_Dpor); + if (il_calcs) + ct[icell].mixf_il = mixf * por_il12 / interlayer_tortf; + } + if (icell == 0) + { + tort1 = tort2 = pow(cell_data[1].por, -multi_Dn); + if (stagnant) + A2 = cell_data[1].por / tort2; + else + { + A2 = t_aq2 / (cell_data[1].length * 0.5 * cell_data[1].length); + if (il_calcs && !stagnant) + ct[icell].A_ij_il = A2 * por_il12 / (cell_data[1].por * interlayer_tortf); + A2 /= tort2; + } + A1 = A2; + } + else if (icell == count_cells) + { + tort1 = tort2 = pow(cell_data[count_cells].por, -multi_Dn); + if (stagnant) + A1 = cell_data[count_cells].por / tort1; + else + { + A1 = t_aq1 / (cell_data[count_cells].length * 0.5 * cell_data[count_cells].length); + if (il_calcs && !stagnant) + ct[icell].A_ij_il = A1 * por_il12 / (cell_data[count_cells].por * interlayer_tortf); + A1 /= tort1; + } + A2 = A1; + } + else + { + tort1 = pow(cell_data[icell].por, -multi_Dn); + tort2 = pow(cell_data[jcell].por, -multi_Dn); + if (stagnant) + { + A1 = cell_data[icell].por / tort1; + A2 = cell_data[jcell].por / tort2; + } + else + { + A1 = t_aq1 / (cell_data[icell].length * 0.5 * cell_data[icell].length); + A2 = t_aq2 / (cell_data[jcell].length * 0.5 * cell_data[jcell].length); + if (il_calcs && !stagnant) + { + dum = A1 * por_il12 / (cell_data[icell].por * interlayer_tortf); + dum2 = A2 * por_il12 / (cell_data[jcell].por * interlayer_tortf); + ct[icell].A_ij_il = dum * dum2 / (dum + dum2); + } + A1 /= tort1; + A2 /= tort2; + } + } + /* diffuse... */ + /* + * malloc sufficient space... + */ + k = sol_D[icell].count_spec + sol_D[jcell].count_spec; + + ct[icell].J_ij = (struct J_ij *) free_check_null(ct[icell].J_ij); + ct[icell].J_ij = (struct J_ij *) PHRQ_malloc((size_t) k * sizeof(struct J_ij)); + if (ct[icell].J_ij == NULL) + malloc_error(); + + ct[icell].v_m = (struct V_M *) free_check_null(ct[icell].v_m); + ct[icell].v_m = (struct V_M *) PHRQ_malloc((size_t) k * sizeof(struct V_M)); + if (ct[icell].v_m == NULL) + malloc_error(); + + for (i = 0; i < k; i++) + { + ct[icell].J_ij[i].tot1 = 0.0; + ct[icell].v_m[i].grad = 0.0; + ct[icell].v_m[i].D = 0.0; + ct[icell].v_m[i].z = 0.0; + ct[icell].v_m[i].c = 0.0; + ct[icell].v_m[i].zc = 0.0; + ct[icell].v_m[i].Dz = 0.0; + ct[icell].v_m[i].Dzc = 0.0; + ct[icell].v_m[i].b_ij = 0.0; + } + ct[icell].Dz2c = ct[icell].Dz2c_dl = ct[icell].Dz2c_il = 0.0; + + if (il_calcs) + { + /* also for interlayer cations */ + k = sol_D[icell].count_exch_spec + sol_D[jcell].count_exch_spec; + + ct[icell].J_ij_il = (struct J_ij *) free_check_null(ct[icell].J_ij_il); + ct[icell].J_ij_il = (struct J_ij *) PHRQ_malloc((size_t) k * sizeof(struct J_ij)); + if (ct[icell].J_ij_il == NULL) + malloc_error(); + + ct[icell].v_m_il = (struct V_M *) free_check_null(ct[icell].v_m_il); + ct[icell].v_m_il = (struct V_M *) PHRQ_malloc((size_t) k * sizeof(struct V_M)); + if (ct[icell].v_m_il == NULL) + malloc_error(); + + for (i = 0; i < k; i++) + { + ct[icell].J_ij_il[i].tot1 = 0.0; + ct[icell].v_m_il[i].grad = 0.0; + ct[icell].v_m_il[i].D = 0.0; + ct[icell].v_m_il[i].z = 0.0; + ct[icell].v_m_il[i].c = 0.0; + ct[icell].v_m_il[i].zc = 0.0; + ct[icell].v_m_il[i].Dz = 0.0; + ct[icell].v_m_il[i].Dzc = 0.0; + ct[icell].v_m_il[i].b_ij = 0.0; + } + } + /* + * coefficients in Eqn (1)... + */ + i = j = k = k_il = 0; + i_max = sol_D[icell].count_spec; + j_max = sol_D[jcell].count_spec; + + while (i < i_max || j < j_max) + { + if (j == j_max + || (i < i_max && strcmp(sol_D[icell].spec[i].name, sol_D[jcell].spec[j].name) < 0)) + { + /* species 'name' is only in icell */ + if (il_calcs && sol_D[icell].spec[i].type == EX) + { + ct[icell].J_ij_il[k_il].name = sol_D[icell].spec[i].name; + ct[icell].v_m_il[k_il].D = sol_D[icell].spec[i].Dwt; + ct[icell].v_m_il[k_il].z = sol_D[icell].spec[i].z; + ct[icell].v_m_il[k_il].Dz = ct[icell].v_m_il[k_il].D * ct[icell].v_m_il[k_il].z; + dum = sol_D[icell].spec[i].c * cec12 / (2 * ct[icell].v_m_il[k_il].z); + ct[icell].v_m_il[k_il].Dzc = ct[icell].v_m_il[k_il].Dz * dum; + ct[icell].Dz2c_il += ct[icell].v_m_il[k_il].Dzc * ct[icell].v_m_il[k_il].z; + ct[icell].v_m_il[k_il].grad = -sol_D[icell].spec[i].c * cec12 / ct[icell].v_m_il[k_il].z; /* use equivalent fraction */ + k_il++; + } + else + { + ct[icell].J_ij[k].name = sol_D[icell].spec[i].name; + ct[icell].v_m[k].z = sol_D[icell].spec[i].z; + ct[icell].v_m[k].grad = -sol_D[icell].spec[i].c; /* assume d log(gamma) / d log(c) = 0 */ + c1 = sol_D[icell].spec[i].c / 2; + ct[icell].v_m[k].c = c1; + if (ct[icell].v_m[k].z) + ct[icell].v_m[k].zc = ct[icell].v_m[k].z * c1; + + if (dV_dcell && ct[icell].v_m[k].z) + { + // compare diffusive and electromotive forces + dum = ct[icell].v_m[k].grad; + if (icell == 0) + dum2 = (cell_data[1].potV - cell_data[0].potV) / (cell_data[1].length / 2); + else if (icell == count_cells) + dum2 = (cell_data[count_cells + 1].potV - cell_data[count_cells].potV) / (cell_data[count_cells].length / 2); + else + dum2 = (cell_data[jcell].potV - cell_data[icell].potV) / ((cell_data[jcell].length + cell_data[icell].length) / 2); + dum2 *= F_Re3 / tk_x2 * ct[icell].v_m[k].z * c1; + if (dum + dum2 > 0) + { + // step out: no transport against the dV_dcell gradient if c = 0 in jcell... + if (i < i_max) + i++; + continue; + } + } + + g_i = g_j = 0; + if (ct[icell].dl_s > 0) + { + if (dl_aq1) + { + for (it_sc = s_charge_p1.begin(); it_sc != s_charge_p1.end(); it_sc++) + { + g_i += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z]; + } + g_i *= sol_D[icell].spec[i].erm_ddl; + } + if (dl_aq2) + { + for (it_sc = s_charge_p2.begin(); it_sc != s_charge_p2.end(); it_sc++) + { + if (ct[icell].v_m[k].z == 0 || only_counter) + { + g_j += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z]; + } + else + { + if (abs(ct[icell].v_m[k].z) == 1) + // there is always H+ and OH-... + g_j += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z]; + else + { + dum1 = it_sc->Get_mass_water() / mass_water_bulk_x; + dum2 = it_sc->Get_z_gMCD_map()[1] / dum1; + g_j += pow(dum2, ct[icell].v_m[k].z) * dum1; + } + } + } + g_j *= sol_D[icell].spec[i].erm_ddl; + } + } + + b_i = A1 * sol_D[icell].spec[i].Dwt * (f_free_i + g_i / ct[icell].visc1); + b_j = A2 * (f_free_j + g_j / ct[icell].visc2); + if (icell == count_cells && !stagnant) + ct[icell].v_m[k].b_ij = b_i; + else + { + if (sol_D[icell].tk_x == sol_D[jcell].tk_x) + b_j *= sol_D[icell].spec[i].Dwt; + else + { + dum2 = sol_D[icell].spec[i].Dwt / sol_D[icell].viscos_f; + dum2 *= exp(sol_D[icell].spec[i].dw_t / sol_D[jcell].tk_x - sol_D[icell].spec[i].dw_t / sol_D[icell].tk_x); + dum2 *= sol_D[jcell].viscos_f; + b_j *= dum2; + } + if (icell == 0 && !stagnant) + { + //if (d_damper < 1 && g_i == 0) + // ct[icell].v_m[k].b_ij = A2 * sol_D[icell].spec[i].Dwt * (f_free_j + g_j * d_damper / ct[icell].visc2); + //else + ct[icell].v_m[k].b_ij = b_j; + } + else + ct[icell].v_m[k].b_ij = b_i * b_j / (b_i + b_j); + } + + if (ct[icell].v_m[k].z) + ct[icell].Dz2c += ct[icell].v_m[k].b_ij * ct[icell].v_m[k].zc * ct[icell].v_m[k].z; + + k++; + } + if (i < i_max) + i++; + } + + else if (i == i_max || + (j < j_max && strcmp(sol_D[icell].spec[i].name, sol_D[jcell].spec[j].name) > 0)) + { + /* species 'name' is only in jcell */ + if (il_calcs && sol_D[jcell].spec[j].type == EX) + { + ct[icell].J_ij_il[k_il].name = sol_D[jcell].spec[j].name; + ct[icell].v_m_il[k_il].D = sol_D[jcell].spec[j].Dwt; + ct[icell].v_m_il[k_il].z = sol_D[jcell].spec[j].z; + ct[icell].v_m_il[k_il].Dz = ct[icell].v_m_il[k_il].D * ct[icell].v_m_il[k_il].z; + ct[icell].v_m_il[k_il].Dzc = ct[icell].v_m_il[k_il].Dz * sol_D[jcell].spec[j].c * + cec12 / (2 * ct[icell].v_m_il[k_il].z); + ct[icell].Dz2c_il += ct[icell].v_m_il[k_il].Dzc * ct[icell].v_m_il[k_il].z; + ct[icell].v_m_il[k_il].grad = sol_D[jcell].spec[j].c * cec12 / ct[icell].v_m_il[k_il].z; /* use equivalent fraction */ + k_il++; + } + else + { + ct[icell].J_ij[k].name = sol_D[jcell].spec[j].name; + ct[icell].v_m[k].z = sol_D[jcell].spec[j].z; + ct[icell].v_m[k].grad = sol_D[jcell].spec[j].c; /* assume d log(gamma) / d log(c) = 0 */ + c2 = sol_D[jcell].spec[j].c / 2; + ct[icell].v_m[k].c = c2; + if (ct[icell].v_m[k].z) + ct[icell].v_m[k].zc = ct[icell].v_m[k].z * c2; + + if (dV_dcell && ct[icell].v_m[k].z) + { + // compare diffuse and electromotive forces + dum = ct[icell].v_m[k].grad; + if (icell == 0) + dum2 = (cell_data[1].potV - cell_data[0].potV) / (cell_data[1].length / 2); + else if (icell == count_cells) + dum2 = (cell_data[count_cells + 1].potV - cell_data[count_cells].potV) / (cell_data[count_cells].length / 2); + else + dum2 = (cell_data[jcell].potV - cell_data[icell].potV) / ((cell_data[jcell].length + cell_data[icell].length) / 2); + dum2 *= F_Re3 / tk_x2 * ct[icell].v_m[k].z * c2; + // don't transport unavailable moles against the gradient + if (dum + dum2 < 0) + { + // step out... + if (j < j_max) + j++; + continue; + } + } + g_i = g_j = 0; + if (ct[icell].dl_s > 0) + { + if (dl_aq1) + { + for (it_sc = s_charge_p1.begin(); it_sc != s_charge_p1.end(); it_sc++) + { + if (ct[icell].v_m[k].z == 0 || only_counter) + { + g_i += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z]; + } + else + { + if (abs(ct[icell].v_m[k].z) == 1) + // there is always H+ and OH-... + g_i += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z]; + else + { + dum1 = it_sc->Get_mass_water() / mass_water_bulk_x; + dum2 = it_sc->Get_z_gMCD_map()[1] / dum1; + g_i += pow(dum2, ct[icell].v_m[k].z) * dum1; + } + } + } + g_i *= sol_D[jcell].spec[j].erm_ddl; + } + if (dl_aq2) + { + for (it_sc = s_charge_p2.begin(); it_sc != s_charge_p2.end(); it_sc++) + { + g_j += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z]; + } + g_j *= sol_D[jcell].spec[j].erm_ddl; + } + } + b_i = A1 * (f_free_i + g_i / ct[icell].visc1); + b_j = A2 * sol_D[jcell].spec[j].Dwt * (f_free_j + g_j / ct[icell].visc2); + if (icell == 0 && !stagnant) + ct[icell].v_m[k].b_ij = b_j; + else + { + if (sol_D[icell].tk_x == sol_D[jcell].tk_x) + b_i *= sol_D[jcell].spec[j].Dwt; + else + { + dum2 = sol_D[jcell].spec[j].Dwt / sol_D[jcell].viscos_f; + dum2 *= exp(sol_D[jcell].spec[j].dw_t / sol_D[icell].tk_x - sol_D[jcell].spec[j].dw_t / sol_D[jcell].tk_x); + dum2 *= sol_D[icell].viscos_f; + b_i *= dum2; + } + if (icell == count_cells && !stagnant) + ct[icell].v_m[k].b_ij = b_i; + else + ct[icell].v_m[k].b_ij = b_i * b_j / (b_i + b_j); + } + if (ct[icell].v_m[k].z) + ct[icell].Dz2c += ct[icell].v_m[k].b_ij * ct[icell].v_m[k].zc * ct[icell].v_m[k].z; + + k++; + } + if (j < j_max) + j++; + } + else if (strcmp(sol_D[icell].spec[i].name, sol_D[jcell].spec[j].name) == 0) + { + /* species 'name' is in both cells */ + if (il_calcs && sol_D[icell].spec[i].type == EX) + { + ct[icell].J_ij_il[k_il].name = sol_D[icell].spec[i].name; + if (sol_D[icell].spec[i].Dwt == 0 || sol_D[jcell].spec[j].Dwt == 0) + ct[icell].v_m_il[k_il].D = 0.0; + else + ct[icell].v_m_il[k_il].D = + (sol_D[icell].spec[i].Dwt + sol_D[jcell].spec[j].Dwt) / 2; + + ct[icell].v_m_il[k_il].z = sol_D[icell].spec[i].z; + ct[icell].v_m_il[k_il].Dz = ct[icell].v_m_il[k_il].D * ct[icell].v_m_il[k_il].z; + ct[icell].v_m_il[k_il].Dzc = ct[icell].v_m_il[k_il].Dz * (sol_D[icell].spec[i].c + + sol_D[jcell].spec[j].c) * cec12 / ct[icell].v_m_il[k_il].z; + ct[icell].Dz2c_il += ct[icell].v_m_il[k_il].Dzc * ct[icell].v_m_il[k_il].z; + ct[icell].v_m_il[k_il].grad = (sol_D[jcell].spec[j].c - sol_D[icell].spec[i].c) * + cec12 / ct[icell].v_m_il[k_il].z; /* use equivalent fraction */ + k_il++; + } + else + { + ct[icell].J_ij[k].name = sol_D[icell].spec[i].name; + ct[icell].v_m[k].z = sol_D[icell].spec[i].z; + ct[icell].v_m[k].grad = (sol_D[jcell].spec[j].c - sol_D[icell].spec[i].c); + c1 = sol_D[icell].spec[i].c / 2; + c2 = sol_D[jcell].spec[j].c / 2; + ct[icell].v_m[k].c = c1 + c2; + if (ct[icell].v_m[k].z) + ct[icell].v_m[k].zc = ct[icell].v_m[k].z * ct[icell].v_m[k].c; + + if (dV_dcell && ct[icell].v_m[k].z) + { + // compare diffuse and electromotive forces + dum = ct[icell].v_m[k].grad; + if (icell == 0) + dum2 = (cell_data[1].potV - cell_data[0].potV) / (cell_data[1].length / 2); + else if (icell == count_cells) + dum2 = (cell_data[count_cells + 1].potV - cell_data[count_cells].potV) / (cell_data[count_cells].length / 2); + else + dum2 = (cell_data[jcell].potV - cell_data[icell].potV) / ((cell_data[jcell].length + cell_data[icell].length) / 2); + dum2 *= F_Re3 / tk_x2 * ct[icell].v_m[k].z * (c1 + c2); + // don't transport unavailable moles against the gradient + if (abs(dum) < abs(dum2) && + ((dum2 >= 0 && sol_D[jcell].spec[j].c * aq2 < 1e-12) || + (dum2 <= 0 && sol_D[icell].spec[i].c * aq1 < 1e-12))) + { + // step out: + if (i < i_max) + i++; + if (j < j_max) + j++; + continue; + } + } + g_i = g_j = 0; + if (ct[icell].dl_s > 0) + { + if (dl_aq1) + { + for (it_sc = s_charge_p1.begin(); it_sc != s_charge_p1.end(); it_sc++) + { + g_i += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z]; + } + g_i *= sol_D[icell].spec[i].erm_ddl; + } + if (dl_aq2) + { + for (it_sc = s_charge_p2.begin(); it_sc != s_charge_p2.end(); it_sc++) + { + g_j += it_sc->Get_z_gMCD_map()[ct[icell].v_m[k].z]; + } + g_j *= sol_D[jcell].spec[j].erm_ddl; + } + } + b_i = A1 * sol_D[icell].spec[i].Dwt * (f_free_i + g_i / ct[icell].visc1); + b_j = A2 * sol_D[jcell].spec[j].Dwt * (f_free_j + g_j / ct[icell].visc2); + if (icell == 0 && !stagnant) + ct[icell].v_m[k].b_ij = b_j; + else if (icell == count_cells && !stagnant) + ct[icell].v_m[k].b_ij = b_i; + else + ct[icell].v_m[k].b_ij = b_i * b_j / (b_i + b_j); + + if (ct[icell].v_m[k].z) + ct[icell].Dz2c += ct[icell].v_m[k].b_ij * ct[icell].v_m[k].zc * ct[icell].v_m[k].z; + + ddlm = sol_D[jcell].spec[j].lm - sol_D[icell].spec[i].lm; + if (fabs(ddlm) > 1e-10) + ct[icell].v_m[k].grad *= (1 + (sol_D[jcell].spec[j].lg - sol_D[icell].spec[i].lg) / ddlm); + + k++; + } + if (i < i_max) + i++; + if (j < j_max) + j++; + } + } + /* + * fill in J_ij... + */ + if (!dV_dcell && !ct[icell].Dz2c) + k = 0; + ct[icell].J_ij_count_spec = i_max = k; + ct[icell].J_ij_il_count_spec = k_il; + + if (dV_dcell) + { + current_cells[icell].ele = current_cells[icell].dif = 0; + dum = dV_dcell * F_Re3 / tk_x2; + for (i = 0; i < ct[icell].J_ij_count_spec; i++) + { + if (!ct[icell].v_m[i].z) + continue; + current_cells[icell].ele -= ct[icell].v_m[i].b_ij * ct[icell].v_m[i].z * + ct[icell].v_m[i].zc * dum; + current_cells[icell].dif -= ct[icell].v_m[i].b_ij * ct[icell].v_m[i].z * + ct[icell].v_m[i].grad; + } + current_cells[icell].R = dV_dcell / current_cells[icell].ele; + sum_R += current_cells[icell].R; + sum_Rd += (current_cells[0].dif - current_cells[icell].dif) * current_cells[icell].R; + return(il_calcs); + } + + // dV_dcell, 2nd pass, current_x has been calculated, and + // voltage was adapted to give equal current in the cells. +dV_dcell2: + + ct[icell].J_ij_sum = 0; + Sum_zM = c_dl = 0.0; + + for (i = 0; i < ct[icell].J_ij_count_spec; i++) + { + if (ct[icell].v_m[i].z) + Sum_zM += ct[icell].v_m[i].b_ij * ct[icell].v_m[i].z * ct[icell].v_m[i].grad; + } + for (i = 0; i < ct[icell].J_ij_count_spec; i++) + { + ct[icell].J_ij[i].tot1 = -ct[icell].v_m[i].grad; + if (!dV_dcell && ct[icell].v_m[i].z && ct[icell].Dz2c > 0) + ct[icell].J_ij[i].tot1 += Sum_zM * ct[icell].v_m[i].zc / ct[icell].Dz2c; + if (stagnant) + ct[icell].J_ij[i].tot1 *= ct[icell].v_m[i].b_ij * 2 * mixf; + else + ct[icell].J_ij[i].tot1 *= ct[icell].v_m[i].b_ij * DDt; + ct[icell].J_ij[i].tot2 = ct[icell].J_ij[i].tot1; + ct[icell].J_ij_sum += ct[icell].v_m[i].z * ct[icell].J_ij[i].tot1; + } + // assure that icell has dl water when checking negative conc's in MCD + ct[icell].dl_s = dl_aq1; + ct[jcell].dl_s = dl_aq2; + + if (dV_dcell) + { + // perhaps adapt dV for getting equal current... + current_cells[icell].ele = current_cells[icell].dif = 0; + dV = cell_data[jcell].potV - cell_data[icell].potV; + dum = dV * F_Re3 / tk_x2; + for (i = 0; i < ct[icell].J_ij_count_spec; i++) + { + if (!ct[icell].v_m[i].z) + continue; + current_cells[icell].ele -= ct[icell].v_m[i].b_ij * ct[icell].v_m[i].z * + ct[icell].v_m[i].zc * dum; + current_cells[icell].dif -= ct[icell].v_m[i].b_ij * ct[icell].v_m[i].z * + ct[icell].v_m[i].grad; + } + dV *= (current_x - current_cells[icell].dif) / current_cells[icell].ele; + dum = dV * F_Re3 / tk_x2; + for (i = 0; i < ct[icell].J_ij_count_spec; i++) + { + if (!ct[icell].v_m[i].z) + continue; + ct[icell].J_ij[i].tot1 -= ct[icell].v_m[i].b_ij * + ct[icell].v_m[i].zc * dum * DDt; + ct[icell].J_ij[i].tot2 = ct[icell].J_ij[i].tot1; + } + current_A = current_x * F_C_MOL; + } + /* + * calculate interlayer mass transfer... + */ + if (il_calcs && ct[icell].Dz2c_il != 0 && ct[icell].J_ij_il_count_spec > 0) + { + cxxExchange *ex_ptr1 = Utilities::Rxn_find(Rxn_exchange_map, icell); + cxxExchange *ex_ptr2 = Utilities::Rxn_find(Rxn_exchange_map, jcell); + Sum_zM = 0.0; + i_max = k_il = ct[icell].J_ij_il_count_spec; + for (i = 0; i < i_max; i++) + Sum_zM += ct[icell].v_m_il[i].Dz * ct[icell].v_m_il[i].grad; + for (i = 0; i < i_max; i++) + { + ct[icell].J_ij_il[i].tot1 = -ct[icell].v_m_il[i].D * ct[icell].v_m_il[i].grad + + Sum_zM * ct[icell].v_m_il[i].Dzc / ct[icell].Dz2c_il; + if (stagnant) + ct[icell].J_ij_il[i].tot1 *= ct[icell].mixf_il; + else + ct[icell].J_ij_il[i].tot1 *= ct[icell].A_ij_il * DDt; + ct[icell].J_ij_sum += ct[icell].v_m_il[i].z * ct[icell].J_ij_il[i].tot1; + ct[icell].J_ij_il[i].tot2 = ct[icell].J_ij_il[i].tot1; + } + + /* express the transfer in elemental moles... */ + tot1_h = tot1_o = tot2_h = tot2_o = 0.0; + m_s = (struct M_S *) free_check_null(m_s); + m_s = (struct M_S *) PHRQ_malloc((size_t) count_elements * + sizeof(struct M_S)); + if (m_s == NULL) + malloc_error(); + for (i1 = 0; i1 < count_elements; i1++) + { + m_s[i1].name = NULL; + m_s[i1].tot1 = 0; + m_s[i1].tot2 = 0; + } + count_m_s = 0; + fill_m_s(ct[icell].J_ij_il, k_il); + + /* do the mass transfer... */ + if (icell > 0 || stagnant) + { + size_t k; + for (k = 0; k < ex_ptr1->Get_exchange_comps().size(); k++) + { + cxxNameDouble nd(ex_ptr1->Get_exchange_comps()[k].Get_totals()); + cxxNameDouble::iterator it = nd.begin(); + i_max = 0; + for (; it != nd.end(); it++) + { + if (strcmp("X", it->first.c_str()) == 0) + i_max = 1; + } + if (i_max) + break; + } + + if (k < ex_ptr1->Get_exchange_comps().size()) + { + cxxExchComp &comp_ref = ex_ptr1->Get_exchange_comps()[k]; + cxxNameDouble nd(comp_ref.Get_totals()); + cxxNameDouble::iterator it = nd.begin(); + /* transfer O and H... */ + for (; it != nd.end(); it++) + { + struct element *elt_ptr = element_store(it->first.c_str()); + LDBLE coef = it->second; + if (strcmp("H", elt_ptr->name) == 0) + { + if (coef < rc1 * tot1_h) + { + tot1_h -= coef; + comp_ref.Get_totals().insert("H", 0); + } + else + { + comp_ref.Get_totals().insert("H", coef - rc1 * tot1_h); + tot1_h *= (1 - rc1); + } + } + else if (strcmp("O", elt_ptr->name) == 0) + { + if (coef < rc1 * tot1_o) + { + tot1_o -= coef; + comp_ref.Get_totals().insert("O", 0); + } + else + { + comp_ref.Get_totals().insert("O", coef - rc1 * tot1_o); + tot1_o *= (1 - rc1); + } + } + } + /* transfer other elements... */ + j_max = 0; /* if j_max turns true, reallocate the exchange structure */ + for (j = 0; j < count_m_s; j++) + { + // Make sure list includes each element + comp_ref.Get_totals().add(m_s[j].name, 0); + + cxxNameDouble nd(comp_ref.Get_totals()); + cxxNameDouble::iterator it = nd.begin(); + for (; it != nd.end(); it++) + { + struct element *elt_ptr = element_store(it->first.c_str()); + LDBLE coef = it->second; + if (strcmp(m_s[j].name, elt_ptr->name) != 0) + continue; + + /* rc1 part goes to exchange species... */ + if (coef < rc1 * m_s[j].tot1) + { + m_s[j].tot1 -= coef; + comp_ref.Get_totals().insert(m_s[j].name, 0); + } + else + { + comp_ref.Get_totals().insert(m_s[j].name, coef - rc1 * m_s[j].tot1); + m_s[j].tot1 *= (1 - rc1); + } + } + } + } + } + if (icell < count_cells || stagnant) + { + size_t k; + for (k = 0; k < ex_ptr2->Get_exchange_comps().size(); k++) + { + cxxExchComp &comp_ref = ex_ptr2->Get_exchange_comps()[k]; + cxxNameDouble nd(comp_ref.Get_totals()); + cxxNameDouble::iterator it = nd.begin(); + i_max = 0; + for (; it != nd.end(); it++) + { + if (strcmp("X", it->first.c_str()) == 0) + i_max = 1; + } + if (i_max) + break; + } + if (k < ex_ptr2->Get_exchange_comps().size()) + { + cxxExchComp &comp_ref = ex_ptr2->Get_exchange_comps()[k]; + cxxNameDouble nd(comp_ref.Get_totals()); + cxxNameDouble::iterator it = nd.begin(); + /* transfer O and H... */ + for (; it != nd.end(); it++) + { + struct element *elt_ptr = element_store(it->first.c_str()); + LDBLE coef = it->second; + + if (strcmp("H", elt_ptr->name) == 0) + { + if (coef < -rc2 * tot2_h) + { + tot2_h += coef; + comp_ref.Get_totals().insert("H", 0); + } + else + { + comp_ref.Get_totals().insert("H", coef + rc2 * tot2_h); + tot2_h *= (1 - rc2); + } + } + else if (strcmp("O", elt_ptr->name) == 0) + { + if (coef < -rc2 * tot2_o) + { + tot2_o += coef; + comp_ref.Get_totals().insert("O", 0); + } + else + { + comp_ref.Get_totals().insert("O", coef + rc2 * tot2_o); + tot2_o *= (1 - rc2); + } + } + } + /* transfer other elements... */ + for (j = 0; j < count_m_s; j++) + { + // Make sure list includes each element + comp_ref.Get_totals().add(m_s[j].name, 0); + + cxxNameDouble nd(comp_ref.Get_totals()); + cxxNameDouble::iterator it = nd.begin(); + for (; it != nd.end(); it++) + { + struct element *elt_ptr = element_store(it->first.c_str()); + LDBLE coef = it->second; + if (strcmp(m_s[j].name, elt_ptr->name) != 0) + continue; + + /* rc2 part goes to exchange species... */ + if (coef < -rc2 * m_s[j].tot2) + { + m_s[j].tot2 += coef; + comp_ref.Get_totals().insert(m_s[j].name, 0); + } + else + { + comp_ref.Get_totals().insert(m_s[j].name, coef + rc2 * m_s[j].tot2); + m_s[j].tot2 *= (1 - rc2); + } + } + } + } + } + } + /* do not transport charge imbalance */ + //ct[icell].J_ij_sum = 0; + //V_M = (struct V_M *) free_check_null(V_M); + if (il_calcs) + ct[icell].v_m_il = (struct V_M *) free_check_null(ct[icell].v_m_il); + return (il_calcs); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +disp_surf(LDBLE DDt) +/* ---------------------------------------------------------------------- */ +/* +* Disperse/diffuse surfaces: +* surface[n1] = SS(mixf * surface[n2]) + (1 - SS(mixf) * surface[i1]) +* where SS is Sum of, f2 is a mixing factor. +* Implementation: +* Mobile surface comps and charges are mixed face by face and 1 by 1 in surface[n1]: +Step (from cell 1 to count_cells + 1): +* 1. Surface n1 is made a copy of cell[i1]'s surface, if it exists, or else +* b. a copy of the first encountered mobile surface[n2] from cell i2 = i1 - 1. +* 2 a. Column surfaces are mixed by dispersion/diffusion +* b. Column surfaces are mixed by MCD (if multi_Dflag is true) +* 3. Surfaces n1 and n2 are stored in a temporary surface, with n_user = i1 or i2. +* 4. When done for all cells, new surfaces are copied into the cells. +* NOTE... The surfaces' diffuse_layer, edl and phases/kinetics relations must be identical, +* but only mobile surface_comp's (Dw > 0) and their surface_charge's are transported. +*/ +{ + int i, i1, i2, k, k1/*, n1, n2*/; + int charge_done, surf1, surf2; + LDBLE f1, f2, mixf, mixf_store, mcd_mixf; + LDBLE lav, A_ij, por, Dp1, Dp2; + cxxMix * mix_ptr; + cxxSurface *surface_ptr1, *surface_ptr2; + LDBLE viscos_f; + /* + * temperature and viscosity correction for MCD coefficient, D_T = D_298 * Tk * viscos_298 / (298 * viscos) + */ + viscos_f = viscos_0; + viscos_f = tk_x * viscos_0_25 / (298.15 * viscos_f); + + //n1 = 0; + //n2 = n1 + 1; + cxxSurface surface_n1, surface_n2; + cxxSurface *surface_n2_ptr; + + std::map Rxn_temp_surface_map; + + for (i1 = 1; i1 <= count_cells + 1; i1++) + { + if (i1 <= count_cells && cell_data[i1].por < multi_Dpor_lim) + continue; + + if (i1 == 1 && bcon_first != 1) + continue; + if (i1 == count_cells + 1 && bcon_last != 1) + continue; + + i2 = i1 - 1; + if (i2 > 0 && cell_data[i2].por < multi_Dpor_lim) + continue; + /* + * step 1. define surface n1 from cell i1, if it exists... + */ + surface_n1.Set_n_user(-99); + surface_n2.Set_n_user(-99); + + surface_ptr1 = Utilities::Rxn_find(Rxn_surface_map, i1); + if (surface_ptr1 != NULL) + { + surface_n1 = *surface_ptr1; + } + + surface_ptr2 = Utilities::Rxn_find(Rxn_surface_map, i2); + surf1 = surf2 = 0; + if (surface_ptr2 != NULL) + { + if (surface_ptr2->Get_transport()) + surf2 = 1; + } + if (surface_ptr1 != NULL) + { + if (surface_ptr1->Get_transport()) + surf1 = 1; + } + + mixf = mixf_store = 0; + if (surf1 || surf2) + { + /* + * step 2a. Dispersive mixing of mobile surfaces from column cells i2 < i1... + */ + if (i1 <= count_cells) + mix_ptr = Utilities::Rxn_find(Dispersion_mix_map, i1); + else + mix_ptr = Utilities::Rxn_find(Dispersion_mix_map, count_cells); + + std::vector num; + std::vector frac; + mix_ptr->Vectorize(num, frac); + for (size_t i3 = 0; i3 < num.size(); i3++) + { + if (i1 <= count_cells) + { + if (num[i3] == i2) + { + mixf = mixf_store = frac[i3]; + break; + } + } + else if (num[i3] == i1) + { + mixf = mixf_store = frac[i3]; + break; + } + } + + /* step 1b. If cell i1 has no surface, get the mobile comps from surface i2... */ + if (surface_n1.Get_n_user() == -99) + { + surface_n1 = mobile_surface_copy(surface_ptr2, i1, false); + /* limit charges to 1... */ + if (surface_n1.Get_surface_charges().size() > 1) + { + std::string charge_name = surface_n1.Get_surface_charges()[0].Get_name(); + surface_n1 = sum_surface_comp(&surface_n1, 0, + &surface_n1, charge_name, 1, surface_n1.Get_surface_comps()[0].Get_Dw()); + } + f1 = 0; + } + else + f1 = 1; + /* find the (possibly modified) surface in the previous cell i2... */ + f2 = 1; + if (i2 > 0 || bcon_first == 1) + { + surface_n2_ptr = Utilities::Rxn_find(Rxn_temp_surface_map, i2); + if (surface_n2_ptr != NULL) + { + surface_n2 = *surface_n2_ptr; + } + + /* if not found... */ + else + { + /* copy it from surface_ptr2... */ + if (surface_ptr2 != NULL) + { + surface_n2 = *surface_ptr2; + surface_n2.Set_n_user_both(i2); + } + else + { + /* or make it a mobile copy of the surface in cell i1... */ + surface_n2 = mobile_surface_copy(surface_ptr1, i2, false); + /* limit charges to 1... */ + if (surface_n2.Get_surface_charges().size() > 1) + { + std::string charge_name = surface_n2.Get_surface_charges()[0].Get_name(); + surface_n2 = sum_surface_comp(&surface_n2, 0, &surface_n2, charge_name, 1, + surface_n2.Get_surface_comps()[0].Get_Dw()); + } + f2 = 0; + } + } + } + + /* + * For MCD, step 2b. Find physical dimensions and porosity of the cells... + */ + if (i1 == 1) + { + por = cell_data[1].por; + lav = cell_data[1].length / 2; + A_ij = Utilities::Rxn_find(Rxn_solution_map, 1)->Get_mass_water() / cell_data[1].length; + } + else if (i1 == count_cells + 1) + { + por = cell_data[count_cells].por; + lav = cell_data[count_cells].length / 2; + A_ij = + Utilities::Rxn_find(Rxn_solution_map, count_cells)->Get_mass_water() / + cell_data[count_cells].length; + } + else + { + por = cell_data[i2].por; + lav = + (cell_data[i1].length + cell_data[i2].length) / 2; + A_ij = + Utilities::Rxn_find(Rxn_solution_map, i1)->Get_mass_water() / + (cell_data[i1].length * cell_data[i1].por); + A_ij += + Utilities::Rxn_find(Rxn_solution_map, i2)->Get_mass_water() / + (cell_data[i2].length * cell_data[i2].por); + A_ij /= 2; + A_ij *= + (cell_data[i1].por < + cell_data[i2].por ? cell_data[i1].por : cell_data[i2].por); + } + + /* mix in comps with the same charge structure... */ + if (surf2) + { + for (k = 0; k < (int) surface_ptr2->Get_surface_comps().size(); k++) + { + cxxSurfaceComp *comp_k_ptr = &(surface_ptr2->Get_surface_comps()[k]); + std::string charge_name = comp_k_ptr->Get_charge_name(); + if (comp_k_ptr->Get_Dw() == 0) + continue; + + charge_done = FALSE; + for (k1 = 0; k1 < k; k1++) + { + cxxSurfaceComp *comp_k1_ptr = &(surface_ptr2->Get_surface_comps()[k1]); + if (comp_k_ptr->Get_charge_name() == + comp_k1_ptr->Get_charge_name()) + { + charge_done = TRUE; + break; + } + } + if (charge_done) + continue; + + /* Define the MCD diffusion coefficient... */ + mcd_mixf = 0; + if (multi_Dflag) + { + Dp2 = comp_k_ptr->Get_Dw() * pow(por, multi_Dn); + Dp1 = 0; + if (surface_ptr1 != NULL && surface_ptr1->Get_transport()) + { + for (k1 = 0; k1 < (int) surface_ptr1->Get_surface_comps().size(); k1++) + { + cxxSurfaceComp *comp_k1_ptr = &(surface_ptr1->Get_surface_comps()[k1]); + if (strcmp(comp_k1_ptr->Get_formula().c_str(), + comp_k_ptr->Get_formula().c_str()) != 0) + continue; + Dp1 = + comp_k1_ptr->Get_Dw() * + pow(cell_data[i1].por, multi_Dn); + break; + } + } + if (Dp1 > 0) + Dp2 = (Dp2 + Dp1) / 2; + + /* and the mixing factor... */ + mcd_mixf = Dp2 * viscos_f * A_ij * DDt / lav; + } + mixf = mixf_store + mcd_mixf; + + if (mixf < 1e-8) + mixf = 0; + if (mixf > 0.99999999) + mixf = 0.99999999; + if (i1 <= count_cells) + { + surface_n1 = sum_surface_comp(&surface_n1, f1, surface_ptr2, charge_name, mixf, + surface_ptr2->Get_surface_comps()[k].Get_Dw()); + f1 = 1; + } + if (i2 > 0) + { + surface_n2 = sum_surface_comp(&surface_n2, f2, surface_ptr2, charge_name, -mixf, + surface_ptr2->Get_surface_comps()[k].Get_Dw()); + f2 = 1; + } + surface_n1.Set_n_user_both(i1); + } + } + if (surf1) + { + for (k = 0; k < (int) surface_ptr1->Get_surface_comps().size(); k++) + { + cxxSurfaceComp * comp_k_ptr = &(surface_ptr1->Get_surface_comps()[k]); + std::string charge_name = comp_k_ptr->Get_charge_name(); + if (comp_k_ptr->Get_Dw() == 0) + continue; + charge_done = FALSE; + for (k1 = 0; k1 < k; k1++) + { + cxxSurfaceComp * comp_k1_ptr = &(surface_ptr1->Get_surface_comps()[k1]); + if (comp_k_ptr->Get_charge_name() == + comp_k1_ptr->Get_charge_name()) + { + charge_done = TRUE; + break; + } + } + if (charge_done) + continue; + + /* Define the MCD diffusion coefficient... */ + mcd_mixf = 0; + if (multi_Dflag) + { + if (i1 <= count_cells) + { + Dp1 = + comp_k_ptr->Get_Dw() * + pow(cell_data[i1].por, multi_Dn); + } + else + { + Dp1 = comp_k_ptr->Get_Dw() * pow(por, multi_Dn); + } + Dp2 = 0; + if (surface_ptr2 != NULL && surface_ptr2->Get_transport()) + { + for (k1 = 0; k1 < (int) surface_ptr2->Get_surface_comps().size(); k1++) + { + cxxSurfaceComp * comp_k1_ptr = &(surface_ptr2->Get_surface_comps()[k1]); + if (strcmp(comp_k1_ptr->Get_formula().c_str(), + comp_k_ptr->Get_formula().c_str()) != 0) + continue; + Dp2 = comp_k1_ptr->Get_Dw() * pow(por, multi_Dn); + break; + } + } + if (Dp2 > 0) + Dp1 = (Dp1 + Dp2) / 2; + + /* and the mixing factor... */ + mcd_mixf = Dp1 * viscos_f * A_ij * DDt / lav; + } + mixf = mixf_store + mcd_mixf; + + if (mixf < 1e-8) + mixf = 0; + if (mixf > 0.99999999) + mixf = 0.99999999; + if (i2 > 0) + { + surface_n2 = sum_surface_comp + (&surface_n2, f2, surface_ptr1, charge_name, mixf, + surface_ptr1->Get_surface_comps()[k].Get_Dw()); + f2 = 1; + } + if (i1 <= count_cells) + { + surface_n1 = sum_surface_comp + (&surface_n1, f1, surface_ptr1, charge_name, -mixf, + surface_ptr1->Get_surface_comps()[k].Get_Dw()); + f1 = 1; + } + surface_n2.Set_n_user_both(i2); + } + } + } + + /* + * Step 3. copy surface[n1] and [n2] in a new temporary surface... + */ + if (surface_n1.Get_n_user() == -99) + continue; + + Rxn_temp_surface_map[i1] = surface_n1; + { + cxxSurface t; + surface_n1 = t; + } + surface_n1.Set_n_user_both(-99); + + if (surface_n2.Get_n_user() == i2) + { + surface_n2.Set_n_user_both(i2); + Rxn_temp_surface_map[i2] = surface_n2; + { + cxxSurface t; + surface_n2 = t; + } + surface_n2.Set_n_user_both(-99); + } + } + /* + * Step 4. Dispersion/diffusion is done. New surfaces can be copied in the cell's surface... + */ + //n2 = 0; + std::map::iterator jit = Rxn_temp_surface_map.begin(); + for (; jit != Rxn_temp_surface_map.end(); jit++) + { + i = jit->first; + assert(i == jit->second.Get_n_user()); + if ((i == 0 && bcon_first == 1) || (i == count_cells + 1 && bcon_last == 1)) + { + continue; + } + if (i >= 0 && i <= 1 + count_cells * (1 + stag_data->count_stag)) + { + surface_ptr1 = Utilities::Rxn_find(Rxn_surface_map, i); + if (surface_ptr1 != NULL) + { + Rxn_surface_map[i] = jit->second; + } + else + { + //Add to map + Rxn_surface_map[i] = jit->second; + } + } + } + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +cxxSurface Phreeqc:: +sum_surface_comp(cxxSurface *source1, LDBLE f1, cxxSurface *source2, +std::string charge_name, LDBLE f2, LDBLE new_Dw) +/* ---------------------------------------------------------------------- */ +{ + /* + * Takes fraction f1 of the 1st surface, adds fraction f2 of the 2nd surface's comps[k] and its charge. + * The result goes in target + */ + int new_n_user; + cxxSurface *surface_ptr1, *surface_ptr2; + std::string token; + /* + * Find surfaces + */ + surface_ptr1 = source1; + if (surface_ptr1 == NULL) + { + error_string = sformatf("Null pointer for surface 1 in sum_surface."); + error_msg(error_string, STOP); + input_error++; + return (ERROR); + } + surface_ptr2 = source2; + /* + * Store data for structure surface + */ + new_n_user = surface_ptr1->Get_n_user(); + cxxSurface temp_surface(*surface_ptr1); + temp_surface.Set_n_user_both(new_n_user); + temp_surface.Set_description("Copy"); + temp_surface.Set_solution_equilibria(false); + temp_surface.Set_n_solution(-99); + /* + * Multiply component compositions by f1 + */ + temp_surface.multiply(f1); + /* + * Add in surface_ptr2 + */ + // Only components with same charge as component k + cxxSurface addee(*surface_ptr2); + addee.Get_surface_comps().clear(); + addee.Get_surface_charges().clear(); + + for (std::vector::iterator it = surface_ptr2->Get_surface_comps().begin(); + it != surface_ptr2->Get_surface_comps().end(); it++) + { + if (it->Get_charge_name() == charge_name) + { + addee.Get_surface_comps().push_back(*it); + } + } + for (std::vector::iterator it = surface_ptr2->Get_surface_charges().begin(); + it != surface_ptr2->Get_surface_charges().end(); it++) + { + if (it->Get_name() == charge_name) + { + addee.Get_surface_charges().push_back(*it); + } + } + + if (f2 == 0) + f2 = 1e-30; + temp_surface.add(addee, f2); + temp_surface.Set_transport(false); + for (size_t i = 0; i < temp_surface.Get_surface_comps().size(); i++) + { + if (temp_surface.Get_surface_comps()[i].Get_charge_name() == charge_name) + { + temp_surface.Get_surface_comps()[i].Set_Dw(new_Dw); + } + if (temp_surface.Get_surface_comps()[i].Get_Dw() > 0) + { + temp_surface.Set_transport(true); + } + } + /* + * Finish up + */ + temp_surface.Sort_comps(); + return (temp_surface); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +check_surfaces(cxxSurface *surface_ptr1, cxxSurface *surface_ptr2) +/* ---------------------------------------------------------------------- */ +{ + /* checks if surfaces can be mixed... + */ + int n_user1, n_user2, return_code; + + return_code = OK; + n_user1 = surface_ptr1->Get_n_user(); + n_user2 = surface_ptr2->Get_n_user(); + + if (surface_ptr1->Get_dl_type() != surface_ptr2->Get_dl_type()) + { + error_string = sformatf( + "Surfaces %d and %d differ in definition of diffuse layer. Cannot mix.", + n_user1, n_user2); + error_msg(error_string, STOP); + return_code = ERROR; + input_error++; + } + + if (surface_ptr1->Get_type() != surface_ptr2->Get_type()) + { + error_string = sformatf( + "Surfaces %d and %d differ in use of electrical double layer. Cannot mix.", + n_user1, n_user2); + error_msg(error_string, STOP); + return_code = ERROR; + input_error++; + } + if (surface_ptr1->Get_only_counter_ions() != surface_ptr2->Get_only_counter_ions()) + { + error_string = sformatf( + "Surfaces %d and %d differ in use of only counter ions in the diffuse layer. Cannot mix.", + n_user1, n_user2); + error_msg(error_string, STOP); + return_code = ERROR; + input_error++; + } + if (surface_ptr1->Get_related_phases() != surface_ptr2->Get_related_phases()) + { + error_string = sformatf( + "Surfaces %d and %d differ in use of related phases (sites proportional to moles of an equilibrium phase). Cannot mix.", + n_user1, n_user2); + error_msg(error_string, STOP); + return_code = ERROR; + input_error++; + } + if (surface_ptr1->Get_related_rate() != surface_ptr2->Get_related_rate()) + { + error_string = sformatf( + "Surfaces %d and %d differ in use of related rate (sites proportional to moles of a kinetic reactant). Cannot mix.", + n_user1, n_user2); + error_msg(error_string, STOP); + return_code = ERROR; + input_error++; + } + + return (return_code); +} +/* ---------------------------------------------------------------------- */ +cxxSurface Phreeqc:: +mobile_surface_copy(cxxSurface *surface_old_ptr, +int n_user_new, bool move_old) +/* ---------------------------------------------------------------------- */ +{ + /* + * Copies mobile comps from surface_old_ptr to surf_ptr1, + * comps and charges with Dw > 0 are moved if move_old == TRUE, else copied. + * NOTE... when all comps are moved, the old surface is deleted and surfaces are sorted again, + * which will modify pointers and surface numbers. + * User number of new surface structure is n_user_new, structure is freed when n_user_new is already defined + */ + cxxSurface temp_surface(*surface_old_ptr); + /* + * Store moving surface's properties in temp_surface + */ + temp_surface.Set_n_user_both(n_user_new); + std::ostringstream desc; + desc << "Surface defined in simulation " << simulation << "."; + temp_surface.Set_description(desc.str().c_str()); + temp_surface.Set_solution_equilibria(false); + temp_surface.Set_transport(true); + + + size_t count_comps = surface_old_ptr->Get_surface_comps().size(); + int i1, i2; + i1 = i2 = 0; + temp_surface.Get_surface_comps().clear(); + temp_surface.Get_surface_charges().clear(); + /* see if comps must be moved, Dw > 0 */ + for (size_t i = 0; i < count_comps; i++) + { + cxxSurfaceComp &comp_ref = surface_old_ptr->Get_surface_comps()[i]; + if (comp_ref.Get_Dw() > 0) + { + i1++; + + // copy comp + temp_surface.Get_surface_comps().push_back(comp_ref); + + // copy charge, if needed + cxxSurfaceCharge *charge_ptr = temp_surface.Find_charge(comp_ref.Get_charge_name()); + if (charge_ptr == NULL) + { + i2++; + cxxSurfaceCharge *old_charge_ptr = surface_old_ptr->Find_charge(comp_ref.Get_charge_name()); + temp_surface.Get_surface_charges().push_back(*old_charge_ptr); + } + } + } + + if (i1 > 0) + { + /* OK, store moved parts from old surface, but first: + get immobile surface comps from new surface... */ + cxxSurface *surf_ptr = Utilities::Rxn_find(Rxn_surface_map, n_user_new); + if (surf_ptr != NULL) + { + for (size_t k = 0; k < surf_ptr->Get_surface_comps().size(); k++) + { + cxxSurfaceComp &comp_ref = surf_ptr->Get_surface_comps()[k]; + if (comp_ref.Get_Dw() > 0) + continue; + bool charge_done(false); + for (size_t k1 = 0; k1 < k; k1++) + { + if (surf_ptr->Get_surface_comps()[k1].Get_charge_name() == + comp_ref.Get_charge_name()) + { + charge_done = true; + break; + } + } + if (charge_done) + continue; + temp_surface = sum_surface_comp(&temp_surface, 1, surf_ptr, comp_ref.Get_charge_name(), 1, 0); + } + } + // Return value is temp_surface + temp_surface.Set_n_user_both(n_user_new); + } + + /* delete moved parts from old surface */ + if (move_old && i1 > 0) + { + cxxSurface replace_old(temp_surface); + int n_user_old = surface_old_ptr->Get_n_user(); + if ((size_t) i1 != count_comps) + { + /* redefine old surface with only unmovable comps (Dw = 0) */ + /* other temp_surface members were set above */ + replace_old.Set_n_user_both(n_user_old); + replace_old.Set_transport(false); + replace_old.Get_surface_comps().clear(); + replace_old.Get_surface_charges().clear(); + + i1 = i2 = 0; + for (size_t i = 0; i < count_comps; i++) + { + if (surface_old_ptr->Get_surface_comps()[i].Get_Dw() == 0) + { + i1++; + // copy surface comp + cxxSurfaceComp & comp_ref = surface_old_ptr->Get_surface_comps()[i]; + replace_old.Get_surface_comps().push_back(comp_ref); + cxxSurfaceCharge *charge_ptr = replace_old.Find_charge(comp_ref.Get_charge_name()); + + // copy surface charge if necessary + if (charge_ptr == NULL) + { + i2++; + cxxSurfaceCharge *old_charge_ptr = surface_old_ptr->Find_charge(comp_ref.Get_charge_name()); + replace_old.Get_surface_charges().push_back(*old_charge_ptr); + } + } + } + if (replace_old.Get_surface_comps().size() == 0) + { + Rxn_surface_map.erase(surface_old_ptr->Get_n_user()); + } + else + { + replace_old.Sort_comps(); + Rxn_surface_map[surface_old_ptr->Get_n_user()] = replace_old; + } + } + } + temp_surface.Sort_comps(); + return (temp_surface); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +diff_stag_surf(int mobile_cell) +/* ---------------------------------------------------------------------- */ +/* +* Diffuse stagnant and mobile surfaces, following the steps of disp_surf. +* First the mobile/stagnant surfaces are mixed, then the stagnant surfaces +* when not already done. +* If mixing factors among the cells are defined expicitly, it is assumed that +* mixing with a lower numbered cell was done when that cell was processed: +* for any cell in MCD, need only include the mixing factors for higher numbered cells. +*/ +{ + int i, i1, i2, k, k1, /*n1, n2,*/ ns; + int charge_done, surf1, surf2; + LDBLE f1, f2, mixf, mixf_store; + LDBLE Dp1, Dp2; + cxxMix *mix_ptr; + cxxSurface *surface_ptr1, *surface_ptr2; + LDBLE viscos_f; + /* + * temperature and viscosity correction for MCD coefficient, D_T = D_298 * Tk * viscos_298 / (298 * viscos) + */ + viscos_f = viscos_0; + viscos_f = tk_x * viscos_0_25 / (298.15 * viscos_f); + + cxxSurface surface_n1, surface_n2; + cxxSurface *surface_n1_ptr = &surface_n1; + cxxSurface *surface_n2_ptr; + std::map Rxn_temp_surface_map; + + for (ns = 0; ns < stag_data->count_stag; ns++) + { + + i1 = mobile_cell + 1 + ns * count_cells; + if (ns == 0) + i1--; + + if (cell_data[i1].por < multi_Dpor_lim) + continue; + surface_n1.Set_n_user_both(-99); + surface_n2.Set_n_user_both(-99); + surface_ptr1 = Utilities::Rxn_find(Rxn_surface_map, i1); + /* + * step 2a. mix surfaces... + */ + mix_ptr = Utilities::Rxn_find(Rxn_mix_map, i1); + if (mix_ptr == NULL) + continue; + + std::vector num; + std::vector frac; + mix_ptr->Vectorize(num, frac); + for (size_t i3 = 0; i3 < num.size(); i3++) + { + if ((i2 = num[i3]) <= i1) + continue; + if (cell_data[i2].por < multi_Dpor_lim) + continue; + surface_ptr2 = Utilities::Rxn_find(Rxn_surface_map, i2); + surf1 = surf2 = 0; + if (surface_ptr2 != NULL) + { + if (surface_ptr2->Get_transport()) + surf2 = 1; + } + if (surface_ptr1 != NULL) + { + if (surface_ptr1->Get_transport()) + surf1 = 1; + } + if (!surf1 && !surf2) + continue; + mixf = mixf_store = frac[i3];; + + /* find the (possibly modified) surface in cell i1... */ + f1 = 1; + surface_n1_ptr = Utilities::Rxn_find(Rxn_temp_surface_map, i1); + if (surface_n1_ptr != NULL) + { + surface_n1 = *surface_n1_ptr; + //n1 = 0; + } + /* if not found... */ + else + { + /* copy it from surface_ptr1... */ + if (surface_ptr1 != NULL) + { + surface_n1 = *surface_ptr1; + } + else + { + /* or make it a mobile copy of the surface in cell i2... */ + surface_n1 = mobile_surface_copy(surface_ptr2, i1, false); + + /* limit comps to 1... */ + if (surface_n1.Get_surface_charges().size() > 1) + { + std::string charge_name = surface_n1.Get_surface_charges()[0].Get_name(); + surface_n1 = sum_surface_comp(&surface_n1, 0, &surface_n1, charge_name, 1, + surface_n1.Get_surface_comps()[0].Get_Dw()); + } + f1 = 0; + } + } + /* find the (possibly modified) surface in cell i2... */ + f2 = 1; + surface_n2_ptr = Utilities::Rxn_find(Rxn_temp_surface_map, i2); + + if (surface_n2_ptr != NULL) + { + surface_n2 = *surface_n2_ptr; + } + /* if not found... */ + else + { + //n2 = 1; + /* copy it from surface_ptr2... */ + if (surface_ptr2 != NULL) + { + surface_n2 = *surface_ptr2; + } + else + { + /* or make it a mobile copy of the surface in cell i1... */ + surface_n2 = mobile_surface_copy(surface_ptr1, i2, false); + /* limit comps to 1... */ + if (surface_n2.Get_surface_charges().size() > 1) + { + std::string charge_name = surface_n2.Get_surface_charges()[0].Get_name(); + surface_n2 = sum_surface_comp(&surface_n2, 0, &surface_n2, charge_name, 1, + surface_n2.Get_surface_comps()[0].Get_Dw()); + } + f2 = 0; + } + } + + /* For MCD, step 2b. Adapt mixf to default values... */ + if (multi_Dflag) + { + mixf_store *= + (cell_data[i1].por <= + cell_data[i2].por ? cell_data[i1].por : cell_data[i2]. + por); + mixf_store /= (default_Dw * pow(multi_Dpor, multi_Dn) * + multi_Dpor); + } + + /* mix in comps with the same charge structure... */ + if (surf2) + { + for (k = 0; k < (int) surface_ptr2->Get_surface_comps().size(); k++) + { + cxxSurfaceComp *comp_k_ptr = &(surface_ptr2->Get_surface_comps()[k]); + std::string charge_name = comp_k_ptr->Get_charge_name(); + if (comp_k_ptr->Get_Dw() == 0) + continue; + charge_done = FALSE; + for (k1 = 0; k1 < k; k1++) + { + cxxSurfaceComp *comp_k1_ptr = &(surface_ptr2->Get_surface_comps()[k1]); + if (comp_k_ptr->Get_charge_name() == + comp_k1_ptr->Get_charge_name()) + { + charge_done = TRUE; + break; + } + } + if (charge_done) + continue; + + /* find diffusion coefficients of surfaces... */ + if (multi_Dflag) + { + Dp2 = comp_k_ptr->Get_Dw() * + pow(cell_data[i2].por, multi_Dn) * viscos_f; + Dp1 = 0; + if (surf1) + { + for (k1 = 0; k1 < (int) surface_ptr1->Get_surface_comps().size(); k1++) + { + cxxSurfaceComp *comp_k1_ptr = &(surface_ptr1->Get_surface_comps()[k1]); + if (strcmp(comp_k1_ptr->Get_formula().c_str(), + comp_k_ptr->Get_formula().c_str()) != 0) + continue; + Dp1 = + comp_k1_ptr->Get_Dw() * + pow(cell_data[i1].por, + multi_Dn) * viscos_f; + break; + } + } + if (Dp1 > 0) + Dp2 = (Dp2 + Dp1) / 2; + + /* and adapt the mixing factor... */ + mixf = mixf_store * Dp2; + mixf /= Utilities::Rxn_find(Rxn_solution_map, i2)->Get_mass_water(); + } + + if (mixf < 1e-8) + mixf = 0; + if (mixf > 0.99999999) + mixf = 0.99999999; + surface_n1 = sum_surface_comp(&surface_n1, f1, surface_ptr2, charge_name, mixf, + surface_ptr2->Get_surface_comps()[k].Get_Dw()); + f1 = 1; + + surface_n2 = sum_surface_comp(&surface_n2, f2, surface_ptr2, charge_name, -mixf, + surface_ptr2->Get_surface_comps()[k].Get_Dw()); + } + } + + if (surf1) + { + for (k = 0; k < (int) surface_ptr1->Get_surface_comps().size(); k++) + { + cxxSurfaceComp *comp_k_ptr = &(surface_ptr1->Get_surface_comps()[k]); + std::string charge_name = comp_k_ptr->Get_charge_name(); + if (comp_k_ptr->Get_Dw() == 0) + continue; + charge_done = FALSE; + for (k1 = 0; k1 < k; k1++) + { + cxxSurfaceComp *comp_k1_ptr = &(surface_ptr1->Get_surface_comps()[k1]); + if (comp_k_ptr->Get_charge_name() == + comp_k1_ptr->Get_charge_name()) + { + charge_done = TRUE; + break; + } + } + if (charge_done) + continue; + + /* find diffusion coefficients of surfaces... */ + if (multi_Dflag) + { + Dp1 = + comp_k_ptr->Get_Dw() * + pow(cell_data[i1].por, multi_Dn) * viscos_f; + + Dp2 = 0; + if (surf2) + { + for (k1 = 0; k1 < (int) surface_ptr2->Get_surface_comps().size(); k1++) + { + cxxSurfaceComp *comp_k1_ptr = &(surface_ptr2->Get_surface_comps()[k1]); + if (strcmp(comp_k1_ptr->Get_formula().c_str(), + comp_k_ptr->Get_formula().c_str()) != 0) + continue; + Dp2 = + comp_k1_ptr->Get_Dw() * + pow(cell_data[i2].por, + multi_Dn) * viscos_f; + break; + } + } + if (Dp2 > 0) + Dp1 = (Dp1 + Dp2) / 2; + + /* and adapt the mixing factor... */ + mixf = mixf_store * Dp1; + mixf /= Utilities::Rxn_find(Rxn_solution_map, i1)->Get_mass_water(); + } + + if (mixf < 1e-8) + mixf = 0; + if (mixf > 0.99999999) + mixf = 0.99999999; + surface_n2 = sum_surface_comp(&surface_n2, f2, surface_ptr1, charge_name, mixf, + surface_ptr1->Get_surface_comps()[k].Get_Dw()); + f2 = 1; + + surface_n1 = sum_surface_comp(&surface_n1, f1, surface_ptr1, charge_name, -mixf, + surface_ptr1->Get_surface_comps()[k].Get_Dw()); + } + } + + /* + * Step 3. copy surface[n1] and [n2] in a new temporary surface... + */ + if (surface_n1.Get_n_user() == -99) + continue; + + surface_n1.Set_n_user_both(i1); + Rxn_temp_surface_map[i1] = surface_n1; + + assert(surface_n2.Get_n_user() != -99); + assert(surface_n2.Get_n_user() == i2); + surface_n2.Set_n_user_both(i2); + Rxn_temp_surface_map[i2] = surface_n2; + } + } + /* + * Step 4. Diffusion is done. New surfaces can be copied in the cells... + */ + //n2 = 0; + std::map::iterator jit = Rxn_temp_surface_map.begin(); + for (; jit != Rxn_temp_surface_map.end(); jit++) + { + i = jit->first; + assert(i == jit->second.Get_n_user()); + if ((i == 0 && bcon_first == 1) || (i == count_cells + 1 && bcon_last == 1)) + { + continue; + } + if (i >= 0 && i <= 1 + count_cells * (1 + stag_data->count_stag)) + { + surface_ptr1 = Utilities::Rxn_find(Rxn_surface_map, i); + if (surface_ptr1 != NULL) + { + Rxn_surface_map[i] = jit->second; + } + else + { + //Add to map + Rxn_surface_map[i] = jit->second; + } + } + } + return (OK); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +reformat_surf(const char *comp_name, LDBLE fraction, const char *new_comp_name, +LDBLE new_Dw, int l_cell) +/* ---------------------------------------------------------------------- */ +{ + cxxSurface *surface_ptr; + + if ((surface_ptr = Utilities::Rxn_find(Rxn_surface_map, l_cell)) == NULL) + return (OK); + if (surface_ptr->Find_charge(comp_name) == NULL) + return (OK); + // Assume comp_name is charge name + std::string old_charge_name = comp_name; + std::string new_charge_name = new_comp_name; + if (fraction > 0.99999999) + fraction = 0.99999999; + + cxxSurface temp_surface(*surface_ptr); + + cxxSurface change; + + for (size_t i = 0; i < temp_surface.Get_surface_comps().size(); i++) + { + cxxSurfaceComp *comp_ptr = &(temp_surface.Get_surface_comps()[i]); + std::string charge_name = comp_ptr->Get_charge_name(); + if (charge_name == old_charge_name) + { + cxxSurfaceComp comp(*comp_ptr); + comp.multiply(fraction); + std::string std_comp_name = comp_ptr->Get_formula(); + Utilities::replace(comp_name, new_comp_name, std_comp_name); + comp.Set_formula(std_comp_name.c_str()); + comp.Set_charge_name(new_comp_name); + cxxNameDouble nd; + cxxNameDouble::iterator it; + for (it = comp.Get_totals().begin(); it != comp.Get_totals().end(); it++) + { + std::string tot_name(it->first); + Utilities::replace(comp_name, new_comp_name, tot_name); + nd[tot_name] = it->second; + } + comp.Set_totals(nd); + change.Get_surface_comps().push_back(comp); + comp_ptr->multiply(1 - fraction); + } + } + for (size_t i = 0; i < temp_surface.Get_surface_charges().size(); i++) + { + cxxSurfaceCharge *charge_ptr = &(temp_surface.Get_surface_charges()[i]); + std::string charge_name = charge_ptr->Get_name(); + if (charge_name == old_charge_name) + { + cxxSurfaceCharge charge(*charge_ptr); + charge.multiply(fraction); + std::string std_charge_name = charge_ptr->Get_name(); + Utilities::replace(comp_name, new_comp_name, std_charge_name); + charge.Set_name(std_charge_name.c_str()); + change.Get_surface_charges().push_back(charge); + charge_ptr->multiply(1 - fraction); + } + } + temp_surface.add(change, 1.0); + // change Dw + for (size_t i = 0; i < temp_surface.Get_surface_comps().size(); i++) + { + cxxSurfaceComp *comp_ptr = &(temp_surface.Get_surface_comps()[i]); + std::string charge_name = comp_ptr->Get_charge_name(); + if (charge_name == new_charge_name) + { + comp_ptr->Set_Dw(new_Dw); + } + } + temp_surface.Set_transport(false); + for (size_t i = 0; i < temp_surface.Get_surface_comps().size(); i++) + { + if (temp_surface.Get_surface_comps()[i].Get_Dw() > 0) + { + temp_surface.Set_transport(true); + break; + } + } + temp_surface.Sort_comps(); + Rxn_surface_map[l_cell] = temp_surface; + return OK; +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +viscosity(void) +/* ---------------------------------------------------------------------- */ +{ + + /* from Atkins, 1994. Physical Chemistry, 5th ed. */ + //viscos = + // pow((LDBLE) 10., + // -(1.37023 * (tc_x - 20) + + // 0.000836 * (tc_x - 20) * (tc_x - 20)) / (109 + tc_x)); + /* Huber et al., 2009, J. Phys. Chem. Ref. Data, Vol. 38, 101-125 */ + LDBLE H[4] = { 1.67752, 2.20462, 0.6366564, -0.241605 }; + LDBLE Tb = tk_x / 647.096, denom = H[0], mu0; + int i, j, i1; + for (i = 1; i < 4; i++) + denom += H[i] / pow(Tb, i); + mu0 = 100.0 * sqrt(Tb) / denom; + + LDBLE H2[42] = + { 5.20094e-1, 2.22531e-1, -2.81378e-1, 1.61913e-1, -3.25372e-2, 0, 0, + 8.50895e-2, 9.99115e-1, -9.06851e-1, 2.57399e-1, 0, 0, 0, + -1.08374, 1.88797, -7.72479e-1, 0, 0, 0, 0, + -2.89555e-1, 1.26613, -4.89837e-1, 0, 6.98452e-2, 0, -4.35673e-3, + 0, 0, -2.57040e-1, 0, 0, 8.72102e-3, 0, + 0, 1.20573e-1, 0, 0, 0, 0, -5.93264e-4 }; + LDBLE Rb = rho_0 / 0.322, Tb_1, Rb_1, S1 = 0.0, S2, mu1; + Tb_1 = 1.0 / Tb - 1.0; Rb_1 = Rb - 1.0; + for (i = 0; i < 6; i++) + { + S2 = 0.0; + for (j = 0; j < 7; j++) + { + i1 = 7 * i; + if (!H2[i1 + j]) + continue; + if (j) + S2 += H2[i1 + j] * pow(Rb_1, j); + else + S2 += H2[i1]; + } + if (i) + S1 += S2 * pow(Tb_1, i); + else + S1 += S2; + } + mu1 = exp(Rb * S1); + viscos_0 = viscos = mu0 * mu1 / 1e3; + viscos_0_25 = 0.8900239182946; + //#define OLD_VISCOSITY +#ifdef OLD_VISCOSITY + /* from Atkins, 1994. Physical Chemistry, 5th ed. */ + viscos = + pow((LDBLE) 10., + -(1.37023 * (tc_x - 20) + + 0.000836 * (tc_x - 20) * (tc_x - 20)) / (109 + tc_x)); + viscos_0_25 = 0.88862; +#endif + //return viscos; + if (!print_viscosity) + return viscos; + + /* (modified) Jones-Dole eqn for viscosity: + viscos / viscos_0 = + 1 + A * eq_tot^0.5 + + f_an * (Sum(B_i * m_i) + + Sum(D_i * m_i * ((1 + f_I) * mu_x^d3_i + (m_i * f_z)^d3_i) / (2 + f_I))) + A calculated from Falkenhagen-Dole + B_i = b0 + b1*exp(b2 * tc), b0..2 in Jones_Dole[0..2], read in SOLUTION_SPECIES + D_i = d1 * exp(d2 * tc), d1, 2 in Jones_Dole[3, 4] + d3_i in Jones_Dole[5] + Jones_Dole[6] contains the anion factor, 1 for Cl-, variable for other anions + f_z = (z * z + |z|) / 2, the contribution of the ion to mu_x, if z = 0: f_z = mu_x / m_i + f_I = variable, depends on d3_i > 1, or d3_i < 1. + tc is limited to 200�C. + + + A from Falkenhagen-Dole for a salt: + A = 4.3787e-14 * TK**1.5 / (eps_r**0.5)* (z1 + z2)**-0.5 / (D1 * D2) * psi + psi = (D1*z2 + D2*z1)/4 - z1*z2 * (D1-D2)**2 / ((D1*z1 + D2*z2)**0.5 + ((D1 + D2) * (z1 + z2))**0.5)**2 + D1, z1 for the cation, D2, |z2| for the anion of the salt. + We use the harmonic mean of the Dw's, and the arithmetic mean of the z's, + both weighted by the equivalent concentration. + */ + LDBLE D1, D2, z1, z2, m_plus, m_min, eq_plus, eq_min, eq_dw_plus, eq_dw_min, t1, t2, ta; + LDBLE A, psi, Bc = 0, Dc = 0, Dw = 0.0, l_z, f_z, lm, V_an, m_an, V_Cl, tc; + + m_plus = m_min = eq_plus = eq_min = eq_dw_plus = eq_dw_min = V_an = m_an = V_Cl = ta = 0; + + tc = (tc_x > 200) ? 200 : tc_x; + + for (i = 0; i < count_s_x; i++) + { + if (s_x[i]->type != AQ && s_x[i]->type > HPLUS) + continue; + if ((lm = s_x[i]->lm) < -9) + continue; + if (s_x[i]->Jones_Dole[0] || s_x[i]->Jones_Dole[1] || s_x[i]->Jones_Dole[3]) + { + t1 = s_x[i]->moles / mass_water_aq_x; + l_z = fabs(s_x[i]->z); + if (l_z) + f_z = (l_z * l_z + l_z) / 2; + else + f_z = mu_x / t1; + //if data at tc's other than 25 are scarce, put the values found for 25 C in [7] and [8], optimize [1], [2], and [4]... + if (s_x[i]->Jones_Dole[7] || s_x[i]->Jones_Dole[8]) + { + s_x[i]->Jones_Dole[0] = s_x[i]->Jones_Dole[7] - + s_x[i]->Jones_Dole[1] * exp(-s_x[i]->Jones_Dole[2] * 25.0); + s_x[i]->Jones_Dole[3] = + s_x[i]->Jones_Dole[8] / exp(-s_x[i]->Jones_Dole[4] * 25.0); + } + // find B * m and D * m * mu^d3 + Bc += (s_x[i]->Jones_Dole[0] + + s_x[i]->Jones_Dole[1] * exp(-s_x[i]->Jones_Dole[2] * tc)) * + t1; + // define f_I from the exponent of the D * m^d3 term... + if (s_x[i]->Jones_Dole[5] >= 1) + t2 = mu_x / 3 / s_x[i]->Jones_Dole[5]; + else if (s_x[i]->Jones_Dole[5] > 0.4) + t2 = -0.8 / s_x[i]->Jones_Dole[5]; + else + t2 = -1; + Dc += (s_x[i]->Jones_Dole[3] * exp(-s_x[i]->Jones_Dole[4] * tc)) * + t1 * (pow(mu_x, s_x[i]->Jones_Dole[5])*(1 + t2) + pow(t1 * f_z, s_x[i]->Jones_Dole[5])) / (2 + t2); + //output_msg(sformatf("\t%s\t%e\t%e\t%e\n", s_x[i]->name, t1, Bc, Dc )); + } + // parms for A... + if ((l_z = s_x[i]->z) == 0) + continue; + Dw = s_x[i]->dw; + if (Dw) + { + Dw *= (0.89 / viscos_0 * tk_x / 298.15); + if (s_x[i]->dw_t) + Dw *= exp(s_x[i]->dw_t / tk_x - s_x[i]->dw_t / 298.15); + } + if (l_z < 0) + { + if (!strcmp(s_x[i]->name, "Cl-")) + // volumina for f_an... + { + V_Cl = s_x[i]->logk[vm_tc]; + V_an += V_Cl * s_x[i]->moles; + ta += s_x[i]->moles; + m_an += s_x[i]->moles; + } + else// if (s_x[i]->Jones_Dole[6]) + { + V_an += s_x[i]->logk[vm_tc] * s_x[i]->Jones_Dole[6] * s_x[i]->moles; + ta += s_x[i]->moles; + m_an += s_x[i]->moles; + } + if (Dw) + { + // anions for A... + m_min += s_x[i]->moles; + t1 = s_x[i]->moles * l_z; + eq_min -= t1; + eq_dw_min -= t1 / Dw; + } + } + else if (Dw) + { + // cations for A... + m_plus += s_x[i]->moles; + t1 = s_x[i]->moles * l_z; + eq_plus += t1; + eq_dw_plus += t1 / Dw; + } + } + if (m_plus && m_min && eq_dw_plus && eq_dw_min) + { + z1 = eq_plus / m_plus; z2 = eq_min / m_min; + D1 = eq_plus / eq_dw_plus; D2 = eq_min / eq_dw_min; + + t1 = (D1 - D2) / (sqrt(D1 * z1 + D2 * z2) + sqrt((D1 + D2) * (z1 + z2))); + psi = (D1 * z2 + D2 * z1) / 4.0 - z1 * z2 * t1 * t1; + // Here A is A * viscos_0, avoids multiplication later on... + A = 4.3787e-14 * pow(tk_x, 1.5) / (sqrt(eps_r * (z1 + z2) / ((z1 > z2) ? z1 : z2)) * (D1 * D2)) * psi; + } + else + A = 0; + viscos = viscos_0 + A * sqrt((eq_plus + eq_min) / 2 / mass_water_aq_x); + if (m_an) + { + V_an /= m_an; + ta /= m_an; + } + if (!V_Cl) + V_Cl = calc_vm_Cl(); + if (V_an && V_Cl && ta) + viscos += (viscos_0 * (2 - ta * V_an / V_Cl) * (Bc + Dc)); + else + viscos += (viscos_0 * (Bc + Dc)); + if (viscos < 0) + { + viscos = 0; + warning_msg("viscosity < 0, reset to 0."); + } + return viscos; + +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_vm_Cl(void) +/* ---------------------------------------------------------------------- */ +{ + /* + * Calculate molar volume of Cl- with a Redlich type eqn: + Vm = Vm0(tc) + (Av / 2) * z^2 * I^0.5 + coef(tc) * I^(b4). + * Vm0(tc) is calc'd using supcrt parms, or from millero[0] + millero[1] * tc + millero[2] * tc^2 + * for Av * z^2 * I^0.5, see Redlich and Meyer, Chem. Rev. 64, 221. + Av is in (cm3/mol)(mol/kg)^-0.5, = DH_Av. + If b_Av != 0, the extended DH formula is used: I^0.5 /(1 + b_Av * DH_B * I^0.5). + DH_Av and DH_B are from calc_dielectrics(tc, pa). + * coef(tc) = logk[vmi1] + logk[vmi2] / (TK - 228) + logk[vmi3] * (TK - 228). + * b4 = logk[vmi4], or + * coef(tc) = millero[3] + millero[4] * tc + millero[5] * tc^2 + */ + LDBLE V_Cl = 0; + LDBLE pb_s = 2600. + patm_x * 1.01325, TK_s = tc_x + 45.15, sqrt_mu = sqrt(mu_x); + struct species *s_ptr; + + s_ptr = s_search("Cl-"); + if (!s_ptr) + return V_Cl; + + if (s_ptr->logk[vma1]) + { + /* supcrt volume at I = 0... */ + V_Cl = s_ptr->logk[vma1] + s_ptr->logk[vma2] / pb_s + + (s_ptr->logk[vma3] + s_ptr->logk[vma4] / pb_s) / TK_s - + s_ptr->logk[wref] * QBrn; + /* the ionic strength term * I^0.5... */ + if (s_ptr->logk[b_Av] < 1e-5) + V_Cl += s_ptr->z * s_ptr->z * 0.5 * DH_Av * sqrt_mu; + else + { + /* limit the Debye-Hueckel slope by b... */ + /* pitzer... */ + //s_ptr->rxn_x->logk[vm_tc] += s_ptr->z * s_ptr->z * 0.5 * DH_Av * + // log(1 + s_ptr->logk[b_Av] * sqrt(mu_x)) / s_ptr->logk[b_Av]; + /* extended DH... */ + V_Cl += s_ptr->z * s_ptr->z * 0.5 * DH_Av * + sqrt_mu / (1 + s_ptr->logk[b_Av] * DH_B * sqrt_mu); + } + /* plus the volume terms * I... */ + if (s_ptr->logk[vmi1] != 0.0 || s_ptr->logk[vmi2] != 0.0 || s_ptr->logk[vmi3] != 0.0) + { + LDBLE bi = s_ptr->logk[vmi1] + s_ptr->logk[vmi2] / TK_s + s_ptr->logk[vmi3] * TK_s; + if (s_ptr->logk[vmi4] == 1.0) + V_Cl += bi * mu_x; + else + V_Cl += bi * pow(mu_x, s_ptr->logk[vmi4]); + } + } + else if (s_ptr->millero[0]) + { + /* Millero volume at I = 0... */ + V_Cl = s_ptr->millero[0] + tc_x * (s_ptr->millero[1] + tc_x * s_ptr->millero[2]); + if (s_ptr->z) + { + /* the ionic strength terms... */ + V_Cl += s_ptr->z * s_ptr->z * 0.5 * DH_Av * sqrt_mu + + (s_ptr->millero[3] + tc_x * (s_ptr->millero[4] + tc_x * s_ptr->millero[5])) * mu_x; + } + } + return V_Cl; +} diff --git a/phreeqcpp/utilities.cpp b/phreeqcpp/utilities.cpp new file mode 100644 index 00000000..22b45a0a --- /dev/null +++ b/phreeqcpp/utilities.cpp @@ -0,0 +1,2062 @@ +#include "Utils.h" +#include "Phreeqc.h" +#include "phqalloc.h" +#include "NameDouble.h" +#include "Exchange.h" +#include "Solution.h" +#include + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_elt_list(struct elt_list *elt_list_ptr, LDBLE coef) +/* ---------------------------------------------------------------------- */ +{ + struct elt_list *elt_list_ptr1; + + if (elt_list_ptr == NULL) + return (OK); + + for (elt_list_ptr1 = elt_list_ptr; elt_list_ptr1->elt != NULL; + elt_list_ptr1++) + { + if (count_elts >= max_elts) + { + space((void **) ((void *) &elt_list), count_elts, &max_elts, + sizeof(struct elt_list)); + } + elt_list[count_elts].elt = elt_list_ptr1->elt; + elt_list[count_elts].coef = elt_list_ptr1->coef * coef; + count_elts++; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +add_elt_list_multi_surf(struct elt_list *elt_list_ptr, LDBLE coef, struct element *surf_elt_ptr) +/* ---------------------------------------------------------------------- */ +{ + struct elt_list *elt_list_ptr1; + + if (elt_list_ptr == NULL || surf_elt_ptr == NULL) + return (OK); + + // determine if surf_elt_ptr is first surface + bool first_surface = true; + for (elt_list_ptr1 = elt_list_ptr; elt_list_ptr1->elt != NULL; + elt_list_ptr1++) + { + if (elt_list_ptr1->elt->master->type == SURF) + { + if (elt_list_ptr1->elt == surf_elt_ptr) + { + first_surface = true; + break; + } + else + { + first_surface = false; + break; + } + } + } + if (first_surface) + { + for (elt_list_ptr1 = elt_list_ptr; elt_list_ptr1->elt != NULL; + elt_list_ptr1++) + { + if (count_elts >= max_elts) + { + space((void **) ((void *) &elt_list), count_elts, &max_elts, + sizeof(struct elt_list)); + } + if (elt_list_ptr1->elt == surf_elt_ptr) + { + elt_list[count_elts].elt = elt_list_ptr1->elt; + elt_list[count_elts].coef = elt_list_ptr1->coef * coef; + count_elts++; + } + else if (elt_list_ptr1->elt->master->type == SURF) + { + continue; + } + else + { + elt_list[count_elts].elt = elt_list_ptr1->elt; + elt_list[count_elts].coef = elt_list_ptr1->coef * coef; + count_elts++; + } + } + } + else + { + for (elt_list_ptr1 = elt_list_ptr; elt_list_ptr1->elt != NULL; + elt_list_ptr1++) + { + if (count_elts >= max_elts) + { + space((void **) ((void *) &elt_list), count_elts, &max_elts, + sizeof(struct elt_list)); + } + if (elt_list_ptr1->elt == surf_elt_ptr) + { + elt_list[count_elts].elt = elt_list_ptr1->elt; + elt_list[count_elts].coef = elt_list_ptr1->coef * coef; + count_elts++; + } + } + } + return (OK); +} +int Phreeqc:: +add_elt_list(const cxxNameDouble & nd, LDBLE coef) +/* ---------------------------------------------------------------------- */ +{ + cxxNameDouble::const_iterator cit = nd.begin(); + for ( ; cit != nd.end(); cit++) + { + if (count_elts >= max_elts) + { + space((void **) ((void *) &elt_list), count_elts, &max_elts, + sizeof(struct elt_list)); + } + elt_list[count_elts].elt = element_store(cit->first.c_str()); + elt_list[count_elts].coef = cit->second * coef; + count_elts++; + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_alk(struct reaction * rxn_ptr) +/* ---------------------------------------------------------------------- */ +{ + int i; + LDBLE return_value; + struct master *master_ptr; + + return_value = 0.0; + for (i = 1; rxn_ptr->token[i].s != NULL; i++) + { + master_ptr = rxn_ptr->token[i].s->secondary; + if (master_ptr == NULL) + { + master_ptr = rxn_ptr->token[i].s->primary; + } + if (master_ptr == NULL) + { + error_string = sformatf( + "Non-master species in secondary reaction, %s.", + rxn_ptr->token[0].s->name); + error_msg(error_string, CONTINUE); + input_error++; + break; + } + return_value += rxn_ptr->token[i].coef * master_ptr->alk; + } + return (return_value); +} + +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_rho_0(LDBLE tc, LDBLE pa) +/* ---------------------------------------------------------------------- */ +{ + /* Density of pure water + Wagner and Pruss, 2002, JPCRD 31, 387, eqn. 2.6, along the saturation pressure line + + interpolation 0 - 300 oC, 0.006 - 1000 atm... + */ + if (tc > 350.) + { + if (need_temp_msg < 1) + { + std::ostringstream w_msg; + w_msg << "Fitting range for density of pure water is 0-300 C.\n"; + w_msg << "Using temperature of 350 C for density and dielectric calculation."; + warning_msg(w_msg.str().c_str()); + need_temp_msg++; + } + tc = 350.; + } + LDBLE T = tc + 273.15; + //eqn. 2.6... + LDBLE Tc = 647.096, th = 1 - T / Tc; + LDBLE b1 = 1.99274064, b2 = 1.09965342, b3 = -0.510839303, + b4 = -1.75493479, b5 = -45.5170352, b6 = -6.7469445e5; + rho_0_sat = 322.0 * (1.0 + b1 * pow(th, (LDBLE) 1./3.) + b2 * pow(th, (LDBLE) 2./3.) + b3 * pow(th, (LDBLE) 5./3.) +\ + b4 * pow(th, (LDBLE) 16./3.) + b5 * pow(th, (LDBLE) 43./3.) + b6 * pow(th, (LDBLE) 110./3)); + //pressure... + LDBLE p0 = 5.1880000E-02 + tc * (-4.1885519E-04 + tc * ( 6.6780748E-06 + tc * (-3.6648699E-08 + tc * 8.3501912E-11))); + LDBLE p1 = -6.0251348E-06 + tc * ( 3.6696407E-07 + tc * (-9.2056269E-09 + tc * ( 6.7024182E-11 + tc * -1.5947241E-13))); + LDBLE p2 = -2.2983596E-09 + tc * (-4.0133819E-10 + tc * ( 1.2619821E-11 + tc * (-9.8952363E-14 + tc * 2.3363281E-16))); + LDBLE p3 = 7.0517647E-11 + tc * ( 6.8566831E-12 + tc * (-2.2829750E-13 + tc * ( 1.8113313E-15 + tc * -4.2475324E-18))); + /* The minimal pressure equals the saturation pressure... */ + if (ah2o_x <= 1.0) + p_sat = exp(11.6702 - 3816.44 / (T - 46.13)) * ah2o_x; + else + p_sat = exp(11.6702 - 3816.44 / (T - 46.13)); + //ah2o_x0 = ah2o_x; // for updating rho in model(): compare with new ah2o_x + if (pa < p_sat || (use.Get_solution_ptr() && use.Get_solution_ptr()->Get_patm() < p_sat)) + { + pa = p_sat; + } + if (!use.Get_gas_phase_in()) + patm_x = pa; + pa -= (p_sat - 1e-6); + rho_0 = rho_0_sat + pa * (p0 + pa * (p1 + pa * (p2 + sqrt(pa) * p3))); + if (rho_0 < 0.01) + rho_0 = 0.01; + + /* compressibility, d(ln(rho)) / d(P), 1/atm... */ + kappa_0 = (p0 + pa * (2 * p1 + pa * (3 * p2 + sqrt(pa) * 3.5 * p3))) / rho_0; + + return (rho_0 / 1e3); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +calc_dielectrics(LDBLE tc, LDBLE pa) +/* ---------------------------------------------------------------------- */ +{ + /* Relative dielectric constant of pure water, eps as a function of (P, T) + Bradley and Pitzer, 1979, JPC 83, 1599. + (newer data in Fernandez et al., 1995, JPCRD 24, 33, + and Fernandez et al., 1997, JPCRD 26, 1125, show its correctness) + + d(eps)/d(P), Debye-Hueckel A and B, and Av (for Av, see Pitzer et al., 1984, JPCRD 13, p. 4) + */ + if (tc > 350.) + { + tc = 350.; + } + LDBLE T = tc + 273.15; + LDBLE u1 = 3.4279e2, u2 = -5.0866e-3, u3 = 9.469e-7, u4 = -2.0525, + u5 = 3.1159e3, u6 = -1.8289e2, u7 = -8.0325e3, u8 = 4.2142e6, + u9 = 2.1417; + LDBLE d1000 = u1 * exp(T * (u2 + T * u3)); // relative dielectric constant at 1000 bar + LDBLE c = u4 + u5 / (u6 + T); + LDBLE b = u7 + u8 / T + u9 * T; + LDBLE pb = pa * 1.01325; // pa in bar + eps_r = d1000 + c * log((b + pb) / (b + 1e3)); // relative dielectric constant + if (eps_r <= 0) + { + eps_r = 10.; + warning_msg("Relative dielectric constant is negative.\nTemperature is out of range of parameterization."); + } + + /* qe^2 / (eps_r * kB * T) = 4.803204e-10**2 / 1.38065e-16 / (eps_r * T) + = 1.671008e-3 (esu^2 / (erg/K)) / (eps_r * T) */ + LDBLE e2_DkT = 1.671008e-3 / (eps_r * T); + + DH_B = sqrt(8 * pi * AVOGADRO * e2_DkT * rho_0 / 1e3); // Debye length parameter, 1/cm(mol/kg)^-0.5 + + DH_A = DH_B * e2_DkT / (2. * LOG_10); //(mol/kg)^-0.5 + + /* A0 in pitzer */ + if (pitzer_model || sit_model) + { + A0 = DH_B * e2_DkT / 6.0; + if (pitzer_model && aphi != NULL) + { + calc_pitz_param(aphi, T, 298.15); + A0 = aphi->p; + } + } + + /* Debye-Hueckel limiting slope = DH_B * e2_DkT * RT * (d(ln(eps_r)) / d(P) - compressibility) */ + DH_Av = DH_B * e2_DkT * R_LITER_ATM * 1e3 * T * (c / (b + pb) * 1.01325 / eps_r - kappa_0 / 3.); // (cm3/mol)(mol/kg)^-0.5 + + DH_B /= 1e8; // kappa, 1/Angstrom(mol/kg)^-0.5 + + /* the Born functions, * 41.84 to give molal volumes in cm3/mol... */ + ZBrn = (- 1 / eps_r + 1.0) * 41.84004; + QBrn = c / (b + pb) / eps_r / eps_r * 41.84004; + /* dgdP from subroutine gShok2 in supcrt92, g is neglected here (at tc < 300)... + and, dgdP is small. Better, adapt Wref to experimental Vm's */ + dgdP = 0; + //if (tc > 150 && rho_0 < 1.0) + //{ + // LDBLE sc[7] = {1, -0.2037662e+01, 0.5747000e-02, -0.6557892e-05, + // 0.6107361e+01, -0.1074377e-01, 0.1268348e-04}; + // LDBLE csc[4] = {1, 0.3666666e+02, -0.1504956e-9, 0.5017997e-13}; + // LDBLE sa = sc[1] + tc * (sc[2] + tc * sc[3]); + // LDBLE sb = sc[4] + tc * (sc[5] + tc * sc[6]); + + // dgdP = - sa * sb * pow(1.0 - rho_0, sb - 1.0) * rho_0 * kappa_0 / 1.01325; + + // LDBLE ft = pow((tc - 155.0)/300.0, 4.8) + csc[1] * pow((tc - 155.0)/300.0, 16.0); + // LDBLE dfdP = ft * (-3.0 * csc[2] * pow(1000.0 - pb, 2) - 4.0 * csc[3] * pow(1000.0 - pb, 3)); + // dgdP -= dfdP; + //} + + return (OK); +} + + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +compute_gfw(const char *string, LDBLE * gfw) +/* ---------------------------------------------------------------------- */ +{ +/* + * Input: string contains a chemical formula + * Output: gfw contains the calculated gfw + */ + std::string str(string); + std::map::iterator it; + it = gfw_map.find(str); + if (it != gfw_map.end()) + { + *gfw = it->second; + return OK; + } + + int i; + char token[MAX_LENGTH]; + char *ptr; + + count_elts = 0; + paren_count = 0; + strcpy(token, string); + ptr = token; + if (get_elts_in_species(&ptr, 1.0) == ERROR) + { + return (ERROR); + } + *gfw = 0.0; + for (i = 0; i < count_elts; i++) + { + if (elt_list[i].elt->gfw <= 0.0) + { + return (ERROR); + } + *gfw += elt_list[i].coef * (elt_list[i].elt)->gfw; + } + gfw_map[str] = *gfw; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +copy_token(char *token_ptr, char **ptr, int *length) +/* ---------------------------------------------------------------------- */ +{ +/* + * Copies from **ptr to *token_ptr until first space is encountered. + * + * Arguments: + * *token_ptr output, place to store token + * + * **ptr input, character string to read token from + * output, next position after token + * + * length output, length of token + * + * Returns: + * UPPER, + * LOWER, + * DIGIT, + * EMPTY, + * UNKNOWN. + */ + int i, return_value; + char c; + +/* + * Read to end of whitespace + */ + while (isspace((int) (c = **ptr))) + (*ptr)++; +/* + * Check what we have + */ + if (isupper((int) c) || c == '[') + { + return_value = UPPER; + } + else if (islower((int) c)) + { + return_value = LOWER; + } + else if (isdigit((int) c) || c == '.' || c == '-') + { + return_value = DIGIT; + } + else if (c == '\0') + { + return_value = EMPTY; + } + else + { + return_value = UNKNOWN; + } +/* + * Begin copying to token + */ + i = 0; + while ((!isspace((int) (c = **ptr))) && + /* c != ',' && */ + c != ';' && c != '\0') + { + token_ptr[i] = c; + (*ptr)++; + i++; + } + token_ptr[i] = '\0'; + *length = i; +#ifdef PHREEQ98 + if ((return_value == DIGIT) && (strstr(token_ptr, ",") != NULL)) + { + error_string = sformatf( + "Commas are not allowed as decimal separator: %s.", + token_ptr); + error_msg(error_string, CONTINUE); + } +#endif + return (return_value); +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +copy_token(std::string &token, char **ptr) +/* ---------------------------------------------------------------------- */ +{ +/* + * Copies from **ptr to *token until first space is encountered. + * + * Arguments: + * &token_ptr output, place to store token + * + * **ptr input, character string to read token from + * output, next position after token + * + * Returns: + * UPPER, + * LOWER, + * DIGIT, + * EMPTY, + * UNKNOWN. + */ + int return_value; + char c; + +/* + * Read to end of whitespace + */ + token.clear(); + while (isspace((int) (c = **ptr))) + (*ptr)++; +/* + * Check what we have + */ + if (isupper((int) c) || c == '[') + { + return_value = UPPER; + } + else if (islower((int) c)) + { + return_value = LOWER; + } + else if (isdigit((int) c) || c == '.' || c == '-') + { + return_value = DIGIT; + } + else if (c == '\0') + { + return_value = EMPTY; + } + else + { + return_value = UNKNOWN; + } +/* + * Begin copying to token + */ + char c_char[2]; + c_char[1] = '\0'; + while ((!isspace((int) (c = **ptr))) && + /* c != ',' && */ + c != ';' && c != '\0') + { + c_char[0] = c; + token.append(c_char); + (*ptr)++; + } +#ifdef PHREEQ98 + if ((return_value == DIGIT) && (strstr(token_ptr, ",") != NULL)) + { + error_string = sformatf( + "Commas are not allowed as decimal separator: %s.", + token_ptr); + error_msg(error_string, CONTINUE); + } +#endif + return (return_value); +} +#if defined PHREEQ98 +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +copy_title(char *token_ptr, char **ptr, int *length) +/* ---------------------------------------------------------------------- */ +{ +/* + * Copies from **ptr to *token_ptr until first space or comma is encountered. + * + * Arguments: + * *token_ptr output, place to store token + * + * **ptr input, character string to read token from + * output, next position after token + * + * length output, length of token + * + * Returns: + * UPPER, + * LOWER, + * DIGIT, + * EMPTY, + * UNKNOWN. + */ + int i, return_value; + char c; + int Quote = FALSE; + +/* + * Read to end of whitespace + */ + while (isspace((int) (c = **ptr)) || (c == ',') || (c == '"')) + { + if (c == '"') + Quote = TRUE; + (*ptr)++; + } +/* + * Check what we have + */ + if (isupper((int) c) || c == '[') + { + return_value = UPPER; + } + else if (islower((int) c)) + { + return_value = LOWER; + } + else if (isdigit((int) c) || c == '.' || c == '-') + { + return_value = DIGIT; + } + else if (c == '\0') + { + return_value = EMPTY; + } + else + { + return_value = UNKNOWN; + } +/* + * Begin copying to token + */ + i = 0; + if (Quote == TRUE) + { + while (((int) (c = **ptr) != '"') && c != '\0') + { + token_ptr[i] = c; + (*ptr)++; + i++; + } + } + else + { + while ((!isspace((int) (c = **ptr))) && + c != ',' && c != ';' && c != '\0') + { + token_ptr[i] = c; + (*ptr)++; + i++; + } + } + token_ptr[i] = '\0'; + *length = i; + return (return_value); +} +#endif +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +dup_print(const char *ptr, int emphasis) +/* ---------------------------------------------------------------------- */ +{ +/* + * print character string to output and logfile + * if emphasis == TRUE the print is set off by + * a row of dashes before and after the character string. + * + */ + int l, i; + char *dash; + + if (pr.headings == FALSE) + return (OK); +#ifdef PHREEQ98 + if ((CreateToC == TRUE) && (AutoLoadOutputFile == TRUE)) + { + if (strstr(ptr, "Reading") == ptr) + AddToCEntry((char *) ptr, 1, outputlinenr); + else if (strstr(ptr, "Beginning") == ptr) + AddToCEntry((char *) ptr, 2, outputlinenr); + else if ((strstr(ptr, "TITLE") != ptr) && (strstr(ptr, "End") != ptr)) + AddToCEntry((char *) ptr, 3, outputlinenr); + } +#endif + std::string save_in(ptr); + l = (int) strlen(ptr); + dash = (char *) PHRQ_malloc((size_t) (l + 2) * sizeof(char)); + if (dash == NULL) + malloc_error(); + if (emphasis == TRUE) + { + for (i = 0; i < l; i++) + dash[i] = '-'; + dash[i] = '\0'; + output_msg(sformatf("%s\n%s\n%s\n\n", dash, save_in.c_str(), dash)); + log_msg(sformatf("%s\n%s\n%s\n\n", dash, save_in.c_str(), dash)); + } + else + { + output_msg(sformatf("%s\n\n", save_in.c_str())); + log_msg(sformatf("%s\n\n", save_in.c_str())); + } + dash = (char *) free_check_null(dash); + + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +equal(LDBLE a, LDBLE b, LDBLE eps) +/* ---------------------------------------------------------------------- */ +{ +/* + * Checks equality between two LDBLE precision numbers + */ + if (fabs(a - b) <= eps) + return (TRUE); + return (FALSE); +} + +/* ---------------------------------------------------------------------- */ +void * Phreeqc:: +free_check_null(void *ptr) +/* ---------------------------------------------------------------------- */ +{ + if (ptr != NULL) + { + PHRQ_free(ptr); + } + return (NULL); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_token(char **eqnaddr, char *string, LDBLE * l_z, int *l) +/* ---------------------------------------------------------------------- */ +/* + * Function finds next species in equation, coefficient has already + * been removed. Also determines charge on the species. + * + * Arguments: + * *eqnaddr input, pointer to position in eqn to start parsing + * output, pointer to a pointer to next position in eqn to start + * parsing. + * *string input pointer to place to store token + * *l_z charge on token + * *l length of token + * + * Returns: + * ERROR, + * OK. + */ +{ + int i, j; + int ltoken, lcharge; + char c; + char *ptr, *ptr1, *rest; + char charge[MAX_LENGTH]; + + rest = *eqnaddr; + ptr = *eqnaddr; + i = 0; +/* + * Find end of token or begining of charge + */ + while (((c = *ptr) != '+') && (c != '-') && (c != '=') && (c != '\0')) + { + string[i++] = c; + if (c == '[') + { + ptr++; + while ((c = *ptr) != ']') + { + if (c == '\0') + { + error_string = sformatf( + "No final bracket \"]\" for element name, %s.", + string); + error_msg(error_string, CONTINUE); + return (ERROR); + } + string[i++] = c; + if (i >= MAX_LENGTH) + { + output_msg(sformatf( + "Species name greater than MAX_LENGTH (%d) characters.\n%s\n", + MAX_LENGTH, string)); + return (ERROR); + } + ptr++; + } + string[i++] = c; + } + + /* check for overflow of space */ + if (i >= MAX_LENGTH) + { + output_msg(sformatf( + "Species name greater than MAX_LENGTH (%d) characters.\n%s\n", + MAX_LENGTH, string)); + return (ERROR); + } + ptr++; + } + string[i] = '\0'; + ltoken = i; +/* + * Check for an empty string + */ + if (i == 0) + { + error_string = sformatf( "NULL string detected in get_token, %s.", rest); + error_msg(error_string, CONTINUE); + return (ERROR); + } +/* + * End of token is = or \0, charge is zero + */ + if (c == '=' || c == '\0') + { + *eqnaddr = ptr; + lcharge = 0; + *l_z = 0.0; + } + else + { +/* + * Copy characters into charge until next species or end is detected + */ + j = 0; + ptr1 = ptr; + while ((isalpha((int) (c = *ptr1)) == FALSE) && + (c != '(') && + (c != ')') && + (c != ']') && (c != '[') && (c != '=') && (c != '\0')) + { + charge[j++] = c; + /* error if no more space */ + if (j >= MAX_LENGTH) + { + error_msg + ("The charge on a species has exceeded MAX_LENGTH characters.", + CONTINUE); + return (ERROR); + } + ptr1++; + } +/* + * Go back to last + or - if not end of side, + * everything before the last + or - in charge is part of the charge + */ + if ((c != '=') && (c != '\0')) + { + while (((c = *ptr1) != '+') && (c != '-')) + { + j--; + ptr1--; + } + } + charge[j] = '\0'; + lcharge = j; + *eqnaddr = ptr1; +/* + * Charge has been written, now need to check if charge has legal format + */ + if (get_charge(charge, l_z) == OK) + { + strcat(string, charge); + } + else + { + return (ERROR); + } + } + *l = ltoken + lcharge; + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +isamong(char c, const char *s_l) +/* ---------------------------------------------------------------------- */ +/* + * Function checks if c is among the characters in the string s + * + * Arguments: + * c input, character to check + * *s string of characters + * + * Returns: + * TRUE if c is in set, + * FALSE if c in not in set. + */ +{ + int i; + + for (i = 0; s_l[i] != '\0'; i++) + { + if (c == s_l[i]) + { + return (TRUE); + } + } + return (FALSE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +islegit(const char c) +/* ---------------------------------------------------------------------- */ +/* + * Function checks for legal characters for chemical equations + * + * Argument: + * c input, character to check + * + * Returns: + * TRUE if c is in set, + * FALSE if c in not in set. + */ +{ + if (isalpha((int) c) || isdigit((int) c) || isamong(c, "+-=().:_[]")) + { + return (TRUE); + } + return (FALSE); +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +malloc_error(void) +/* ---------------------------------------------------------------------- */ +{ + error_msg("NULL pointer returned from malloc or realloc.", CONTINUE); + error_msg("Program terminating.", STOP); + return; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +parse_couple(char *token) +/* ---------------------------------------------------------------------- */ +{ +/* + * Parse couple puts redox couples in standard form + * "+" is removed and couples are rewritten in sort + * order. + */ + int e1, e2, p1, p2; + char *ptr; + char elt1[MAX_LENGTH], elt2[MAX_LENGTH], paren1[MAX_LENGTH], + paren2[MAX_LENGTH]; + + if (strcmp_nocase_arg1(token, "pe") == 0) + { + str_tolower(token); + return (OK); + } + while (replace("+", "", token) == TRUE); + ptr = token; + get_elt(&ptr, elt1, &e1); + if (*ptr != '(') + { + error_string = sformatf( "Element name must be followed by " + "parentheses in redox couple, %s.", token); + error_msg(error_string, CONTINUE); + parse_error++; + return (ERROR); + } + paren_count = 1; + paren1[0] = '('; + p1 = 1; + while (*ptr != '\0') + { + ptr++; + if (*ptr == '/' || *ptr == '\0') + { + error_string = sformatf( + "End of line or " "/" + " encountered before end of parentheses, %s.", token); + error_msg(error_string, CONTINUE); + return (ERROR); + } + paren1[p1++] = *ptr; + if (*ptr == '(') + paren_count++; + if (*ptr == ')') + paren_count--; + if (paren_count == 0) + break; + } + paren1[p1] = '\0'; + ptr++; + if (*ptr != '/') + { + error_string = sformatf( " " "/" " must follow parentheses " + "ending first half of redox couple, %s.", token); + error_msg(error_string, CONTINUE); + parse_error++; + return (ERROR); + } + ptr++; + get_elt(&ptr, elt2, &e2); + if (strcmp(elt1, elt2) != 0) + { + error_string = sformatf( "Redox couple must be two redox states " + "of the same element, %s.", token); + error_msg(error_string, CONTINUE); + return (ERROR); + } + if (*ptr != '(') + { + error_string = sformatf( "Element name must be followed by " + "parentheses in redox couple, %s.", token); + error_msg(error_string, CONTINUE); + parse_error++; + return (ERROR); + } + paren2[0] = '('; + paren_count = 1; + p2 = 1; + while (*ptr != '\0') + { + ptr++; + if (*ptr == '/' || *ptr == '\0') + { + error_string = sformatf( "End of line or " "/" " encountered" + " before end of parentheses, %s.", token); + error_msg(error_string, CONTINUE); + return (ERROR); + } + + paren2[p2++] = *ptr; + if (*ptr == '(') + paren_count++; + if (*ptr == ')') + paren_count--; + if (paren_count == 0) + break; + } + paren2[p2] = '\0'; + if (strcmp(paren1, paren2) < 0) + { + strcpy(token, elt1); + strcat(token, paren1); + strcat(token, "/"); + strcat(token, elt2); + strcat(token, paren2); + } + else if (strcmp(paren1, paren2) > 0) + { + strcpy(token, elt2); + strcat(token, paren2); + strcat(token, "/"); + strcat(token, elt1); + strcat(token, paren1); + } + else + { + error_string = sformatf( "Both parts of redox couple are the same, %s.", + token); + error_msg(error_string, CONTINUE); + return (ERROR); + } + return (OK); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +print_centered(const char *string) +/* ---------------------------------------------------------------------- */ +{ + int i, l, l1, l2; + char token[MAX_LENGTH]; + +#ifdef PHREEQ98 + if ((CreateToC == TRUE) && (AutoLoadOutputFile == TRUE)) + AddToCEntry((char *) string, 4, outputlinenr); +#endif + l = (int) strlen(string); + l1 = (79 - l) / 2; + l2 = 79 - l - l1; + for (i = 0; i < l1; i++) + token[i] = '-'; + token[i] = '\0'; + strcat(token, string); + for (i = 0; i < l2; i++) + token[i + l1 + l] = '-'; + token[79] = '\0'; + output_msg(sformatf("%s\n\n", token)); + return (OK); +} +bool Phreeqc:: +replace(const char *str1, const char *str2, std::string & str) +{ + size_t pos = str.find(str1); + if (pos != std::string::npos) + { + size_t l = strlen(str1); + str.replace(pos, l, str2); + return true; + } + return false; +} +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +replace(const char *str1, const char *str2, char *str) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function replaces str1 with str2 in str + * + * Arguments: + * str1 search str for str1 + * str2 replace str1 if str1 found in str + * str string to be searched + * + * Returns + * TRUE if string was replaced + * FALSE if string was not replaced + */ + int l, l1, l2; + char *ptr_start; + + ptr_start = strstr(str, str1); +/* + * Str1 not found, return + */ + if (ptr_start == NULL) + return (FALSE); +/* + * Str1 found, replace Str1 with Str2 + */ + l = (int) strlen(str); + l1 = (int) strlen(str1); + l2 = (int) strlen(str2); +/* + * Make gap in str long enough for str2 + */ + /* The plus one includes the terminating NULL */ + memmove(ptr_start + l2, ptr_start + l1, l - (ptr_start - str + l1) + 1); +/* + * Copy str2 into str + */ + memcpy(ptr_start, str2, l2); + return (TRUE); +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +space(void **ptr, int i, int *max, int struct_size) +/* ---------------------------------------------------------------------- */ +{ +/* + * Routine has 4 functions, allocate space, reallocate space, test to + * determine whether space is available, and free space. + * + * Arguments: + * ptr pointer to malloced space + * i value for test + * i = INIT, allocates space + * i >= 0 && i < max, space is available, return + * i >= max, reallocate space + * i = FREE, free space. + * max maximum value for i with available space + * struct size size of structure to be allocated + */ +/* + * Return if space exists + */ + if ((i >= 0) && (i + 1 < *max)) + { + return; + } +/* + * Realloc space + */ + if (i + 1 >= *max) + { + if (*max > 1000) + { + *max += 1000; + } + else + { + *max *= 2; + } + if (i + 1 > *max) + *max = i + 1; + *ptr = PHRQ_realloc(*ptr, (size_t) (*max) * struct_size); + if (*ptr == NULL) + malloc_error(); + return; + } +/* + * Allocate space + */ + if (i == INIT) + { +/* free(*ptr); */ + *ptr = PHRQ_malloc((size_t) (*max) * struct_size); + if (*ptr == NULL) + malloc_error(); + return; + } +/* + * Free space + */ +/* + if ( i == FREE ) { + free(*ptr); + return; + } + */ +/* + * Error + */ + error_msg("Illegal argument to function space.", CONTINUE); + error_msg("Program terminating.", STOP); + return; +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +squeeze_white(char *s_l) +/* ---------------------------------------------------------------------- */ +/* + * Delete all white space from string s + * + * Argument: + * *s_l input, character string, possibly containing white space + * output, character string with all white space removed + * + * Return: void + */ +{ + int i, j; + + for (i = j = 0; s_l[i] != '\0'; i++) + { + if (!isspace((int) s_l[i])) + s_l[j++] = s_l[i]; + } + s_l[j] = '\0'; +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +str_tolower(char *str) +/* ---------------------------------------------------------------------- */ +{ +/* + * Replaces string, str, with same string, lower case + */ + char *ptr; + ptr = str; + while (*ptr != '\0') + { + *ptr = (char) tolower(*ptr); + ptr++; + } +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +str_toupper(char *str) +/* ---------------------------------------------------------------------- */ +{ +/* + * Replaces string, str, with same string, lower case + */ + char *ptr; + ptr = str; + while (*ptr != '\0') + { + *ptr = (char) toupper(*ptr); + ptr++; + } +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +strcmp_nocase(const char *str1, const char *str2) +/* ---------------------------------------------------------------------- */ +{ +/* + * Compare two strings disregarding case + */ + int c1, c2; + while ((c1 = tolower(*str1++)) == (c2 = tolower(*str2++))) + { + if (c1 == '\0') + return (0); + } + if (c1 < c2) + return (-1); + return (1); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +strcmp_nocase_arg1(const char *str1, const char *str2) +/* ---------------------------------------------------------------------- */ +{ +/* + * Compare two strings disregarding case + */ + int c1, c2; + while ((c1 = tolower(*str1++)) == (c2 = *str2++)) + { + if (c1 == '\0') + return (0); + } + if (c1 < c2) + return (-1); + return (1); +} + +/* ---------------------------------------------------------------------- */ +char * Phreeqc:: +#if !defined(NDEBUG) && defined(WIN32_MEMORY_DEBUG) +_string_duplicate(const char *token, const char *szFileName, int nLine) +#else +string_duplicate(const char *token) +#endif +/* ---------------------------------------------------------------------- */ +{ + int l; + char *str; + + if (token == NULL) + return NULL; + l = (int) strlen(token); +#if !defined(NDEBUG) && defined(WIN32_MEMORY_DEBUG) + str = (char *) _malloc_dbg((size_t) (l + 1) * sizeof(char), _NORMAL_BLOCK, szFileName, nLine); +#else + str = (char *) PHRQ_malloc((size_t) (l + 1) * sizeof(char)); +#endif + + if (str == NULL) + malloc_error(); + strcpy(str, token); + return (str); +} +#ifdef HASH +/* ---------------------------------------------------------------------- */ +const char * Phreeqc:: +string_hsave(const char *str) +/* ---------------------------------------------------------------------- */ +{ +/* + * Save character string str + * + * Arguments: + * str input string to save. + * + * Returns: + * starting address of saved string (str) + */ + std::hash_map::const_iterator it; + it = strings_hash.find(str); + if (it != strings_hash.end()) + { + return (it->second->c_str()); + } + + std::string *stdstr = new std::string(str); + strings_map[*stdstr] = stdstr; + return(stdstr->c_str()); +} +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +strings_hash_clear() +/* ---------------------------------------------------------------------- */ +{ +/* + * Save character string str + * + * Arguments: + * str input string to save. + * + * Returns: + * starting address of saved string (str) + */ + std::hash_map::iterator it; + for (it = strings_hash.begin(); it != strings_hash.end(); it++) + { + delete it->second; + } + strings_hash.clear(); +} +#else +/* ---------------------------------------------------------------------- */ +const char * Phreeqc:: +string_hsave(const char *str) +/* ---------------------------------------------------------------------- */ +{ +/* + * Save character string str + * + * Arguments: + * str input string to save. + * + * Returns: + * starting address of saved string (str) + */ + std::map::const_iterator it; + it = strings_map.find(str); + if (it != strings_map.end()) + { + return (it->second->c_str()); + } + + std::string *stdstr = new std::string(str); + strings_map[*stdstr] = stdstr; + return(stdstr->c_str()); +} +#endif +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +strings_map_clear() +/* ---------------------------------------------------------------------- */ +{ +/* + * Save character string str + * + * Arguments: + * str input string to save. + * + * Returns: + * starting address of saved string (str) + */ + std::map::iterator it; + for (it = strings_map.begin(); it != strings_map.end(); it++) + { + delete it->second; + } + strings_map.clear(); +} +/* ---------------------------------------------------------------------- */ +LDBLE Phreeqc:: +under(LDBLE xval) +/* ---------------------------------------------------------------------- */ +{ +/* + * Exponentiate a number, x, but censor large and small numbers + * log values less than MIN_LM are set to 0 + * log values greater than MAX_LM are set to 10**MAX_LM + */ +/* if (xval < MIN_LM) { */ + if (xval < -40.) + { + return (0.0); + } + if (xval > MAX_LM) + { + return ( MAX_M ); + } + return (pow ((LDBLE) 10.0, xval)); +} +#ifndef PHREEQCI_GUI +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +status(int count, const char *str, bool rk_string) +/* ---------------------------------------------------------------------- */ +{ + char sim_str[20]; + char state_str[45]; + char spin_str[2]; + clock_t t2; + +#ifdef PHREEQ98 + if (ProcessMessages) + ApplicationProcessMessages(); + if (stop_calculations == TRUE) + error_msg("Execution canceled by user.", STOP); +#endif + if (pr.status == FALSE || phast == TRUE) + return (OK); + + if (state == INITIALIZE) + { + screen_string = sformatf("\n%-80s", "Initializing..."); + screen_msg(screen_string.c_str()); + status_on = true; + return (OK); + } +#ifdef NPP + t2 = clock(); + if (((state < ADVECTION && reaction_step < count_total_steps) || + (state == ADVECTION && (advection_step < count_ad_shifts || cell_no < count_cells)) || + (state == TRANSPORT && (transport_step < count_shifts || (mixrun < nmix /*&& + cell_no < count_cells*/)))) + && (int) (1e3 / CLOCKS_PER_SEC * (t2 - status_timer)) < status_interval) + return (OK); +#endif + + switch (state) + { + case INITIALIZE: + break; + case TRANSPORT: + if (str != NULL) + { + if (rk_string) + { + + screen_string = screen_string.substr(0, 43); + screen_string.append(str); + status_string = screen_string; + } + else + { + screen_string = "\r"; + screen_string.append(str); + status_string = screen_string; + } + status_on = true; + } + case PHAST: + break; + default: + // if str not NULL, print it + if (str != NULL && !rk_string) + { + screen_string = "\r"; + screen_string.append(str); + status_string = screen_string; + } + else + // print state + { + std::string stdstr; + if (str != NULL && rk_string) + { + stdstr = str; + } + sprintf(sim_str, "\rSimulation %d.", simulation); + sprintf(state_str, " "); + sprintf(spin_str, " "); + switch (state) + { + default: + break; + case INITIAL_SOLUTION: + sprintf(state_str, "Initial solution %d.", use.Get_solution_ptr()->Get_n_user()); + break; + case INITIAL_EXCHANGE: + sprintf(state_str, "Initial exchange %d.", use.Get_exchange_ptr()->Get_n_user()); + break; + case INITIAL_SURFACE: + sprintf(state_str, "Initial surface %d.", use.Get_surface_ptr()->Get_n_user()); + break; + case INVERSE: + sprintf(state_str, "Inverse %d. Models = %d.", use.Get_inverse_ptr()->n_user, count); + break; + case REACTION: + if (use.Get_kinetics_in() == TRUE) + { + sprintf(state_str, "Kinetic step %d.", reaction_step); + } + else + { + sprintf(state_str, "Reaction step %d.", reaction_step); + } + break; + case ADVECTION: + sprintf(state_str, "Advection, shift %d.", advection_step); + break; + } + spinner++; + if (spinner == 1) + { + spin_str[0] = '/'; + } + else if (spinner == 2) + { + spin_str[0] = '-'; + } + else + { + spin_str[0] = '\\'; + spinner = 0; + } + if (use.Get_kinetics_in() == TRUE) + { + screen_string = sformatf("%-15s%-27s%38s", sim_str, state_str, stdstr.c_str()); + status_string = screen_string; + } + else + { + screen_string = sformatf("%-15s%-27s%1s%37s", sim_str, state_str, spin_str, stdstr.c_str()); + status_string = screen_string; + } + } + status_on = true; + break; + } + +#ifndef NPP + t2 = clock(); + if ((int) (1e3 / CLOCKS_PER_SEC * (t2 - status_timer)) > status_interval) +#endif + { + status_timer = t2; + screen_msg(status_string.c_str()); + status_string.clear(); + } + return (OK); +} +#endif /*PHREEQCI_GUI */ +/* +** Dynamic hashing, after CACM April 1988 pp 446-457, by Per-Ake Larson. +** Coded into C, with minor code improvements, and with hsearch(3) interface, +** by ejp@ausmelb.oz, Jul 26, 1988: 13:16; +** also, hcreate/hdestroy routines added to simulate hsearch(3). +** +** These routines simulate hsearch(3) and family, with the important +** difference that the hash table is dynamic - can grow indefinitely +** beyond its original size (as supplied to hcreate()). +** +** Performance appears to be comparable to that of hsearch(3). +** The 'source-code' options referred to in hsearch(3)'s 'man' page +** are not implemented; otherwise functionality is identical. +** +** Compilation controls: +** DEBUG controls some informative traces, mainly for debugging. +** HASH_STATISTICS causes HashAccesses and HashCollisions to be maintained; +** when combined with DEBUG, these are displayed by hdestroy(). +** +** Problems & fixes to ejp@ausmelb.oz. WARNING: relies on pre-processor +** concatenation property, in probably unnecessary code 'optimisation'. +** Esmond Pitt, Austec (Asia/Pacific) Ltd +** ...!uunet.UU.NET!munnari!ausmelb!ejp,ejp@ausmelb.oz +*/ + +# include + +/* +** Fast arithmetic, relying on powers of 2, +** and on pre-processor concatenation property +*/ + +/* rewrote to remove MUL and DIV */ +# define MOD(x,y) ((x) & ((y)-1)) + +/* +** Local data +*/ + +#ifdef HASH_STATISTICS + long HashAccesses, HashCollisions; +#endif + +/* +** Code +*/ + +int Phreeqc:: +hcreate_multi(unsigned Count, HashTable ** HashTable_ptr) +{ + int i; + HashTable *Table; + /* + ** Adjust Count to be nearest higher power of 2, + ** minimum SegmentSize, then convert into segments. + */ + i = SegmentSize; + while (i < (int) Count) + i <<= 1; +/* Count = DIV(i,SegmentSize); */ + Count = ((i) >> (SegmentSizeShift)); + + Table = (HashTable *) PHRQ_calloc(sizeof(HashTable), 1); + *HashTable_ptr = Table; + + if (Table == NULL) + return (0); + /* + ** resets are redundant - done by calloc(3) + ** + Table->SegmentCount = Table->p = Table->KeyCount = 0; + */ + /* + ** Allocate initial 'i' segments of buckets + */ + for (i = 0; i < (int) Count; i++) + { + Table->Directory[i] = + (Segment *) PHRQ_calloc(sizeof(Segment), SegmentSize); + if (Table->Directory[i] == NULL) + { + hdestroy_multi(Table); + return (0); + } + Table->SegmentCount++; + } +/* Table->maxp = MUL(Count,SegmentSize); */ + Table->maxp = (short) ((Count) << (SegmentSizeShift)); + Table->MinLoadFactor = 1; + Table->MaxLoadFactor = DefaultMaxLoadFactor; +#ifdef HASH_STATISTICS + HashAccesses = HashCollisions = 0; +#endif + return (1); +} + +void Phreeqc:: +hdestroy_multi(HashTable * Table) +{ + int i, j; + Segment *seg; + Element *p, *q; + + if (Table != NULL) + { + for (i = 0; i < Table->SegmentCount; i++) + { + /* test probably unnecessary */ + if ((seg = Table->Directory[i]) != NULL) + { + for (j = 0; j < SegmentSize; j++) + { + p = seg[j]; + while (p != NULL) + { + q = p->Next; + PHRQ_free((void *) p); + p = q; + } + } + PHRQ_free(Table->Directory[i]); + } + } + PHRQ_free(Table); + /* Table = NULL; */ + } +} + +ENTRY * Phreeqc:: +hsearch_multi(HashTable * Table, ENTRY item, ACTION action) +/* ACTION FIND/ENTER */ +{ + Address h; + Segment *CurrentSegment; + int SegmentIndex; + int SegmentDir; + Segment *p, q; + + assert(Table != NULL); /* Kinder really than return(NULL); */ +#ifdef HASH_STATISTICS + HashAccesses++; +#endif + h = Hash_multi(Table, item.key); +/* SegmentDir = DIV(h,SegmentSize); */ + SegmentDir = ((h) >> (SegmentSizeShift)); + SegmentIndex = MOD(h, SegmentSize); + /* + ** valid segment ensured by Hash() + */ + CurrentSegment = Table->Directory[SegmentDir]; + assert(CurrentSegment != NULL); /* bad failure if tripped */ + p = &CurrentSegment[SegmentIndex]; + q = *p; + /* + ** Follow collision chain + */ + while (q != NULL && strcmp(q->Key, item.key)) + { + p = &q->Next; + q = *p; +#ifdef HASH_STATISTICS + HashCollisions++; +#endif + } + if (q != NULL /* found */ + || action == FIND /* not found, search only */ + ) + { + return ((ENTRY *) q); + } + else if ((q = (Element *) PHRQ_calloc(sizeof(Element), 1)) == NULL) + { + malloc_error(); + } + *p = q; /* link into chain */ + /* + ** Initialize new element + */ + q->Key = item.key; + q->Data = (char *) item.data; + /* + ** cleared by calloc(3) + q->Next = NULL; + */ + /* + ** Table over-full? + */ +/* if (++Table->KeyCount / MUL(Table->SegmentCount,SegmentSize) > Table->MaxLoadFactor) */ + if (++Table->KeyCount / ((Table->SegmentCount) << (SegmentSizeShift)) > + Table->MaxLoadFactor) + ExpandTable_multi(Table); /* doesn`t affect q */ + return ((ENTRY *) q); +} + +/* +** Internal routines +*/ + + Address Phreeqc:: +Hash_multi(HashTable * Table, const char *Key) +{ + Address h, address; + register unsigned char *k = (unsigned char *) Key; + + h = 0; + /* + ** Convert string to integer + */ + while (*k) + h = h * Prime1 ^ (*k++ - ' '); + h %= Prime2; + address = MOD(h, Table->maxp); + if (address < (unsigned long) Table->p) + address = MOD(h, (Table->maxp << 1)); /* h % (2*Table->maxp) */ + return (address); +} + +void Phreeqc:: +ExpandTable_multi(HashTable * Table) +{ + Address NewAddress; + int OldSegmentIndex, NewSegmentIndex; + int OldSegmentDir, NewSegmentDir; + Segment *OldSegment, *NewSegment; + Element *Current, **Previous, **LastOfNew; + +/* if (Table->maxp + Table->p < MUL(DirectorySize,SegmentSize)) */ + if (Table->maxp + Table->p < ((DirectorySize) << (SegmentSizeShift))) + { + /* + ** Locate the bucket to be split + */ +/* OldSegmentDir = DIV(Table->p,SegmentSize); */ + OldSegmentDir = ((Table->p) >> (SegmentSizeShift)); + OldSegment = Table->Directory[OldSegmentDir]; + OldSegmentIndex = MOD(Table->p, SegmentSize); + /* + ** Expand address space; if necessary create a new segment + */ + NewAddress = Table->maxp + Table->p; +/* NewSegmentDir = DIV(NewAddress,SegmentSize); */ + NewSegmentDir = ((NewAddress) >> (SegmentSizeShift)); + NewSegmentIndex = MOD(NewAddress, SegmentSize); + if (NewSegmentIndex == 0) + { + Table->Directory[NewSegmentDir] = + (Segment *) PHRQ_calloc(sizeof(Segment), SegmentSize); + if (Table->Directory[NewSegmentDir] == NULL) + { + malloc_error(); + } + } + NewSegment = Table->Directory[NewSegmentDir]; + /* + ** Adjust state variables + */ + Table->p++; + if (Table->p == Table->maxp) + { + Table->maxp <<= 1; /* Table->maxp *= 2 */ + Table->p = 0; + } + Table->SegmentCount++; + /* + ** Relocate records to the new bucket + */ + Previous = &OldSegment[OldSegmentIndex]; + Current = *Previous; + LastOfNew = &NewSegment[NewSegmentIndex]; + *LastOfNew = NULL; + while (Current != NULL) + { + if (Hash_multi(Table, Current->Key) == NewAddress) + { + /* + ** Attach it to the end of the new chain + */ + *LastOfNew = Current; + /* + ** Remove it from old chain + */ + *Previous = Current->Next; + LastOfNew = &Current->Next; + Current = Current->Next; + *LastOfNew = NULL; + } + else + { + /* + ** leave it on the old chain + */ + Previous = &Current->Next; + Current = Current->Next; + } + } + } +} + + +void Phreeqc:: +free_hash_strings(HashTable * Table) +{ + int i, j; + Segment *seg; + Element *p, *q; + + if (Table != NULL) + { + for (i = 0; i < Table->SegmentCount; i++) + { + /* test probably unnecessary */ + if ((seg = Table->Directory[i]) != NULL) + { + for (j = 0; j < SegmentSize; j++) + { + p = seg[j]; + while (p != NULL) + { + q = p->Next; + p->Data = (char *) free_check_null((void *) p->Data); + p = q; + } + } + } + } + } +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +string_trim(char *str) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function trims white space from left and right of string + * + * Arguments: + * str string to trime + * + * Returns + * TRUE if string was changed + * FALSE if string was not changed + * EMPTY if string is all whitespace + */ + int i, l, start, end, length; + char *ptr_start; + + l = (int) strlen(str); + /* + * leading whitespace + */ + for (i = 0; i < l; i++) + { + if (isspace((int) str[i])) + continue; + break; + } + if (i == l) + return (EMPTY); + start = i; + ptr_start = &(str[i]); + /* + * trailing whitespace + */ + for (i = l - 1; i >= 0; i--) + { + if (isspace((int) str[i])) + continue; + break; + } + end = i; + if (start == 0 && end == l) + return (FALSE); + length = end - start + 1; + memmove((void *) str, (void *) ptr_start, (size_t) length); + str[length] = '\0'; + + return (TRUE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +string_trim_right(char *str) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function trims white space from right of string + * + * Arguments: + * str string to trime + * + * Returns + * TRUE if string was changed + * FALSE if string was not changed + * EMPTY if string is all whitespace + */ + int i, l, end, length; + + l = (int) strlen(str); + for (i = l - 1; i >= 0; i--) + { + if (isspace((int) str[i])) + continue; + break; + } + end = i; + length = end + 1; + str[length] = '\0'; + if (end == 0) + return (EMPTY); + if (end == l) + return (FALSE); + return (TRUE); +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +string_trim_left(char *str) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function trims white space from left of string + * + * Arguments: + * str string to trime + * + * Returns + * TRUE if string was changed + * FALSE if string was not changed + * EMPTY if string is all whitespace + */ + int i, l, start, end, length; + char *ptr_start; + + l = (int) strlen(str); + /* + * leading whitespace + */ + for (i = 0; i < l; i++) + { + if (isspace((int) str[i])) + continue; + break; + } + if (i == l) + return (EMPTY); + start = i; + ptr_start = &(str[i]); + end = l; + if (start == 0 && end == l) + return (FALSE); + length = end - start + 1; + memmove((void *) str, (void *) ptr_start, (size_t) length); + str[length] = '\0'; + + return (TRUE); +} + +/* ---------------------------------------------------------------------- */ +char * Phreeqc:: +string_pad(const char *str, int i) +/* ---------------------------------------------------------------------- */ +{ +/* + * Function returns new string padded to width i + * or returns str if longer + * Arguments: + * str string to pad + * + * Returns + * new string of with i + */ + int j, l, max; + char *str_ptr; + + l = (int) strlen(str); + max = l; + if (l < i) + max = i; + str_ptr = (char *) PHRQ_malloc((size_t) ((max + 1) * sizeof(char))); + if (str_ptr == NULL) + malloc_error(); + strcpy(str_ptr, str); + if (i > l) + { + for (j = l; j < i; j++) + { + str_ptr[j] = ' '; + } + str_ptr[i] = '\0'; + } + return (str_ptr); +} + +/* ---------------------------------------------------------------------- */ +void Phreeqc:: +zero_double(LDBLE * target, int n) +/* ---------------------------------------------------------------------- */ +{ + int i; + + if (n > zeros_max) + { + zeros = (LDBLE *) PHRQ_realloc(zeros, (size_t) (n * sizeof(LDBLE))); + if (zeros == NULL) + malloc_error(); + for (i = zeros_max; i < n; i++) + { + zeros[i] = 0.0; + } + zeros_max = n; + } + memcpy((void *) target, (void *) zeros, (size_t) (n * sizeof(LDBLE))); + return; +} + +/* ---------------------------------------------------------------------- */ +int Phreeqc:: +get_input_errors() +/* ---------------------------------------------------------------------- */ +{ + if (input_error == 0) + { + return phrq_io->Get_io_error_count(); + } + return input_error; +}