iphreeqc/phreeqcpp/inverse.cpp
Darth Vader 622bc5fb4c Squashed 'src/' changes from ac393756..d6a74675
d6a74675 Merge commit '4e80a54467a084df3b666c7d6fc56a4798fd3301'
4e80a544 Squashed 'phreeqcpp/' changes from 7c7fafd..c876219
0209fdf9 Merge commit 'b537589773f4819fe97ff8e5322bcd38c54b63f7'
b5375897 Squashed 'phreeqcpp/' changes from e317dd0..7c7fafd

git-subtree-dir: src
git-subtree-split: d6a74675d73985977ceac1601b57463c1ee8c331
2024-10-08 20:05:43 +00:00

5136 lines
137 KiB
C++

#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<std::string> 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)
{
Utilities::strcpy_safe(string, MAX_LENGTH, inverse[n].pat);
if (replace(".pat", ".pat", string) != TRUE)
{
Utilities::strcat_safe(string, MAX_LENGTH, ".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 structure "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;
snprintf(token, sizeof(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;
}
snprintf(token, sizeof(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++)
{
snprintf(token, sizeof(token), "ph %d", i);
col_name[column] = string_hsave(token);
column++;
}
/* put names in col_name for water */
snprintf(token, sizeof(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++)
{
snprintf(token, sizeof(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++)
{
snprintf(token, sizeof(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;
}
}
snprintf(token, sizeof(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];
}
snprintf(token, sizeof(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);
snprintf(token, sizeof(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;
snprintf(token, sizeof(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;
snprintf(token, sizeof(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;
snprintf(token, sizeof(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;
snprintf(token, sizeof(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;
snprintf(token, sizeof(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;
snprintf(token, sizeof(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;
snprintf(token, sizeof(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;
snprintf(token, sizeof(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;
snprintf(token, sizeof(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<class isotope>& 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;
snprintf(token, sizeof(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++)
{
snprintf(token, sizeof(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<class isotope>& 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<class isotope>& 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 */
if (j < inv_ptr->i_u[i].uncertainties.size()
&& !std::isnan(inv_ptr->i_u[i].uncertainties[j]))
{
kit->second.Set_x_ratio_uncertainty(inv_ptr->i_u[i].uncertainties[j]);
/* use solution-defined uncertainties second */
}
else if (inv_ptr->i_u[i].uncertainties.size() > 0
&& !std::isnan(inv_ptr->i_u[i].uncertainties[inv_ptr->i_u[i].uncertainties.size() - 1]))
{
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 */
}
else if (!std::isnan(kit->second.Get_ratio_uncertainty()))
{
kit->second.Set_x_ratio_uncertainty(
kit->second.Get_ratio_uncertainty());
/* use isotope defaults third */
}
else
{
snprintf(token, sizeof(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;
}
}
}
if (std::isnan(kit->second.Get_x_ratio_uncertainty()))
{
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;
snprintf(token, sizeof(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;
snprintf(token, sizeof(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;
snprintf(token, sizeof(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;
snprintf(token, sizeof(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++)
{
snprintf(token, sizeof(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++)
{
snprintf(token, sizeof(token), "%s %s %d", "optimize", "pH", inv_ptr->solns[i]);
row_name[row] = string_hsave(token);
row++;
}
}
/*
* water
*/
snprintf(token, sizeof(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++)
{
snprintf(token, sizeof(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++)
{
snprintf(token, sizeof(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<int, cxxSolution>::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<std::string, cxxSolutionIsotope>::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;
Utilities::strcpy_safe(elts[0], MAX_LENGTH, elt0);
Utilities::strcpy_safe(elts[1], MAX_LENGTH, elt1);
Utilities::strcpy_safe(elts[2], MAX_LENGTH, elt2);
Utilities::strcpy_safe(elts[3], MAX_LENGTH, elt3);
Utilities::strcpy_safe(elts[4], MAX_LENGTH, 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<double> 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<class isotope>& 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;
}
}