#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. #if defined(PHREEQCI_GUI) #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #endif /* ---------------------------------------------------------------------- */ 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; 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].isotope_unknowns.size() > 0) { inverse[n].isotope_unknowns.clear(); } 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(class inverse *inv_ptr) /* ---------------------------------------------------------------------- */ { /* * Fill in array for an inverse problem */ int i, j, k, i_alk, i_carb; size_t max; size_t count_rows_t; size_t column, row; int temp; LDBLE isotope_number; LDBLE f, coef, cb, conc; char token[MAX_LENGTH]; class phase *phase_ptr; cxxSolution *solution_ptr; CReaction *rxn_ptr; class 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 */ if (inv_ptr->isotopes.size() > 0) { set_isotope_unknowns(inv_ptr); 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->elts.size() * inv_ptr->count_solns + /* epsilons */ inv_ptr->count_solns + /* solutions */ inv_ptr->phases.size() + /* phases */ inv_ptr->count_redox_rxns + /* redox reactions */ carbon * inv_ptr->count_solns + /* pH */ 1 + /* water */ inv_ptr->isotope_unknowns.size() * inv_ptr->count_solns + /* isotopes in solution */ inv_ptr->isotopes.size() * inv_ptr->phases.size() + /* 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->phases.size(); col_epsilon = col_redox + inv_ptr->count_redox_rxns; col_ph = col_epsilon + inv_ptr->elts.size() * 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->isotope_unknowns.size() * inv_ptr->count_solns; max_row_count = inv_ptr->count_solns * inv_ptr->elts.size() + /* optimize */ carbon * inv_ptr->count_solns + /* optimize ph */ 1 + /* optimize water */ inv_ptr->count_solns * inv_ptr->isotope_unknowns.size() + /* optimize isotopes */ inv_ptr->isotopes.size() * inv_ptr->phases.size() + /* optimize phase isotopes */ inv_ptr->elts.size() + /* mass balances */ 1 + 1 + /* fractions, init and final */ inv_ptr->count_solns + /* charge balances */ carbon * inv_ptr->count_solns + /* dAlk = dC + dph */ inv_ptr->isotopes.size() + /* isotopes */ 2 * inv_ptr->count_solns * inv_ptr->elts.size() + /* epsilon constraints */ 2 * carbon * inv_ptr->count_solns + /* epsilon on ph */ 2 + /* epsilon for water */ 2 * inv_ptr->isotope_unknowns.size() * inv_ptr->count_solns + /* epsilon for isotopes */ 2 * inv_ptr->isotopes.size() * inv_ptr->phases.size() + /* epsilon for isotopes in phases */ 2; /* work space */ row_mb = inv_ptr->count_solns * inv_ptr->elts.size() + carbon * inv_ptr->count_solns + 1 + inv_ptr->count_solns * inv_ptr->isotope_unknowns.size() + inv_ptr->isotopes.size() * inv_ptr->phases.size(); row_fract = row_mb + inv_ptr->elts.size(); 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->isotopes.size(); /* 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 */ my_array.resize(max_column_count * max_row_count); array1.resize(max_column_count * max_row_count); col_name.resize(max_column_count); row_name.resize(max_row_count); delta.resize(max_column_count); inv_delta1.resize(max_column_count); delta2.resize(max_column_count); delta3.resize(max_column_count); delta_save.resize(max_column_count); min_delta.resize(max_column_count); max_delta.resize(max_column_count); inv_res.resize(max_row_count); if (max_column_count < max_row_count) { max = max_row_count; } else { max = max_column_count; } inv_zero.resize((size_t) max); /* * Define inv_zero and inv_zero array, delta */ for (i = 0; i < (int) 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 *) &(my_array[(size_t)i * max_column_count]), (void *) &(inv_zero[0]), max_column_count * sizeof(LDBLE)); } /* * begin filling array */ count_rows = 0; /* * optimization */ count_optimize = inv_ptr->count_solns * inv_ptr->elts.size() + /* optimize */ carbon * inv_ptr->count_solns + /* optimize ph */ 1 + /* optimize water */ inv_ptr->count_solns * inv_ptr->isotope_unknowns.size() + /* optimize isotopes */ inv_ptr->isotopes.size() * inv_ptr->phases.size(); /* 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 < (int)master.size(); 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->elts.size(); 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 = (int)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 < (int)master.size(); j++) { if (master[j]->in >= 0) { my_array[(size_t)master[j]->in * max_column_count + (size_t)i] = f * master[j]->total; if (master[j]->s == s_eminus) { my_array[(size_t)master[j]->in * max_column_count + (size_t)i] = 0.0; } } } /* calculate charge balance for elements in model */ cb = 0; for (j = 0; j < (int)master.size(); 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; my_array[((size_t)row_charge + (size_t)i) * max_column_count + (size_t)i] = cb; } /* mass_balance: phase data */ for (size_t i = 0; i < inv_ptr->phases.size(); 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; my_array[(size_t)row * max_column_count + (size_t)column] = rxn_ptr->token[j].coef * coef; } row = master_alk->in; /* include alkalinity for phase */ my_array[(size_t)row * max_column_count + (size_t)column] = calc_alk(*rxn_ptr); } /* mass balance: redox reaction data */ k = 0; for (i = 0; i < inv_ptr->elts.size(); 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); my_array[(size_t)row * max_column_count + (size_t)column] = rxn_ptr->token[j].coef; /* if coefficient of element is not 1.0 in master species */ if (j != 0) my_array[(size_t)row * max_column_count + (size_t)column] /= coef; } row = master_alk->in; /* include alkalinity for redox reaction */ my_array[(size_t)row * max_column_count + (size_t)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->elts.size(); i++) { row = inv_ptr->elts[i].master->in; for (j = 0; j < inv_ptr->count_solns; j++) { if (j < (inv_ptr->count_solns - 1)) { my_array[(size_t)row * max_column_count + (size_t)column] = 1.0; } else { my_array[(size_t)row * max_column_count + (size_t)column] = -1.0; } if (inv_ptr->elts[i].master->s == s_eminus) { my_array[(size_t)row * max_column_count + (size_t)column] = 0.0; } sprintf(token, "%s %d", row_name[row], j); col_name[column] = string_hsave(token); column++; } } count_rows += inv_ptr->elts.size(); /* 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->isotope_unknowns.size(); 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->isotopes.size() > 0) { /* isotopes of phases phases */ for (size_t i = 0; i < inv_ptr->phases.size(); i++) { for (j = 0; j < inv_ptr->isotopes.size(); 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) { my_array[count_rows * max_column_count + (size_t)i] = 1.0 / gfw_water * solution_ptr->Get_mass_water(); } else { my_array[count_rows * max_column_count + (size_t)inv_ptr->count_solns - 1] = -1.0 / gfw_water * solution_ptr->Get_mass_water(); } } /* coefficient for water uncertainty */ if (inv_ptr->water_uncertainty > 0) { my_array[count_rows * max_column_count + (size_t)col_water] = 1.0; } row_name[count_rows] = string_hsave("H2O"); row_water = count_rows; count_rows++; /* * Final solution fraction equals 1.0 */ my_array[count_rows * max_column_count + (size_t)inv_ptr->count_solns - 1] = 1.0; my_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 + (size_t)i] = solution_ptr->cb; */ for (j = 0; j < inv_ptr->elts.size(); 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; } my_array[count_rows * max_column_count + (size_t)column] = coef; if (inv_ptr->elts[j].master->s == s_eminus) { my_array[count_rows * max_column_count + (size_t)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; my_array[count_rows * max_column_count + (size_t)column] = inv_ptr->dalk_dph[i]; column = col_epsilon + i_alk * inv_ptr->count_solns + i; my_array[count_rows * max_column_count + (size_t)column] = -1.0; column = col_epsilon + i_carb * inv_ptr->count_solns + i; my_array[count_rows * max_column_count + (size_t)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->isotopes.size() != 0) { for (size_t j = 0; j < inv_ptr->isotopes.size(); j++) { isotope_balance_equation(inv_ptr, (int)count_rows, (int)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->elts.size(); 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 = my_array[(size_t)inv_ptr->elts[j].master->in * max_column_count + (size_t)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++) { my_array[(size_t)k * max_column_count + (size_t)column] = 0.0; } continue; } /* this statement probably obviates some of the following logic. */ /* coef += toler; */ /* scale epsilon optimization equation */ if (coef < toler) { my_array[((size_t)column - (size_t)col_epsilon) * max_column_count + (size_t)column] = SCALE_EPSILON / toler; } else { my_array[((size_t)column - (size_t)col_epsilon) * max_column_count + (size_t)column] = SCALE_EPSILON / coef; } /* set upper limit of change in positive direction */ if (coef < toler) { coef = toler; f = 10; } else { f = 1.0; } my_array[count_rows * max_column_count + (size_t)column] = 1.0 * f; my_array[count_rows * max_column_count + (size_t)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 = my_array[(size_t)inv_ptr->elts[j].master->in * max_column_count + (size_t)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; my_array[count_rows * max_column_count + (size_t)i] = -coef * f; my_array[count_rows * max_column_count + (size_t)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 */ my_array[((size_t)column - (size_t)col_epsilon) * max_column_count + (size_t)column] = SCALE_EPSILON / coef; /* set upper limit of change in positive direction */ my_array[count_rows * max_column_count + (size_t)column] = 1.0; my_array[count_rows * max_column_count + (size_t)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 */ my_array[count_rows * max_column_count + (size_t)column] = -1.0; my_array[count_rows * max_column_count + (size_t)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 */ my_array[count_rows * max_column_count + (size_t)column] = 1.0; my_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 */ my_array[count_rows * max_column_count + (size_t)column] = -1.0; my_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->isotopes.size() > 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->isotope_unknowns.size(); j++) { column = col_isotopes + (i * inv_ptr->isotope_unknowns.size()) + 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++) { class 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 */ my_array[((size_t)column - (size_t)col_epsilon) * max_column_count + (size_t)column] = SCALE_EPSILON / coef; /* set upper limit of change in positive direction */ my_array[count_rows * max_column_count + (size_t)column] = 1.0; my_array[count_rows * max_column_count + (size_t)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 */ my_array[count_rows * max_column_count + (size_t)column] = -1.0; my_array[count_rows * max_column_count + (size_t)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 (size_t i = 0; i < inv_ptr->phases.size(); i++) { if (inv_ptr->phases[i].constraint == PRECIPITATE) { delta[(size_t)col_phases + (size_t)i] = -1.0; } else if (inv_ptr->phases[i].constraint == DISSOLVE) { delta[(size_t)col_phases + (size_t)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++) { my_array[(size_t)row_water * max_column_count + (size_t)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)my_array[(size_t)i * max_column_count + (size_t)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(class 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 = (int)count_unknowns; /* columns in A, C, E */ klmd = (max_row_count - 2); nklmd = (n + klmd); n2d = (size_t)n + 2; max_good = MAX_MODELS; max_bad = MAX_MODELS; max_minimal = MAX_MODELS; good.resize(max_good); count_good = 0; bad.resize(max_bad); count_bad = 0; minimal.resize(max_minimal); count_minimal = 0; col_back.resize(max_column_count); row_back.resize(max_row_count); /* * Allocate space for arrays */ inv_cu.resize( 2 * (size_t)nklmd); memset(&inv_cu[0], 0, ((2 * nklmd * sizeof(LDBLE)))); inv_iu.resize(2 * nklmd); inv_is.resize(klmd); 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->phases.size() > 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 (size_t 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, (int)(inv_ptr->count_solns - 2), (int)(inv_ptr->count_solns - 1)) > 0; soln_bits--) { /* * Loop through all models of of descending size */ for (model_size = (int)inv_ptr->phases.size(); 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->phases.size()) + 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 (size_t i = 0; i < inv_ptr->phases.size(); i++) { if (equal(inv_delta1[i + inv_ptr->count_solns], 0.0, TOL) == TRUE) { good_bits = set_bit(good_bits, (int)i, 0); } } for (size_t i = 0; i < inv_ptr->count_solns; i++) { if (equal(inv_delta1[i], 0.0, TOL) == TRUE) { good_bits = set_bit(good_bits, (int)(i + inv_ptr->phases.size()), 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)); } my_array.clear(); delta.clear(); array1.clear(); inv_zero.clear(); inv_res.clear(); inv_delta1.clear(); delta2.clear(); delta3.clear(); delta_save.clear(); inv_cu.clear(); inv_iu.clear(); inv_is.clear(); col_name.clear(); row_name.clear(); col_back.clear(); row_back.clear(); min_delta.clear(); max_delta.clear(); good.clear(); bad.clear(); minimal.clear(); return (OK); } /* ---------------------------------------------------------------------- */ unsigned long Phreeqc:: minimal_solve(class inverse *inv_ptr, unsigned long minimal_bits) /* ---------------------------------------------------------------------- */ { /* * Starting with phases indicated in minimal bits, sequentially * remove phases to find minimal solution */ unsigned long temp_bits_l; if (debug_inverse == TRUE) { output_msg(sformatf( "Beginning minimal solve: \n")); bit_print(minimal_bits, (int)(inv_ptr->phases.size() + inv_ptr->count_solns)); } for (size_t i = 0; i < inv_ptr->phases.size() + inv_ptr->count_solns - 1; i++) { if (get_bits(minimal_bits, (int)i, 1) == 0) continue; temp_bits_l = 1 << (int)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, (int)(inv_ptr->phases.size() + 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, (int)(inv_ptr->phases.size() + inv_ptr->count_solns)); } solve_with_mask(inv_ptr, minimal_bits); unsigned long actual_bits = 0; for (size_t i = 0; i < inv_ptr->count_solns; i++) { if (equal(inv_delta1[i], 0.0, TOL) == FALSE) { actual_bits = set_bit(actual_bits, (int)(i + inv_ptr->phases.size()), 1); } } for (size_t i = 0; i < inv_ptr->phases.size(); i++) { if (equal(inv_delta1[i + inv_ptr->count_solns], 0.0, TOL) == FALSE) { actual_bits = set_bit(actual_bits, (int)i, 1); } } if (actual_bits != minimal_bits) { warning_msg("Roundoff errors in minimal calculation"); } return (actual_bits); } /* ---------------------------------------------------------------------- */ int Phreeqc:: solve_with_mask(class 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 = (int)row_mb; /* rows in A */ l = (int)(row_epsilon - row_mb); /* rows in C */ m = (int)(count_rows - row_epsilon); /* rows in E */ n = (int)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, &my_array[0], &array1[0], &k, &l, &m, &n, cur_bits, &delta2[0], &col_back[0], &row_back[0]); /* * 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[0], k + l + m, n + 1, (int)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 = 100000; count_calls++; #ifdef INVERSE_CL1MP if (inv_ptr->mp == TRUE) { cl1mp(k, l, m, n, (int)nklmd, (int)n2d, &array1[0], &kode, inv_ptr->mp_tolerance, &iter, &delta2[0], &inv_res[0], &error, &inv_cu[0], &inv_iu[0], &inv_is[0], TRUE, inv_ptr->mp_censor); } else { cl1(k, l, m, n, (int)nklmd, (int)n2d, &array1[0], &kode, toler, &iter, &delta2[0], &inv_res[0], &error, &inv_cu[0], &inv_iu[0], &inv_is[0], TRUE); } #else cl1(k, l, m, n, (int)nklmd, (int)n2d, &array1[0], &kode, toler, &iter, &delta2[0], &inv_res[0], &error, &inv_cu[0], &inv_iu[0], &inv_is[0], 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.resize(max_minimal); } 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.resize(max_good); } 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.resize(max_bad); } 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(class inverse *inv_ptr) /* ---------------------------------------------------------------------- */ { /* * Prints model */ int i, j; size_t column; int print_msg; cxxSolution *solution_ptr; class master *master_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->elts.size(); 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->isotopes.size() > 0) { /* adjustments to solution isotope composition */ for (j = 0; j < inv_ptr->isotope_unknowns.size(); 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->isotope_unknowns.size() + 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->isotopes.size() > 0) { output_msg(sformatf( "\nIsotopic composition of phases:\n")); for (size_t i = 0; i < inv_ptr->phases.size(); i++) { if (inv_ptr->phases[i].isotopes.size() == 0) continue; size_t 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; std::vector& isotope_ref = inv_ptr->phases[i].isotopes; for (size_t j = 0; j < inv_ptr->isotopes.size(); j++) { for (size_t k = 0; k < inv_ptr->phases[i].isotopes.size(); k++) { if (inv_ptr->isotopes[j].elt_name != isotope_ref[k].elt_name || inv_ptr->isotopes[j].isotope_number != isotope_ref[k].isotope_number) continue; d1 = isotope_ref[k].ratio; column = col_phase_isotopes + i * inv_ptr->isotopes.size() + 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_ref[k].ratio_uncertainty + toler)) { output_msg(sformatf( " **")); print_msg = TRUE; } output_msg(sformatf( "\n")); if (isotope_ref[k].ratio_uncertainty > 0) { scaled_error += fabs(d2) / isotope_ref[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)); } // appt, calculate and print SI's LDBLE t_i, p_i, iap, lk, t; const char *name; class rxn_token *rxn_ptr; CReaction *reaction_ptr; output_msg(sformatf( "\n%-25.25s %2s %12.12s %12.12s %-18.18s (Approximate SI in solution ", "Phase mole transfers:", " ", "Minimum", "Maximum", "Formula")); for (i = 0; i < inv_ptr->count_solns - 1; i++) output_msg(sformatf("%d, ", inv_ptr->solns[i])); solution_ptr = Utilities::Rxn_find(Rxn_solution_map, inv_ptr->solns[i]); t_i = solution_ptr->Get_tc() + 273.15; p_i = solution_ptr->Get_patm(); output_msg(sformatf("%d at %3d K, %3d atm)\n", inv_ptr->solns[i], int(t_i), int(floor(p_i + 0.5)))); p_i *= PASCAL_PER_ATM; for (size_t 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 %-25.25s (", col_name[i], (double)d1, (double)d2, (double)d3, inv_ptr->phases[i - col_phases].phase->formula)); size_t i1 = 0; for (; i1 < phases.size(); i1++) { if (Utilities::strcmp_nocase(phases[i1]->name, col_name[i])) continue; reaction_ptr = &phases[i1]->rxn_s; for (size_t i2 = 0; i2 < inv_ptr->count_solns; i2++) { solution_ptr = Utilities::Rxn_find(Rxn_solution_map, inv_ptr->solns[i2]); reaction_ptr->logk[delta_v] = calc_delta_v(*reaction_ptr, true) - phases[i1]->logk[vm0]; if (reaction_ptr->logk[delta_v]) mu_terms_in_logk = true; lk = k_calc(reaction_ptr->logk, t_i, p_i); iap = 0.0; for (rxn_ptr = &reaction_ptr->token[0] + 1; rxn_ptr->s != NULL; rxn_ptr++) { t = 0; if (rxn_ptr->s == s_eminus) t = -solution_ptr->Get_pe(); else if (!Utilities::strcmp_nocase(rxn_ptr->s->name, "H2O")) t = log10(solution_ptr->Get_ah2o()); else if (!Utilities::strcmp_nocase(rxn_ptr->s->name, "H+")) t = -solution_ptr->Get_ph(); else { if (rxn_ptr->s->secondary) name = rxn_ptr->s->secondary->elt->name; else name = rxn_ptr->s->primary->elt->name; t = solution_ptr->Get_master_activity()[name]; } if (t) iap += t * rxn_ptr->coef; else { iap = -999; break; } } if (iap == -999) output_msg(sformatf(" ")); else output_msg(sformatf("%6.2f", iap - lk)); if (i2 < inv_ptr->count_solns - 1) output_msg(sformatf(",")); } } output_msg(sformatf(")\n")); } output_msg(sformatf( "\n%-25.25s\n", "Redox mole transfers:")); for (size_t 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(class 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 (size_t 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())); } for (size_t 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); } /* ---------------------------------------------------------------------- */ int Phreeqc:: punch_model(class 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 (size_t 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(class 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] = (int)inv_ptr->phases.size() - 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(class 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 (size_t i = 0; i < inv_ptr->count_solns + inv_ptr->phases.size(); i++) { if (i < inv_ptr->phases.size()) { if (inv_ptr->phases[i].force == TRUE) { cur_bits = set_bit(cur_bits, (int)i, 1); } } else { if (inv_ptr->force_solns[i - inv_ptr->phases.size()] == TRUE) { cur_bits = set_bit(cur_bits, (int)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, (int)(inv_ptr->phases.size() + inv_ptr->count_solns) - 1, (int)inv_ptr->count_solns); bits += (get_bits(cur_bits, (int)inv_ptr->phases.size() - 1, (int)inv_ptr->phases.size()) << (int)inv_ptr->count_solns); /* * Do range calculation */ for (i = 0; i < inv_ptr->count_solns + inv_ptr->phases.size(); 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 = (int)row_mb; /* rows in A */ l = (int)(row_epsilon - row_mb); /* rows in C */ m = (int)(count_rows - row_epsilon); /* rows in E */ n = (int)count_unknowns; /* number of variables */ /* * Copy equations */ memcpy((void *) &(array1[0]), (void *) &(my_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[0], &array1[0], &k, &l, &m, &n, cur_bits, &delta2[0], &col_back[0], &row_back[0]); /* * 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[0], k + l + m, n + 1, (int)max_column_count); } kode = 1; iter = 200; count_calls++; #ifdef INVERSE_CL1MP if (inv_ptr->mp == TRUE) { cl1mp(k, l, m, n, (int)nklmd, (int)n2d, &array1[0], &kode, inv_ptr->mp_tolerance, &iter, &delta2[0], &inv_res[0], &error2, &inv_cu[0], &inv_iu[0], &inv_is[0], TRUE, inv_ptr->mp_censor); } else { cl1(k, l, m, n, (int)nklmd, (int)n2d, &array1[0], &kode, toler, &iter, &delta2[0], &inv_res[0], &error2, &inv_cu[0], &inv_iu[0], &inv_is[0], TRUE); } #else cl1(k, l, m, n, (int)nklmd, (int)n2d, &array1[0], &kode, toler, &iter, &delta2[0], &inv_res[0], &error2, &inv_cu[0], &inv_iu[0], &inv_is[0], 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(class 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; size_t 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->phases.size(); i++) { if (get_bits(cur_bits, i, 1) == 0) { col_back_l[col_phases + i] = -1; /* drop isotopes */ if (inv_ptr->isotopes.size() > 0) { for (j = 0; j < inv_ptr->isotopes.size(); j++) { column = col_phase_isotopes + i * inv_ptr->isotopes.size() + 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, (int)inv_ptr->phases.size() + i, 1) == 0) { col_back_l[i] = -1; /* drop all epsilons for the solution */ for (j = 0; j < inv_ptr->elts.size(); 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->isotopes.size() > 0) { for (size_t j = 0; j < inv_ptr->isotope_unknowns.size(); j++) { column = col_isotopes + i * inv_ptr->isotope_unknowns.size() + j; col_back_l[column] = -1; } } } } /* * Drop epsilons not used */ for (i = (int)col_epsilon; i < *n; i++) { if (col_back_l[i] < 0) continue; for (j = 0; j < (*k + *l + *m); j++) { if (array_out[(size_t)j * max_column_count + (size_t)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 = (int)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[(size_t)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[(size_t)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[(size_t)row * max_column_count]), &(array_out[(size_t)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(class 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; size_t 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->phases.size() + i); /* * Check for feasibility of charge balance with given uncertainties */ k = (int)row_mb; /* rows in A */ l = (int)(row_epsilon - row_mb); /* rows in C */ m = (int)(count_rows - row_epsilon); /* rows in E */ n = (int)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 *) &(my_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]), 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 (size_t j = row_isotopes; j < row_epsilon; j++) { memcpy((void *) &(array1[j * max_column_count]), (void *) &(inv_zero[0]), max_column_count * sizeof(LDBLE)); } /* * Zero out isotope uncertainties */ for (size_t j = row_isotope_epsilon; j < count_rows; j++) { memcpy((void *) &(array1[j * max_column_count]), (void *) &(inv_zero[0]), 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[0], &array1[0], &k, &l, &m, &n, bits, &delta2[0], &col_back[0], &row_back[0]); /* 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, (int)nklmd, (int)n2d, &array1[0], &kode, toler, &iter, &delta2[0], &inv_res[0], &error2, &inv_cu[0], &inv_iu[0], &inv_is[0], 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. * */ LDBLE sum; /* * Check equalities */ output_msg(sformatf( "\nPost_mortem examination of inverse modeling:\n\n")); for (size_t i = row_mb; i < row_epsilon; i++) { sum = 0; for (size_t j = 0; j < count_unknowns; j++) { sum += inv_delta1[j] * my_array[i * max_column_count + j]; } if (equal(sum, my_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 - my_array[(i * max_column_count) + count_unknowns]))); } } /* * Check inequalities */ for (size_t i = row_epsilon; i < count_rows; i++) { sum = 0; for (size_t j = 0; j < count_unknowns; j++) { sum += inv_delta1[j] * my_array[i * max_column_count + j]; } if (sum > my_array[(i * max_column_count) + count_unknowns] + toler) { output_msg(sformatf( "\tERROR: inequality not satisfied for %s, %e\n", row_name[i], (double) (sum - my_array[(i * max_column_count) + count_unknowns]))); } } /* * Check dissolution/precipitation constraints */ for (size_t 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; LDBLE sum; /* * Check equalities */ bool rv = true; if (debug_inverse) { output_msg(sformatf( "\nTesting cl1 inverse modeling:\n\n")); } for (size_t i = row_mb; i < row_epsilon; i++) { sum = 0; for (size_t j = 0; j < count_unknowns; j++) { sum += inv_delta1[j] * my_array[i * max_column_count + j]; } if (equal(sum, my_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 - my_array[(i * max_column_count) + count_unknowns]))); } rv = false; } } /* * Check inequalities */ for (size_t i = row_epsilon; i < count_rows; i++) { sum = 0; for (size_t j = 0; j < count_unknowns; j++) { sum += inv_delta1[j] * my_array[i * max_column_count + j]; } if (sum > my_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 - my_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(class 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.resize(inv_ptr->count_solns); inv_ptr->dalk_dc.resize(inv_ptr->count_solns); 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->elts.size(); 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(class 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(class 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; size_t column; LDBLE f; class 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++) { class 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) { my_array[(size_t)row * max_column_count + (size_t)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; class master *master_jit = master_bsearch(jit->second.Get_elt_name().c_str()); class 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->elts.size(); k++) { if (master_jit == inv_ptr->elts[k].master) break; } column = col_epsilon + (k * inv_ptr->count_solns) + i; my_array[(size_t)row * max_column_count + (size_t)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++) { class master *master_jit = master_bsearch(jit->second.Get_elt_name().c_str()); class 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->isotope_unknowns.size(); 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->isotope_unknowns.size()) + k; } } my_array[(size_t)row * max_column_count + (size_t)column] += f * jit->second.Get_total(); } } } /* * Fill in terms for each phase */ for (i = 0; i < inv_ptr->phases.size(); i++) { if (inv_ptr->phases[i].isotopes.size() == 0) continue; std::vector& isotope_ref = inv_ptr->phases[i].isotopes; for (j = 0; j < inv_ptr->phases[i].isotopes.size(); j++) { if (isotope_ref[j].primary == primary_ptr && isotope_ref[j].isotope_number == isotope_number) { /* term for alpha phase unknowns */ column = col_phases + i; my_array[(size_t)row * max_column_count + (size_t)column] = isotope_ref[j].ratio * isotope_ref[j].coef; /* term for phase isotope uncertainty unknown */ column = col_phase_isotopes + i * inv_ptr->isotopes.size() + (size_t)n; my_array[(size_t)row * max_column_count + column] = isotope_ref[j].coef; break; } } } return OK; } /* ---------------------------------------------------------------------- */ bool Phreeqc:: set_isotope_unknowns(class inverse* inv_ptr) /* ---------------------------------------------------------------------- */ { /* * 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; class master* primary_ptr; size_t count_isotopes; std::vector& isotopes = inv_ptr->isotope_unknowns; if (inv_ptr->isotopes.size() == 0) { isotopes.clear(); return true; } count_isotopes = 0; for (i = 0; i < inv_ptr->isotopes.size(); 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.resize(count_isotopes + 1); 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 < (int)master.size(); k++) { if (master[k] == primary_ptr) break; } /* sum all secondary for master */ k++; for (; k < (int)master.size(); k++) { isotopes.resize(count_isotopes + 1); 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++; } } } return true; } /* ---------------------------------------------------------------------- */ int Phreeqc:: check_isotopes(class 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; class master *master_ptr, *primary_ptr; cxxSolution *solution_ptr; class 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->isotopes.size(); 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++) { class 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++) { class master *master_kit = master_bsearch(kit->second.Get_elt_name().c_str()); class 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->i_u.size(); 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].uncertainties.size() && !isnan(inv_ptr->i_u[i].uncertainties[j])) #else if (j < inv_ptr->i_u[i].uncertainties.size() && 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].uncertainties.size() > 0 && !isnan(inv_ptr->i_u[i].uncertainties[inv_ptr->i_u[i].uncertainties.size() - 1])) #else else if (inv_ptr->i_u[i].uncertainties.size() > 0 && inv_ptr->i_u[i].uncertainties[(size_t)inv_ptr->i_u[i].uncertainties.size() - 1] != NAN) #endif { kit->second.Set_x_ratio_uncertainty(inv_ptr->i_u[i].uncertainties[inv_ptr->i_u[i].uncertainties.size() - 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->phases.size(); j++) { for (i = 0; i < inv_ptr->isotopes.size(); 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].isotopes.size(); 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(class inverse *inv_ptr) /* ---------------------------------------------------------------------- */ { size_t column; char token[MAX_LENGTH]; if (inv_ptr->isotopes.size() <= 0) return OK; for (size_t i = 0; i < inv_ptr->phases.size(); i++) { if (inv_ptr->phases[i].isotopes.size() == 0) continue; for (size_t j = 0; j < inv_ptr->phases[i].isotopes.size(); j++) { /* find index number */ size_t k = 0; for (k = 0; k < inv_ptr->isotopes.size(); 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->isotopes.size()) break; column = col_phase_isotopes + i * inv_ptr->isotopes.size() + k; /* * zero column if uncertainty is zero */ if (inv_ptr->phases[i].isotopes[j].ratio_uncertainty == 0) { for (k = 0; k < count_rows; k++) { my_array[(size_t)k * max_column_count + (size_t)column] = 0.0; } continue; } /* * optimization */ my_array[((size_t)column - (size_t)col_epsilon) * max_column_count + (size_t)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) { my_array[count_rows * max_column_count + (size_t)col_phases + (size_t)i] = inv_ptr->phases[i].isotopes[j].ratio_uncertainty; my_array[count_rows * max_column_count + (size_t)column] = 1.0; sprintf(token, "%s %s", inv_ptr->phases[i].phase->name, "iso pos"); row_name[count_rows] = string_hsave(token); count_rows++; my_array[count_rows * max_column_count + (size_t)col_phases + (size_t)i] = inv_ptr->phases[i].isotopes[j].ratio_uncertainty; my_array[count_rows * max_column_count + (size_t)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) { my_array[count_rows * max_column_count + (size_t)col_phases + (size_t)i] = -inv_ptr->phases[i].isotopes[j].ratio_uncertainty; my_array[count_rows * max_column_count + (size_t)column] = -1.0; sprintf(token, "%s %s", inv_ptr->phases[i].phase->name, "iso pos"); row_name[count_rows] = string_hsave(token); count_rows++; my_array[count_rows * max_column_count + (size_t)col_phases + (size_t)i] = -inv_ptr->phases[i].isotopes[j].ratio_uncertainty; my_array[count_rows * max_column_count + (size_t)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(class inverse *inv_ptr) /* ---------------------------------------------------------------------- */ { int i, j, row; char token[MAX_LENGTH]; row = 0; /* * epsilons for analytical data */ for (j = 0; j < inv_ptr->elts.size(); 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->isotope_unknowns.size(); 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->phases.size(); i++) { for (j = 0; j < inv_ptr->isotopes.size(); 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(class inverse *inverse_ptr) /* ---------------------------------------------------------------------- */ { std::string string; //const char* cptr; 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; if (it->second.Get_description().size() > 0) { string = it->second.Get_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(class inverse *inv_ptr) /* ---------------------------------------------------------------------- */ { /* * Prints model */ int i, j, k; cxxSolution *solution_ptr, *solution_ptr_orig; class master *master_ptr; LDBLE d1, d2, d3; LDBLE sum, sum1, sum_iso, d; std::vector array_save, l_delta_save; size_t count_unknowns_save, max_row_count_save, max_column_count_save, count_current_solutions; int temp, temp_punch; int solnmap[10][2]; FILE *model_file; const class elt_list *next_elt; int exch; size_t column; LDBLE f; class rxn_token *rxn_ptr; /* * print solution data, epsilons, and revised data */ if (inv_ptr->pat == NULL) return (OK); array_save = my_array; l_delta_save = delta; count_unknowns_save = count_unknowns; max_row_count_save = max_row_count; max_column_count_save = max_column_count; 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->elts.size(); 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->isotopes.size() > 0) { /* adjustments to solution isotope composition */ for (j = 0; j < inv_ptr->isotope_unknowns.size(); 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->isotope_unknowns.size() + 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 */ std::string string; if (solution_ptr_orig->Get_description().size() > 0) { 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()); } /* 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); } my_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 < (int)master.size(); j++) { master[j]->in = FALSE; } for (j = 0; j < inv_ptr->elts.size(); 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 < (int)master.size(); 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->isotopes.size(); 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 (size_t i = 0; i < inv_ptr->phases.size(); i++) { size_t 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[0]; 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[0]; 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[0] + 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); (void)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].isotopes.size(); k++) { std::vector& isotope_ref = inv_ptr->phases[i].isotopes; d1 = isotope_ref[k].ratio; for (j = 0; j < inv_ptr->isotopes.size(); j++) { if ((inv_ptr->isotopes[j].elt_name != isotope_ref[k].elt_name) || (inv_ptr->isotopes[j].isotope_number != isotope_ref[k].isotope_number)) continue; break; } d2 = 0.0; if (j < inv_ptr->isotopes.size()) { column = col_phase_isotopes + i * inv_ptr->isotopes.size() + 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_ref[k].isotope_number, isotope_ref[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->isotopes.size() > 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; } }