mirror of
https://git.gfz-potsdam.de/naaice/iphreeqc.git
synced 2025-12-15 16:18:22 +01:00
57b3a6cd Merge commit 'b34eedb91d08cd3b5fd34a70311fad8c14c7854d' b34eedb9 Squashed 'phreeqcpp/' changes from ccb9ba3..f5587da git-subtree-dir: src git-subtree-split: 57b3a6cd39393db8b91ec6b40325addb453d10e2
6268 lines
169 KiB
C++
6268 lines
169 KiB
C++
#include "Utils.h"
|
|
#include "Phreeqc.h"
|
|
#include "phqalloc.h"
|
|
#include <vector>
|
|
#include <assert.h>
|
|
#include "Exchange.h"
|
|
#include "GasPhase.h"
|
|
#include "PPassemblage.h"
|
|
#include "SSassemblage.h"
|
|
#include "SS.h"
|
|
#include "Solution.h"
|
|
#include "cxxKinetics.h"
|
|
|
|
#if defined(PHREEQCI_GUI)
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
#endif
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
prep(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Input is model defined by the structure use.
|
|
* Most of routine is skipped if model, as defined by master.total
|
|
* plus use.pure_phases, is same as previous calculation.
|
|
* Routine sets up class unknown for each unknown.
|
|
* Determines elements, species, and phases that are in the model.
|
|
* Calculates mass-action equations for each species and phase.
|
|
* Routine builds a set of lists for calculating mass balance and
|
|
* for building jacobian.
|
|
*/
|
|
cxxSolution *solution_ptr;
|
|
|
|
if (state >= REACTION)
|
|
{
|
|
same_model = check_same_model();
|
|
}
|
|
else
|
|
{
|
|
same_model = FALSE;
|
|
last_model.force_prep = true;
|
|
}
|
|
/*same_model = FALSE; */
|
|
/*
|
|
* Initialize s, master, and unknown pointers
|
|
*/
|
|
solution_ptr = use.Get_solution_ptr();
|
|
if (solution_ptr == NULL)
|
|
{
|
|
error_msg("Solution needed for calculation not found, stopping.",
|
|
STOP);
|
|
return ERROR;
|
|
}
|
|
description_x = solution_ptr->Get_description();
|
|
/*
|
|
* Allocate space for unknowns
|
|
* Must allocate all necessary space before pointers to
|
|
* X are set.
|
|
*/
|
|
if (same_model == FALSE || my_array.size() == 0)
|
|
{
|
|
clear();
|
|
setup_unknowns();
|
|
/*
|
|
* Set unknown pointers, unknown types, validity checks
|
|
*/
|
|
if (state == INITIAL_SOLUTION)
|
|
convert_units(solution_ptr);
|
|
setup_solution();
|
|
setup_exchange();
|
|
setup_surface();
|
|
setup_pure_phases();
|
|
setup_gas_phase();
|
|
setup_ss_assemblage();
|
|
setup_related_surface();
|
|
tidy_redox();
|
|
if (get_input_errors() > 0)
|
|
{
|
|
error_msg("Program terminating due to input errors.", STOP);
|
|
}
|
|
/*
|
|
* Allocate space for array
|
|
*/
|
|
my_array.resize((max_unknowns + 1) * max_unknowns);
|
|
delta.resize(max_unknowns);
|
|
residual.resize(max_unknowns);
|
|
for (int j = 0; j < max_unknowns; j++)
|
|
{
|
|
residual[j] = 0;
|
|
}
|
|
|
|
/*
|
|
* Build lists to fill Jacobian array and species list
|
|
*/
|
|
build_model();
|
|
adjust_setup_pure_phases();
|
|
adjust_setup_solution();
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If model is same, just update masses, don`t rebuild unknowns and lists
|
|
*/
|
|
quick_setup();
|
|
}
|
|
if (debug_mass_balance)
|
|
{
|
|
output_msg(sformatf("\nTotals for the equation solver.\n"));
|
|
output_msg(sformatf("\n\tRow\tName Type Total moles\n"));
|
|
for (int i = 0; i < count_unknowns; i++)
|
|
{
|
|
if (x[i]->type == PITZER_GAMMA)
|
|
continue;
|
|
output_msg(sformatf("\t%3d\t%-17s%2d %15.6e\n",
|
|
x[i]->number, x[i]->description, (int)x[i]->type, (double)x[i]->moles));
|
|
}
|
|
output_msg(sformatf("\n\n"));
|
|
}
|
|
if (get_input_errors() > 0)
|
|
{
|
|
error_msg("Program stopping due to input errors.", STOP);
|
|
}
|
|
if (sit_model) sit_make_lists();
|
|
if (pitzer_model)
|
|
pitzer_make_lists();
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
quick_setup(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Routine is used if model is the same as previous model
|
|
* Assumes moles of elements, exchangers, surfaces, gases, and solid solutions have
|
|
* been accumulated in array master, usually by subroutine step.
|
|
* Updates essential information for the model.
|
|
*/
|
|
int i;
|
|
for (i = 0; i < (int)master.size(); i++)
|
|
{
|
|
if (master[i]->s->type == SURF_PSI)
|
|
continue;
|
|
if (master[i]->s == s_eminus ||
|
|
master[i]->s == s_hplus ||
|
|
master[i]->s == s_h2o || master[i]->s == s_h2
|
|
|| master[i]->s == s_o2)
|
|
continue;
|
|
if (master[i]->total > 0)
|
|
{
|
|
if (master[i]->s->secondary != NULL)
|
|
{
|
|
master[i]->s->secondary->unknown->moles = master[i]->total;
|
|
}
|
|
else
|
|
{
|
|
master[i]->unknown->moles = master[i]->total;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Reaction: pH for charge balance
|
|
*/
|
|
ph_unknown->moles = use.Get_solution_ptr()->Get_cb();
|
|
/*
|
|
* Reaction: pe for total hydrogen
|
|
*/
|
|
if (mass_hydrogen_unknown != NULL)
|
|
{
|
|
/* Use H - 2O linear combination in place of H */
|
|
#define COMBINE
|
|
/*#define COMBINE_CHARGE */
|
|
#ifdef COMBINE
|
|
mass_hydrogen_unknown->moles =
|
|
use.Get_solution_ptr()->Get_total_h() - 2 * use.Get_solution_ptr()->Get_total_o();
|
|
#else
|
|
mass_hydrogen_unknown->moles = use.Get_solution_ptr()->total_h;
|
|
#endif
|
|
}
|
|
/*
|
|
* Reaction H2O for total oxygen
|
|
*/
|
|
if (mass_oxygen_unknown != NULL)
|
|
{
|
|
mass_oxygen_unknown->moles = use.Get_solution_ptr()->Get_total_o();
|
|
}
|
|
|
|
/*
|
|
* pp_assemblage
|
|
*/
|
|
for (i = 0; i < count_unknowns; i++)
|
|
{
|
|
if (x[i]->type == PP)
|
|
{
|
|
cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr();
|
|
std::map<std::string, cxxPPassemblageComp>::iterator it;
|
|
//it = pp_assemblage_ptr->Get_pp_assemblage_comps().find(x[i]->pp_assemblage_comp_name);
|
|
cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name);
|
|
assert(comp_ptr != NULL);
|
|
//assert(it != pp_assemblage_ptr->Get_pp_assemblage_comps().end());
|
|
//cxxPPassemblageComp * comp_ptr = &(it->second);
|
|
x[i]->pp_assemblage_comp_ptr = comp_ptr;
|
|
x[i]->moles = comp_ptr->Get_moles();
|
|
/* A. Crapsi */
|
|
x[i]->si = comp_ptr->Get_si();
|
|
x[i]->delta = comp_ptr->Get_delta();
|
|
/* End A. Crapsi */
|
|
x[i]->dissolve_only = comp_ptr->Get_dissolve_only() ? TRUE : FALSE;
|
|
comp_ptr->Set_delta(0.0);
|
|
}
|
|
}
|
|
// Need to update SIs for gases
|
|
adjust_setup_pure_phases();
|
|
|
|
/*
|
|
* gas phase
|
|
*/
|
|
if (gas_unknown != NULL)
|
|
{
|
|
cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr();
|
|
if ((gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME) &&
|
|
numerical_fixed_volume &&
|
|
(gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume))
|
|
{
|
|
for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++)
|
|
{
|
|
cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]);
|
|
gas_unknowns[i]->moles = gc_ptr->Get_moles();
|
|
if (gas_unknowns[i]->moles <= 0)
|
|
gas_unknowns[i]->moles = MIN_TOTAL;
|
|
gas_unknowns[i]->phase->pr_in = false;
|
|
gas_unknowns[i]->phase->pr_phi = 1.0;
|
|
gas_unknowns[i]->phase->pr_p = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gas_unknown->moles = 0.0;
|
|
for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++)
|
|
{
|
|
cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]);
|
|
gas_unknown->moles += gc_ptr->Get_moles();
|
|
}
|
|
if (gas_unknown->moles <= 0)
|
|
gas_unknown->moles = MIN_TOTAL;
|
|
gas_unknown->ln_moles = log(gas_unknown->moles);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* ss_assemblage
|
|
*/
|
|
if (ss_unknown != NULL)
|
|
{
|
|
for (i = 0; i < count_unknowns; i++)
|
|
{
|
|
if (x[i]->type == SS_MOLES)
|
|
break;
|
|
}
|
|
|
|
std::vector<cxxSS *> ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize();
|
|
for (size_t j = 0; j < ss_ptrs.size(); j++)
|
|
{
|
|
for (size_t k = 0; k < ss_ptrs[j]->Get_ss_comps().size(); k++)
|
|
{
|
|
x[i]->ss_ptr = ss_ptrs[j];
|
|
cxxSScomp *comp_ptr = &(ss_ptrs[j]->Get_ss_comps()[k]);
|
|
x[i]->ss_comp_ptr = comp_ptr;
|
|
x[i]->moles = comp_ptr->Get_moles();
|
|
if (x[i]->moles <= 0)
|
|
{
|
|
x[i]->moles = MIN_TOTAL_SS;
|
|
comp_ptr->Set_moles(MIN_TOTAL_SS);
|
|
}
|
|
comp_ptr->Set_initial_moles(x[i]->moles);
|
|
x[i]->ln_moles = log(x[i]->moles);
|
|
|
|
x[i]->phase->dn = comp_ptr->Get_dn();
|
|
x[i]->phase->dnb = comp_ptr->Get_dnb();
|
|
x[i]->phase->dnc = comp_ptr->Get_dnc();
|
|
x[i]->phase->log10_fraction_x = comp_ptr->Get_log10_fraction_x();
|
|
x[i]->phase->log10_lambda = comp_ptr->Get_log10_lambda();
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* exchange
|
|
*/
|
|
// number of moles is set from master->moles above
|
|
/*
|
|
* surface
|
|
*/
|
|
if (use.Get_surface_ptr() != NULL)
|
|
{
|
|
for (i = 0; i < count_unknowns; i++)
|
|
{
|
|
if (x[i]->type == SURFACE)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
for (; i < count_unknowns; i++)
|
|
{
|
|
if (x[i]->type == SURFACE_CB)
|
|
{
|
|
cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge);
|
|
x[i]->related_moles = charge_ptr->Get_grams();
|
|
x[i]->mass_water = charge_ptr->Get_mass_water();
|
|
/* moles picked up from master->total */
|
|
}
|
|
else if (x[i]->type == SURFACE_CB1 || x[i]->type == SURFACE_CB2)
|
|
{
|
|
cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge);
|
|
x[i]->related_moles = charge_ptr->Get_grams();
|
|
x[i]->mass_water = charge_ptr->Get_mass_water();
|
|
}
|
|
else if (x[i]->type == SURFACE)
|
|
{
|
|
/* moles picked up from master->total
|
|
except for surfaces related to kinetic minerals ... */
|
|
cxxSurfaceComp *comp_ptr = use.Get_surface_ptr()->Find_comp(x[i]->surface_comp);
|
|
if (comp_ptr->Get_rate_name().size() > 0)
|
|
{
|
|
cxxNameDouble::iterator lit;
|
|
for (lit = comp_ptr->Get_totals().begin(); lit != comp_ptr->Get_totals().end(); lit++)
|
|
{
|
|
class element *elt_ptr = element_store(lit->first.c_str());
|
|
class master *master_ptr = elt_ptr->master;
|
|
if (master_ptr->type != SURF)
|
|
continue;
|
|
if (strcmp_nocase(x[i]->description, lit->first.c_str()) == 0)
|
|
{
|
|
x[i]->moles = lit->second;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
save_model();
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
build_gas_phase(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Put coefficients into lists to sum iaps to test for equilibrium
|
|
* Put coefficients into lists to build jacobian for
|
|
* sum of partial pressures equation and
|
|
* mass balance equations for elements contained in gases
|
|
*/
|
|
size_t row, col;
|
|
class master *master_ptr;
|
|
class rxn_token *rxn_ptr;
|
|
class unknown *unknown_ptr;
|
|
LDBLE coef, coef_elt;
|
|
|
|
if (gas_unknown == NULL)
|
|
return (OK);
|
|
cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr();
|
|
if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME &&
|
|
(gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume) &&
|
|
numerical_fixed_volume)
|
|
{
|
|
return build_fixed_volume_gas();
|
|
}
|
|
for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++)
|
|
{
|
|
cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]);
|
|
int k;
|
|
class phase *phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str() , &k, FALSE);
|
|
assert(phase_ptr);
|
|
/*
|
|
* Determine elements in gas component
|
|
*/
|
|
count_elts = 0;
|
|
paren_count = 0;
|
|
if (phase_ptr->rxn_x.token.size() == 0)
|
|
continue;
|
|
add_elt_list(phase_ptr->next_elt, 1.0);
|
|
#ifdef COMBINE
|
|
change_hydrogen_in_elt_list(0);
|
|
#endif
|
|
/*
|
|
* Build mass balance sums for each element in gas
|
|
*/
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf( "\n\tMass balance summations. %s.\n",
|
|
phase_ptr->name));
|
|
}
|
|
|
|
/* All elements in gas */
|
|
for (int j = 0; j < count_elts; j++)
|
|
{
|
|
unknown_ptr = NULL;
|
|
if (strcmp(elt_list[j].elt->name, "H") == 0)
|
|
{
|
|
unknown_ptr = mass_hydrogen_unknown;
|
|
}
|
|
else if (strcmp(elt_list[j].elt->name, "O") == 0)
|
|
{
|
|
unknown_ptr = mass_oxygen_unknown;
|
|
}
|
|
else
|
|
{
|
|
if (elt_list[j].elt->primary->in == TRUE)
|
|
{
|
|
unknown_ptr = elt_list[j].elt->primary->unknown;
|
|
}
|
|
else if (elt_list[j].elt->primary->s->secondary != NULL)
|
|
{
|
|
unknown_ptr =
|
|
elt_list[j].elt->primary->s->secondary->unknown;
|
|
}
|
|
}
|
|
if (unknown_ptr != NULL)
|
|
{
|
|
coef = elt_list[j].coef;
|
|
store_mb(&(phase_ptr->moles_x), &(unknown_ptr->f), coef);
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf( "\t\t%-24s%10.3f\n",
|
|
unknown_ptr->description, (double) coef));
|
|
}
|
|
}
|
|
}
|
|
if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE)
|
|
{
|
|
/* Total pressure of gases */
|
|
store_mb(&(phase_ptr->p_soln_x), &(gas_unknown->f), 1.0);
|
|
}
|
|
/*
|
|
* Build jacobian sums for mass balance equations
|
|
*/
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf( "\n\tJacobian summations %s.\n\n",
|
|
phase_ptr->name));
|
|
}
|
|
for (int j = 0; j < count_elts; j++)
|
|
{
|
|
unknown_ptr = NULL;
|
|
if (strcmp(elt_list[j].elt->name, "H") == 0)
|
|
{
|
|
unknown_ptr = mass_hydrogen_unknown;
|
|
}
|
|
else if (strcmp(elt_list[j].elt->name, "O") == 0)
|
|
{
|
|
unknown_ptr = mass_oxygen_unknown;
|
|
}
|
|
else
|
|
{
|
|
if (elt_list[j].elt->primary->in == TRUE)
|
|
{
|
|
unknown_ptr = elt_list[j].elt->primary->unknown;
|
|
}
|
|
else if (elt_list[j].elt->primary->s->secondary != NULL)
|
|
{
|
|
unknown_ptr =
|
|
elt_list[j].elt->primary->s->secondary->unknown;
|
|
}
|
|
}
|
|
if (unknown_ptr == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf( "\n\t%s.\n",
|
|
unknown_ptr->description));
|
|
}
|
|
row = unknown_ptr->number * (count_unknowns + 1);
|
|
coef_elt = elt_list[j].coef;
|
|
for (rxn_ptr = &phase_ptr->rxn_x.token[0] + 1;
|
|
rxn_ptr->s != NULL; rxn_ptr++)
|
|
{
|
|
|
|
if (rxn_ptr->s->secondary != NULL
|
|
&& rxn_ptr->s->secondary->in == TRUE)
|
|
{
|
|
master_ptr = rxn_ptr->s->secondary;
|
|
}
|
|
else if (rxn_ptr->s->primary != NULL && rxn_ptr->s->primary->in == TRUE)
|
|
{
|
|
master_ptr = rxn_ptr->s->primary;
|
|
}
|
|
else
|
|
{
|
|
master_ptr = master_bsearch_primary(rxn_ptr->s->name);
|
|
master_ptr->s->la = -999.0;
|
|
}
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf( "\t\t%s\n",
|
|
master_ptr->s->name));
|
|
}
|
|
if (master_ptr->unknown == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
if (master_ptr->in == FALSE)
|
|
{
|
|
error_string = sformatf(
|
|
"Element, %s, in phase, %s, is not in model.",
|
|
master_ptr->elt->name, phase_ptr->name);
|
|
error_msg(error_string, CONTINUE);
|
|
input_error++;
|
|
}
|
|
col = master_ptr->unknown->number;
|
|
coef = coef_elt * rxn_ptr->coef;
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d",
|
|
master_ptr->s->name, (double) coef,
|
|
row / (count_unknowns + 1), col));
|
|
}
|
|
store_jacob(&(phase_ptr->moles_x),
|
|
&(my_array[(size_t)row + (size_t)col]), coef);
|
|
}
|
|
if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE)
|
|
{
|
|
/* derivative wrt total moles of gas */
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d",
|
|
"gas moles", (double) elt_list[j].coef,
|
|
row / (count_unknowns + 1),
|
|
gas_unknown->number));
|
|
}
|
|
store_jacob(&(phase_ptr->fraction_x),
|
|
&(my_array[(size_t)row + (size_t)gas_unknown->number]), coef_elt);
|
|
}
|
|
}
|
|
/*
|
|
* Build jacobian sums for sum of partial pressures equation
|
|
*/
|
|
if (gas_phase_ptr->Get_type() != cxxGasPhase::GP_PRESSURE)
|
|
continue;
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf( "\n\tPartial pressure eqn %s.\n\n",
|
|
phase_ptr->name));
|
|
}
|
|
unknown_ptr = gas_unknown;
|
|
row = unknown_ptr->number * (count_unknowns + 1);
|
|
for (rxn_ptr = &phase_ptr->rxn_x.token[0] + 1; rxn_ptr->s != NULL; rxn_ptr++)
|
|
{
|
|
if (rxn_ptr->s != s_eminus && rxn_ptr->s->in == FALSE)
|
|
{
|
|
error_string = sformatf(
|
|
"Element in species, %s, in phase, %s, is not in model.",
|
|
rxn_ptr->s->name, phase_ptr->name);
|
|
warning_msg(error_string);
|
|
}
|
|
else
|
|
{
|
|
if (rxn_ptr->s->secondary != NULL
|
|
&& rxn_ptr->s->secondary->in == TRUE)
|
|
{
|
|
master_ptr = rxn_ptr->s->secondary;
|
|
}
|
|
else if (rxn_ptr->s->primary != NULL && rxn_ptr->s->primary->in == TRUE)
|
|
{
|
|
master_ptr = rxn_ptr->s->primary;
|
|
}
|
|
else
|
|
{
|
|
master_ptr = master_bsearch_primary(rxn_ptr->s->name);
|
|
if (master_ptr && master_ptr->s)
|
|
{
|
|
master_ptr->s->la = -999.0;
|
|
}
|
|
}
|
|
if (master_ptr == NULL)
|
|
{
|
|
error_string = sformatf(
|
|
"Master species for %s, in phase, %s, is not in model.",
|
|
rxn_ptr->s->name, phase_ptr->name);
|
|
error_msg(error_string, CONTINUE);
|
|
input_error++;
|
|
}
|
|
else
|
|
{
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf( "\t\t%s\n", master_ptr->s->name));
|
|
}
|
|
if (master_ptr->unknown == NULL)
|
|
{
|
|
assert(false);
|
|
continue;
|
|
}
|
|
if (master_ptr->in == FALSE)
|
|
{
|
|
error_string = sformatf(
|
|
"Element, %s, in phase, %s, is not in model.",
|
|
master_ptr->elt->name, phase_ptr->name);
|
|
warning_msg(error_string);
|
|
}
|
|
col = master_ptr->unknown->number;
|
|
coef = rxn_ptr->coef;
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d",
|
|
master_ptr->s->name, (double) coef,
|
|
row / (count_unknowns + 1), col));
|
|
}
|
|
store_jacob(&(phase_ptr->p_soln_x), &(my_array[(size_t)row + (size_t)col]), coef);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
build_ss_assemblage(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Put coefficients into lists to sum iaps to test for equilibrium
|
|
* Put coefficients into lists to build jacobian for
|
|
* mass action equation for component
|
|
* mass balance equations for elements contained in solid solutions
|
|
*/
|
|
bool stop;
|
|
size_t row, col;
|
|
class master *master_ptr;
|
|
class rxn_token *rxn_ptr;
|
|
const char* cptr;
|
|
|
|
if (ss_unknown == NULL)
|
|
return (OK);
|
|
cxxSS * ss_ptr_old = NULL;
|
|
col = 0;
|
|
for (int i = 0; i < count_unknowns; i++)
|
|
{
|
|
if (x[i]->type != SS_MOLES)
|
|
continue;
|
|
//cxxSS *ss_ptr = use.Get_ss_assemblage_ptr()->Find(x[i]->ss_name);
|
|
cxxSS *ss_ptr = (cxxSS *) x[i]->ss_ptr;
|
|
assert(ss_ptr);
|
|
if (ss_ptr != ss_ptr_old)
|
|
{
|
|
col = x[i]->number;
|
|
ss_ptr_old = ss_ptr;
|
|
}
|
|
/*
|
|
* Calculate function value (inverse saturation index)
|
|
*/
|
|
if (x[i]->phase->rxn_x.token.size() == 0)
|
|
continue;
|
|
store_mb(&(x[i]->phase->lk), &(x[i]->f), 1.0);
|
|
for (rxn_ptr = &x[i]->phase->rxn_x.token[0] + 1; rxn_ptr->s != NULL;
|
|
rxn_ptr++)
|
|
{
|
|
store_mb(&(rxn_ptr->s->la), &(x[i]->f), -rxn_ptr->coef);
|
|
}
|
|
/* include mole fraction */
|
|
store_mb(&(x[i]->phase->log10_fraction_x), &(x[i]->f), 1.0);
|
|
|
|
/* include activity coefficient */
|
|
store_mb(&(x[i]->phase->log10_lambda), &(x[i]->f), 1.0);
|
|
/*
|
|
* Put coefficients into mass action equations
|
|
*/
|
|
/* first IAP terms */
|
|
for (rxn_ptr = &x[i]->phase->rxn_x.token[0] + 1; rxn_ptr->s != NULL;
|
|
rxn_ptr++)
|
|
{
|
|
if (rxn_ptr->s->secondary != NULL
|
|
&& rxn_ptr->s->secondary->in == TRUE)
|
|
{
|
|
master_ptr = rxn_ptr->s->secondary;
|
|
}
|
|
else
|
|
{
|
|
master_ptr = rxn_ptr->s->primary;
|
|
}
|
|
if (master_ptr == NULL || master_ptr->unknown == NULL)
|
|
continue;
|
|
store_jacob0((int)x[i]->number, (int)master_ptr->unknown->number,
|
|
rxn_ptr->coef);
|
|
}
|
|
|
|
if (ss_ptr->Get_a0() != 0.0 || ss_ptr->Get_a1() != 0.0)
|
|
{
|
|
/*
|
|
* For binary solid solution
|
|
*/
|
|
/* next dnc terms */
|
|
row = x[i]->number * (count_unknowns + 1);
|
|
if (x[i]->ss_comp_number == 0)
|
|
{
|
|
col = x[i]->number;
|
|
}
|
|
else
|
|
{
|
|
col = x[i]->number - 1;
|
|
}
|
|
store_jacob(&(x[i]->phase->dnc), &(my_array[(size_t)row + (size_t)col]), -1);
|
|
|
|
/* next dnb terms */
|
|
col++;
|
|
store_jacob(&(x[i]->phase->dnb), &(my_array[(size_t)row + (size_t)col]), -1);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* For ideal solid solution
|
|
*/
|
|
row = x[i]->number * (count_unknowns + 1);
|
|
for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++)
|
|
{
|
|
if ((int) j != x[i]->ss_comp_number)
|
|
{
|
|
/* store_jacob (&(s_s_ptr->dn), &(array[row + col + j]), -1.0); */
|
|
store_jacob(&(x[i]->phase->dn), &(my_array[(size_t)row + (size_t)col + (size_t)j]),
|
|
-1.0);
|
|
}
|
|
else
|
|
{
|
|
store_jacob(&(x[i]->phase->dnb), &(my_array[(size_t)row + (size_t)col + (size_t)j]),
|
|
-1.0);
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Put coefficients into mass balance equations
|
|
*/
|
|
count_elts = 0;
|
|
paren_count = 0;
|
|
cptr = x[i]->phase->formula;
|
|
get_elts_in_species(&cptr, 1.0);
|
|
/*
|
|
* Go through elements in phase
|
|
*/
|
|
#ifdef COMBINE
|
|
change_hydrogen_in_elt_list(0);
|
|
#endif
|
|
for (int j = 0; j < count_elts; j++)
|
|
{
|
|
|
|
if (strcmp(elt_list[j].elt->name, "H") == 0
|
|
&& mass_hydrogen_unknown != NULL)
|
|
{
|
|
store_jacob0((int)mass_hydrogen_unknown->number, (int)x[i]->number,
|
|
-elt_list[j].coef);
|
|
store_sum_deltas(&(delta[i]), &mass_hydrogen_unknown->delta,
|
|
elt_list[j].coef);
|
|
|
|
}
|
|
else if (strcmp(elt_list[j].elt->name, "O") == 0
|
|
&& mass_oxygen_unknown != NULL)
|
|
{
|
|
store_jacob0((int)mass_oxygen_unknown->number, (int)x[i]->number,
|
|
-elt_list[j].coef);
|
|
store_sum_deltas(&(delta[i]), &mass_oxygen_unknown->delta,
|
|
elt_list[j].coef);
|
|
|
|
}
|
|
else
|
|
{
|
|
master_ptr = elt_list[j].elt->primary;
|
|
if (master_ptr->in == FALSE)
|
|
{
|
|
master_ptr = master_ptr->s->secondary;
|
|
}
|
|
if (master_ptr == NULL || master_ptr->in == FALSE)
|
|
{
|
|
if (state != ADVECTION && state != TRANSPORT
|
|
&& state != PHAST)
|
|
{
|
|
error_string = sformatf(
|
|
"Element in phase, %s, is not in model.",
|
|
x[i]->phase->name);
|
|
warning_msg(error_string);
|
|
}
|
|
if (master_ptr != NULL)
|
|
{
|
|
master_ptr->s->la = -999.9;
|
|
}
|
|
/*
|
|
* Master species is in model
|
|
*/
|
|
}
|
|
else if (master_ptr->in == TRUE)
|
|
{
|
|
store_jacob0((int)master_ptr->unknown->number, (int)x[i]->number,
|
|
-elt_list[j].coef);
|
|
store_sum_deltas(&delta[i], &master_ptr->unknown->delta,
|
|
elt_list[j].coef);
|
|
/*
|
|
* Master species in equation needs to be rewritten
|
|
*/
|
|
}
|
|
else if (master_ptr->in == REWRITE)
|
|
{
|
|
stop = FALSE;
|
|
for (int k = 0; k < count_unknowns; k++)
|
|
{
|
|
if (x[k]->type != MB)
|
|
continue;
|
|
for (size_t l = 0; l < x[k]->master.size(); l++)
|
|
{
|
|
if (x[k]->master[l] == master_ptr)
|
|
{
|
|
store_jacob0((int)x[k]->master[0]->unknown->number,
|
|
(int)x[i]->number, -elt_list[j].coef);
|
|
store_sum_deltas(&delta[i],
|
|
&x[k]->master[0]->unknown->
|
|
delta, elt_list[j].coef);
|
|
stop = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (stop == TRUE)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
build_jacobian_sums(int k)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Function builds lists sum_jacob1 and sum_jacob2 that describe how to sum molalities
|
|
* to form jacobian.
|
|
*/
|
|
int i, j, kk;
|
|
int count_g;
|
|
LDBLE coef;
|
|
LDBLE *source, *target;
|
|
|
|
if (debug_prep == TRUE)
|
|
output_msg(sformatf( "\n\tJacobian summations.\n"));
|
|
/*
|
|
* Calculate jacobian coefficients for each mass balance equation
|
|
*/
|
|
for (i = 0; i < (int)mb_unknowns.size(); i++)
|
|
{
|
|
/*
|
|
* Store d(moles) for a mass balance equation
|
|
*/
|
|
/* initial solution only */
|
|
if (mb_unknowns[i].unknown->type == SOLUTION_PHASE_BOUNDARY)
|
|
{
|
|
continue;
|
|
}
|
|
coef = mb_unknowns[i].coef;
|
|
if (debug_prep == TRUE)
|
|
output_msg(sformatf("\n\tMass balance eq: %-13s\t%f\trow\tcol\n",
|
|
mb_unknowns[i].unknown->description, (double)coef));
|
|
store_dn(k, mb_unknowns[i].source, (int)mb_unknowns[i].unknown->number,
|
|
coef, mb_unknowns[i].gamma_source);
|
|
/*
|
|
* Add extra terms for change in dg/dx in diffuse layer model
|
|
*/
|
|
if (s[k]->type >= H2O || dl_type_x == cxxSurface::NO_DL)
|
|
{
|
|
continue;
|
|
}
|
|
else if ((mb_unknowns[i].unknown->type == MB ||
|
|
mb_unknowns[i].unknown->type == MH ||
|
|
mb_unknowns[i].unknown->type == MH2O) && state >= REACTION)
|
|
{
|
|
if (mass_oxygen_unknown != NULL)
|
|
{
|
|
/* term for water, sum of all surfaces */
|
|
source = &s[k]->tot_dh2o_moles;
|
|
target = &(my_array[(size_t)mb_unknowns[i].unknown->number *
|
|
(count_unknowns + 1) + (size_t)mass_oxygen_unknown->number]);
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d",
|
|
"sum[dn(i,s)/dlnwater]", (double) coef,
|
|
mb_unknowns[i].unknown->number,
|
|
mass_oxygen_unknown->number));
|
|
}
|
|
store_jacob(source, target, coef);
|
|
}
|
|
|
|
/* terms for psi, one for each surface */
|
|
count_g = 0;
|
|
for (j = 0; j < count_unknowns; j++)
|
|
{
|
|
if (x[j]->type != SURFACE_CB)
|
|
continue;
|
|
cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge);
|
|
source = s_diff_layer[k][charge_ptr->Get_name()].Get_dx_moles_address();
|
|
target = &(my_array[(size_t)mb_unknowns[i].unknown->number *
|
|
(count_unknowns + 1) + (size_t)x[j]->number]);
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d",
|
|
"dg/dlny", (double) coef,
|
|
mb_unknowns[i].unknown->number, x[j]->number));
|
|
}
|
|
store_jacob(source, target, coef);
|
|
count_g++;
|
|
if (count_g >= (int) use.Get_surface_ptr()->Get_surface_charges().size())
|
|
break;
|
|
}
|
|
|
|
/* terms for related phases */
|
|
count_g = 0;
|
|
for (j = 0; j < count_unknowns; j++)
|
|
{
|
|
if (x[j]->type != SURFACE_CB)
|
|
continue;
|
|
cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge);
|
|
/* has related phase */
|
|
cxxSurfaceComp *comp_ptr = use.Get_surface_ptr()->Find_comp(x[(size_t)j - 1]->surface_comp);
|
|
if (comp_ptr->Get_phase_name().size() == 0)
|
|
continue;
|
|
|
|
/* now find the related phase */
|
|
for (kk = (int)count_unknowns - 1; kk >= 0; kk--)
|
|
{
|
|
if (x[kk]->type != PP)
|
|
continue;
|
|
//if (x[kk]->phase->name == string_hsave(comp_ptr->Get_phase_name().c_str()))
|
|
if (strcmp_nocase(x[kk]->phase->name, comp_ptr->Get_phase_name().c_str()) == 0)
|
|
break;
|
|
}
|
|
|
|
if (kk >= 0)
|
|
{
|
|
source = s_diff_layer[k][charge_ptr->Get_name()].Get_drelated_moles_address();
|
|
target = &(my_array[(size_t)mb_unknowns[i].unknown->number *
|
|
(count_unknowns + 1) + (size_t)x[kk]->number]);
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf(
|
|
"\t\t%-24s%10.3f\t%d\t%d", "dphase",
|
|
(double) coef,
|
|
mb_unknowns[i].unknown->number,
|
|
x[kk]->number));
|
|
}
|
|
store_jacob(source, target, coef);
|
|
}
|
|
count_g++;
|
|
if (count_g >= (int) use.Get_surface_ptr()->Get_surface_charges().size())
|
|
break;
|
|
}
|
|
|
|
}
|
|
else if (mb_unknowns[i].unknown->type == SURFACE_CB)
|
|
{
|
|
count_g = 0;
|
|
for (j = 0; j < count_unknowns; j++)
|
|
{
|
|
if (x[j]->type != SURFACE_CB)
|
|
continue;
|
|
cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge);
|
|
if (mb_unknowns[i].unknown->number == x[j]->number)
|
|
{
|
|
source = s_diff_layer[k][charge_ptr->Get_name()].Get_dx_moles_address();
|
|
target = &(my_array[(size_t)mb_unknowns[i].unknown->number *
|
|
(count_unknowns + 1) + (size_t)x[j]->number]);
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf("\t\t%-24s%10.3f\t%d\t%d", "dg/dlny",
|
|
(double)coef,
|
|
mb_unknowns[i].unknown->number,
|
|
x[j]->number));
|
|
}
|
|
store_jacob(source, target, coef);
|
|
|
|
/* term for related phase */
|
|
/* has related phase */
|
|
cxxSurfaceComp *comp_ptr = use.Get_surface_ptr()->Find_comp(x[(size_t)j - 1]->surface_comp);
|
|
if (comp_ptr->Get_phase_name().size() > 0)
|
|
{
|
|
/* now find the related phase */
|
|
for (kk = (int)count_unknowns - 1; kk >= 0; kk--)
|
|
{
|
|
if (x[kk]->type != PP)
|
|
continue;
|
|
//if (x[kk]->phase->name == string_hsave(comp_ptr->Get_phase_name().c_str()))
|
|
if (strcmp_nocase(x[kk]->phase->name, comp_ptr->Get_phase_name().c_str()) == 0)
|
|
break;
|
|
}
|
|
if (kk >= 0)
|
|
{
|
|
source = s_diff_layer[k][charge_ptr->Get_name()].Get_drelated_moles_address();
|
|
target = &(my_array[(size_t)(size_t)mb_unknowns[i].unknown->number *
|
|
(count_unknowns + 1) + (size_t)x[kk]->number]);
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf(
|
|
"\t\t%-24s%10.3f\t%d\t%d",
|
|
"dphase", (double) coef,
|
|
mb_unknowns[i].unknown->number,
|
|
x[kk]->number));
|
|
}
|
|
store_jacob(source, target, coef);
|
|
}
|
|
}
|
|
|
|
if (mass_oxygen_unknown != NULL)
|
|
{
|
|
/* term for water, for same surfaces */
|
|
source = s_diff_layer[k][charge_ptr->Get_name()].Get_dh2o_moles_address();
|
|
target = &(my_array[(size_t)mb_unknowns[i].unknown->number *
|
|
(count_unknowns + 1) +
|
|
(size_t)mass_oxygen_unknown->number]);
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf(
|
|
"\t\t%-24s%10.3f\t%d\t%d",
|
|
"dn(i,s)/dlnwater", (double) coef,
|
|
mb_unknowns[i].unknown->number,
|
|
mass_oxygen_unknown->number));
|
|
}
|
|
store_jacob(source, target, coef);
|
|
}
|
|
break;
|
|
}
|
|
count_g++;
|
|
if (count_g >= (int) use.Get_surface_ptr()->Get_surface_charges().size())
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
build_mb_sums(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Function builds lists sum_mb1 and sum_mb2 that describe how to sum molalities
|
|
* to calculate mass balance sums, including activity of water, ionic strength,
|
|
* charge balance, and alkalinity.
|
|
*/
|
|
int i;
|
|
LDBLE *target;
|
|
/*
|
|
* Make space for lists
|
|
*/
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf( "\n\tMass balance summations.\n"));
|
|
}
|
|
for (i = 0; i < (int)mb_unknowns.size(); i++)
|
|
{
|
|
target = &(mb_unknowns[i].unknown->f);
|
|
store_mb(mb_unknowns[i].source, target, mb_unknowns[i].coef);
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf( "\t\t%-24s%10.3f\n",
|
|
mb_unknowns[i].unknown->description,
|
|
(double) mb_unknowns[i].coef));
|
|
}
|
|
}
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
build_model(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Guts of prep. Determines species in model, rewrites equations,
|
|
* builds lists for mass balance and jacobian sums.
|
|
*/
|
|
int i, j;
|
|
LDBLE coef_e;
|
|
|
|
if (s_hplus == NULL || s_eminus == NULL || s_h2o == NULL)
|
|
{
|
|
error_msg("Data base is missing H+, H2O, or e- species.", CONTINUE);
|
|
input_error++;
|
|
}
|
|
/*
|
|
* Make space for lists of pointers to species in the model
|
|
*/
|
|
// clear sum_species_map, which is built from s_x
|
|
sum_species_map_db.clear();
|
|
sum_species_map.clear();
|
|
s_x.clear();
|
|
sum_mb1.clear();
|
|
sum_mb2.clear();
|
|
sum_jacob0.clear();
|
|
sum_jacob1.clear();
|
|
sum_jacob2.clear();
|
|
sum_delta.clear();
|
|
species_list.clear();
|
|
/*
|
|
* Pick species in the model, determine reaction for model, build jacobian
|
|
*/
|
|
s_x.clear();
|
|
compute_gfw("H2O", &gfw_water);
|
|
gfw_water *= 0.001;
|
|
for (i = 0; i < (int)s.size(); i++)
|
|
{
|
|
if (s[i]->type > H2O && s[i]->type != EX && s[i]->type != SURF)
|
|
continue;
|
|
s[i]->in = FALSE;
|
|
count_trxn = 0;
|
|
trxn_add(s[i]->rxn_s, 1.0, false); /* rxn_s is set in tidy_model */
|
|
/*
|
|
* Check if species is in model
|
|
*/
|
|
s[i]->in = inout();
|
|
if (s[i]->in == TRUE)
|
|
{
|
|
/* for isotopes, activity of water is for 1H and 16O */
|
|
if (s[i]->gflag == 9)
|
|
{
|
|
gfw_water = 18.0 / 1000.0;
|
|
}
|
|
if (pitzer_model == FALSE && sit_model == FALSE)
|
|
s[i]->lg = 0.0;
|
|
compute_gfw(s[i]->name, &s[i]->gfw);
|
|
size_t count_s_x = s_x.size();
|
|
s_x.resize(count_s_x + 1);
|
|
s_x[count_s_x] = s[i];
|
|
|
|
/*
|
|
* Write mass action equation for current model
|
|
*/
|
|
//if (write_mass_action_eqn_x(STOP) == ERROR) continue;
|
|
write_mass_action_eqn_x(STOP);
|
|
if (s[i]->type == SURF)
|
|
{
|
|
add_potential_factor();
|
|
add_cd_music_factors(i);
|
|
}
|
|
trxn_copy(s[i]->rxn_x);
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
s[i]->dz[j] = s[i]->rxn_x.dz[j];
|
|
}
|
|
if (debug_mass_action == TRUE)
|
|
{
|
|
output_msg(sformatf( "\n%s\n\tMass-action equation\n",
|
|
s[i]->name));
|
|
trxn_print();
|
|
}
|
|
/*
|
|
* Determine mass balance equations, build sums for mass balance, build sums for jacobian
|
|
*/
|
|
count_trxn = 0;
|
|
trxn_add(s[i]->rxn_s, 1.0, false);
|
|
if (s[i]->next_secondary.size() == 0)
|
|
{
|
|
write_mb_eqn_x();
|
|
}
|
|
else
|
|
{
|
|
count_elts = 0;
|
|
add_elt_list(s[i]->next_secondary, 1.0);
|
|
}
|
|
if (s[i]->type == SURF)
|
|
{
|
|
add_potential_factor();
|
|
add_cd_music_factors(i);
|
|
add_surface_charge_balance();
|
|
add_cd_music_charge_balances(i);
|
|
}
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf( "\n%s, Element composition:\n",
|
|
trxn.token[0].s->name));
|
|
for (j = 0; j < count_elts; j++)
|
|
{
|
|
output_msg(sformatf( "\t\t%-20s\t%10.2f\n",
|
|
elt_list[j].elt->name,
|
|
(double) elt_list[j].coef));
|
|
}
|
|
}
|
|
//if (debug_prep == TRUE)
|
|
//{
|
|
// output_msg(sformatf( "\n\tMass balance equation\n",
|
|
// s[i]->name));
|
|
// trxn_print();
|
|
//}
|
|
if (s[i]->type < EMINUS)
|
|
{
|
|
mb_for_species_aq(i);
|
|
}
|
|
else if (s[i]->type == EX)
|
|
{
|
|
mb_for_species_ex(i);
|
|
}
|
|
else if (s[i]->type == SURF)
|
|
{
|
|
mb_for_species_surf(i);
|
|
}
|
|
#ifdef COMBINE
|
|
build_mb_sums();
|
|
#else
|
|
if (s[i] != s_h2o)
|
|
{
|
|
build_mb_sums();
|
|
}
|
|
#endif
|
|
|
|
if (!pitzer_model && !sit_model)
|
|
build_jacobian_sums(i);
|
|
/*
|
|
* Build list of species for summing and printing
|
|
*/
|
|
if (s[i]->next_secondary.size() == 0)
|
|
{
|
|
write_mb_for_species_list(i);
|
|
}
|
|
else
|
|
{
|
|
count_elts = 0;
|
|
add_elt_list(s[i]->next_secondary, 1.0);
|
|
}
|
|
build_species_list(i);
|
|
}
|
|
}
|
|
if (dl_type_x != cxxSurface::NO_DL && (/*pitzer_model == TRUE || */sit_model == TRUE)) //DL_pitz
|
|
{
|
|
warning_msg("-diffuse_layer option not tested for SIT model");
|
|
}
|
|
/*
|
|
* Sum diffuse layer water into hydrogen and oxygen mass balances
|
|
*/
|
|
if (dl_type_x != cxxSurface::NO_DL && state >= REACTION)
|
|
{
|
|
for (i = 0; i < count_unknowns; i++)
|
|
{
|
|
if (x[i]->type == SURFACE_CB)
|
|
{
|
|
#ifndef COMBINE
|
|
store_mb(&(x[i]->mass_water),
|
|
&(mass_hydrogen_unknwon->f), 2 / gfw_water);
|
|
#endif
|
|
if (mass_oxygen_unknown != NULL)
|
|
{
|
|
store_mb(&(x[i]->mass_water),
|
|
&(mass_oxygen_unknown->f), 1 / gfw_water);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* For Pitzer model add lg unknown for each aqueous species
|
|
*/
|
|
if (pitzer_model == TRUE || sit_model == TRUE)
|
|
{
|
|
size_t j0 = count_unknowns;
|
|
size_t j = count_unknowns + this->s_x.size();
|
|
size_t k = j0;
|
|
for (size_t i = j0; i < j; i++)
|
|
{
|
|
if (s_x[i - j0]->type == EX)
|
|
continue;
|
|
if (s_x[i - j0]->type == SURF)
|
|
continue;
|
|
x[k]->number = k;
|
|
x[k]->type = PITZER_GAMMA;
|
|
x[k]->s = s_x[i - j0];
|
|
x[k]->description = s_x[i - j0]->name;
|
|
k++;
|
|
count_unknowns++;
|
|
}
|
|
sit_aqueous_unknowns = count_unknowns - j0;
|
|
}
|
|
/*
|
|
* Rewrite phases to current master species
|
|
*/
|
|
for (i = 0; i < (int)phases.size(); i++)
|
|
{
|
|
count_trxn = 0;
|
|
trxn_add_phase(phases[i]->rxn_s, 1.0, false);
|
|
trxn_reverse_k();
|
|
phases[i]->in = inout();
|
|
if (phases[i]->in == TRUE)
|
|
{
|
|
/*
|
|
* Replace e- in original equation with default redox reaction
|
|
*/
|
|
coef_e = trxn_find_coef("e-", 1);
|
|
if (equal(coef_e, 0.0, TOL) == FALSE)
|
|
{
|
|
trxn_add(pe_x[default_pe_x.c_str()], coef_e, TRUE);
|
|
}
|
|
/*
|
|
* Rewrite reaction to current master species
|
|
*/
|
|
write_mass_action_eqn_x(STOP);
|
|
trxn_reverse_k();
|
|
trxn_copy(phases[i]->rxn_x);
|
|
write_phase_sys_total(i);
|
|
}
|
|
}
|
|
build_solution_phase_boundaries();
|
|
build_pure_phases();
|
|
build_min_exch();
|
|
build_min_surface();
|
|
build_gas_phase();
|
|
build_ss_assemblage();
|
|
/*
|
|
* Sort species list, by master only
|
|
*/
|
|
if (species_list.size() > 1) qsort(&species_list[0], species_list.size(),
|
|
sizeof(class species_list), species_list_compare_master);
|
|
/*
|
|
* Save model description
|
|
*/
|
|
save_model();
|
|
|
|
if (input_error > 0)
|
|
{
|
|
error_msg("Stopping due to input errors.", STOP);
|
|
}
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
build_pure_phases(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Includes calculation of inverse saturation index in sum_mb.
|
|
* Puts coefficients in iap and mass balance equations for each phase.
|
|
*/
|
|
bool stop;
|
|
std::string token;
|
|
const char* cptr;
|
|
class master *master_ptr;
|
|
class rxn_token *rxn_ptr;
|
|
/*
|
|
* Build into sums the logic to calculate inverse saturation indices for
|
|
* pure phases
|
|
*/
|
|
if (pure_phase_unknown == NULL)
|
|
return (OK);
|
|
|
|
/*
|
|
* Calculate inverse saturation index
|
|
*/
|
|
for (int i = 0; i < count_unknowns; i++)
|
|
{
|
|
if (x[i]->type != PP || x[i]->phase->rxn_x.token.size() == 0)
|
|
continue;
|
|
if (pure_phase_unknown == NULL)
|
|
pure_phase_unknown = x[i];
|
|
|
|
store_mb(&(x[i]->phase->lk), &(x[i]->f), 1.0);
|
|
store_mb(&(x[i]->si), &(x[i]->f), 1.0);
|
|
|
|
for (rxn_ptr = &x[i]->phase->rxn_x.token[0] + 1; rxn_ptr->s != NULL;
|
|
rxn_ptr++)
|
|
{
|
|
store_mb(&(rxn_ptr->s->la), &(x[i]->f), -rxn_ptr->coef);
|
|
}
|
|
}
|
|
for (int i = 0; i < count_unknowns; i++)
|
|
{
|
|
/*
|
|
* rxn_x is null if an element in phase is not in solution
|
|
*/
|
|
if (x[i]->type != PP || x[i]->phase->rxn_x.token.size() == 0)
|
|
continue;
|
|
/*
|
|
* Put coefficients into IAP equations
|
|
*/
|
|
for (rxn_ptr = &x[i]->phase->rxn_x.token[0] + 1; rxn_ptr->s != NULL;
|
|
rxn_ptr++)
|
|
{
|
|
if (rxn_ptr->s->secondary != NULL
|
|
&& rxn_ptr->s->secondary->in == TRUE)
|
|
{
|
|
master_ptr = rxn_ptr->s->secondary;
|
|
}
|
|
else
|
|
{
|
|
master_ptr = rxn_ptr->s->primary;
|
|
}
|
|
if (master_ptr == NULL || master_ptr->unknown == NULL)
|
|
continue;
|
|
store_jacob0((int)x[i]->number, (int)master_ptr->unknown->number,
|
|
rxn_ptr->coef);
|
|
}
|
|
/*
|
|
* Put coefficients into mass balance equations
|
|
*/
|
|
count_elts = 0;
|
|
paren_count = 0;
|
|
//cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name);
|
|
cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[i]->pp_assemblage_comp_ptr;
|
|
if (comp_ptr->Get_add_formula().size() > 0)
|
|
{
|
|
cptr = comp_ptr->Get_add_formula().c_str();
|
|
get_elts_in_species(&cptr, 1.0);
|
|
}
|
|
else
|
|
{
|
|
cptr = x[i]->phase->formula;
|
|
get_elts_in_species(&cptr, 1.0);
|
|
}
|
|
/*
|
|
* Go through elements in phase
|
|
*/
|
|
|
|
#ifdef COMBINE
|
|
change_hydrogen_in_elt_list(0);
|
|
#endif
|
|
for (int j = 0; j < count_elts; j++)
|
|
{
|
|
|
|
if (strcmp(elt_list[j].elt->name, "H") == 0
|
|
&& mass_hydrogen_unknown != NULL)
|
|
{
|
|
store_jacob0((int)mass_hydrogen_unknown->number, (int)x[i]->number,
|
|
-elt_list[j].coef);
|
|
store_sum_deltas(&(delta[i]), &mass_hydrogen_unknown->delta,
|
|
elt_list[j].coef);
|
|
|
|
}
|
|
else if (strcmp(elt_list[j].elt->name, "O") == 0
|
|
&& mass_oxygen_unknown != NULL)
|
|
{
|
|
store_jacob0((int)mass_oxygen_unknown->number, (int)x[i]->number,
|
|
-elt_list[j].coef);
|
|
store_sum_deltas(&(delta[i]), &mass_oxygen_unknown->delta,
|
|
elt_list[j].coef);
|
|
|
|
}
|
|
else
|
|
{
|
|
master_ptr = elt_list[j].elt->primary;
|
|
if (master_ptr == NULL)
|
|
{
|
|
error_string = sformatf(
|
|
"Element undefined, %s.",
|
|
elt_list[j].elt->name);
|
|
error_msg(error_string, STOP);
|
|
}
|
|
if (master_ptr->in == FALSE)
|
|
{
|
|
master_ptr = master_ptr->s->secondary;
|
|
}
|
|
if (master_ptr == NULL || master_ptr->in == FALSE)
|
|
{
|
|
if (state != ADVECTION && state != TRANSPORT
|
|
&& state != PHAST)
|
|
{
|
|
error_string = sformatf(
|
|
"Element in phase, %s, is not in model.",
|
|
x[i]->phase->name);
|
|
warning_msg(error_string);
|
|
}
|
|
if (master_ptr != NULL)
|
|
{
|
|
master_ptr->s->la = -999.9;
|
|
}
|
|
/*
|
|
* Master species is in model
|
|
*/
|
|
}
|
|
else if (master_ptr->in == TRUE)
|
|
{
|
|
store_jacob0((int)master_ptr->unknown->number, (int)x[i]->number,
|
|
-elt_list[j].coef);
|
|
store_sum_deltas(&delta[i], &master_ptr->unknown->delta,
|
|
elt_list[j].coef);
|
|
/*
|
|
* Master species in equation needs to be rewritten
|
|
*/
|
|
}
|
|
else if (master_ptr->in == REWRITE)
|
|
{
|
|
stop = false;
|
|
for (int k = 0; k < count_unknowns; k++)
|
|
{
|
|
if (x[k]->type != MB)
|
|
continue;
|
|
for (size_t l = 0; l < x[k]->master.size(); l++)
|
|
{
|
|
if (x[k]->master[l] == master_ptr)
|
|
{
|
|
store_jacob0((int)x[k]->master[0]->unknown->number,
|
|
(int)x[i]->number, -elt_list[j].coef);
|
|
store_sum_deltas(&delta[i],
|
|
&x[k]->master[0]->unknown->
|
|
delta, elt_list[j].coef);
|
|
stop = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (stop == TRUE)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
build_solution_phase_boundaries(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
int i;
|
|
class master *master_ptr;
|
|
class rxn_token *rxn_ptr;
|
|
/*
|
|
* Build into sums the logic to calculate inverse saturation indices for
|
|
* solution phase boundaries
|
|
*/
|
|
if (solution_phase_boundary_unknown == NULL)
|
|
return (OK);
|
|
/*
|
|
* Calculate inverse saturation index
|
|
*/
|
|
for (i = 0; i < count_unknowns; i++)
|
|
{
|
|
if (x[i]->type != SOLUTION_PHASE_BOUNDARY)
|
|
continue;
|
|
|
|
store_mb(&(x[i]->phase->lk), &(x[i]->f), 1.0);
|
|
store_mb(&(x[i]->si), &(x[i]->f), 1.0);
|
|
if (x[i]->phase->in != TRUE)
|
|
{
|
|
error_string = sformatf(
|
|
"Solution does not contain all elements for phase-boundary mineral, %s.",
|
|
x[i]->phase->name);
|
|
error_msg(error_string, CONTINUE);
|
|
input_error++;
|
|
break;
|
|
}
|
|
for (rxn_ptr = &x[i]->phase->rxn_x.token[0] + 1; rxn_ptr->s != NULL;
|
|
rxn_ptr++)
|
|
{
|
|
store_mb(&(rxn_ptr->s->la), &(x[i]->f), -rxn_ptr->coef);
|
|
}
|
|
}
|
|
if (get_input_errors() > 0)
|
|
return (ERROR);
|
|
/*
|
|
* Put coefficients into array
|
|
*/
|
|
for (i = 0; i < count_unknowns; i++)
|
|
{
|
|
if (x[i]->type != SOLUTION_PHASE_BOUNDARY)
|
|
continue;
|
|
for (rxn_ptr = &x[i]->phase->rxn_x.token[0] + 1; rxn_ptr->s != NULL;
|
|
rxn_ptr++)
|
|
{
|
|
if (rxn_ptr->s->secondary != NULL
|
|
&& rxn_ptr->s->secondary->in == TRUE)
|
|
{
|
|
master_ptr = rxn_ptr->s->secondary;
|
|
}
|
|
else
|
|
{
|
|
master_ptr = rxn_ptr->s->primary;
|
|
}
|
|
if (master_ptr->unknown == NULL)
|
|
continue;
|
|
store_jacob0((int)x[i]->number, (int)master_ptr->unknown->number,
|
|
rxn_ptr->coef);
|
|
}
|
|
}
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
build_species_list(int n)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Builds a list that includes an entry for each master species in each
|
|
* secondary reaction. Used for summing species of each element and
|
|
* printing results.
|
|
*/
|
|
int j;
|
|
class master *master_ptr;
|
|
/*
|
|
* Treat species made only with H+, e-, and H2O specially
|
|
*/
|
|
if (is_special(s[n]) == TRUE)
|
|
{
|
|
size_t count_species_list = species_list.size();
|
|
species_list.resize(count_species_list + 1);
|
|
species_list[count_species_list].master_s = s_hplus;
|
|
species_list[count_species_list].s = s[n];
|
|
species_list[count_species_list].coef = 0.0;
|
|
return (OK);
|
|
}
|
|
/*
|
|
* Treat exchange species specially
|
|
*/
|
|
if (s[n]->type == EX)
|
|
{
|
|
if (s[n]->primary != NULL)
|
|
return (OK); /* master species has zero molality */
|
|
for (j = 0; j < count_elts; j++)
|
|
{
|
|
if (elt_list[j].elt->master->s->type != EX)
|
|
continue;
|
|
master_ptr = elt_list[j].elt->master;
|
|
size_t count_species_list = species_list.size();
|
|
species_list.resize(count_species_list + 1);
|
|
species_list[count_species_list].master_s =
|
|
elt_list[j].elt->master->s;
|
|
species_list[count_species_list].s = s[n];
|
|
species_list[count_species_list].coef = master_ptr->coef *
|
|
elt_list[j].coef;
|
|
}
|
|
return (OK);
|
|
}
|
|
/*
|
|
* Treat surface species specially
|
|
*/
|
|
if (s[n]->type == SURF_PSI)
|
|
return (OK);
|
|
if (s[n]->type == SURF)
|
|
{
|
|
for (j = 0; j < count_elts; j++)
|
|
{
|
|
if (elt_list[j].elt->master->s->type != SURF)
|
|
continue;
|
|
master_ptr = elt_list[j].elt->master;
|
|
size_t count_species_list = species_list.size();
|
|
species_list.resize(count_species_list + 1);
|
|
species_list[count_species_list].master_s =
|
|
elt_list[j].elt->master->s;
|
|
species_list[count_species_list].s = s[n];
|
|
species_list[count_species_list].coef = master_ptr->coef *
|
|
elt_list[j].coef;
|
|
}
|
|
return (OK);
|
|
}
|
|
/*
|
|
* Other aqueous species
|
|
*/
|
|
for (j = 0; j < count_elts; j++)
|
|
{
|
|
if (is_special(elt_list[j].elt->master->s) == TRUE)
|
|
continue;
|
|
if (elt_list[j].elt->master->s->secondary != NULL)
|
|
{
|
|
master_ptr = elt_list[j].elt->master->s->secondary;
|
|
}
|
|
else
|
|
{
|
|
master_ptr = elt_list[j].elt->master->s->primary;
|
|
}
|
|
size_t count_species_list = species_list.size();
|
|
species_list.resize(count_species_list + 1);
|
|
species_list[count_species_list].master_s = master_ptr->s;
|
|
species_list[count_species_list].s = s[n];
|
|
/*
|
|
* Find coefficient for element represented by master species
|
|
*/
|
|
species_list[count_species_list].coef = master_ptr->coef *
|
|
elt_list[j].coef;
|
|
}
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
clear(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
int i;
|
|
/*
|
|
* Resets information for setting up a new model
|
|
*/
|
|
cxxSolution *solution_ptr;
|
|
/*
|
|
* Clear species solution-dependent data
|
|
*/
|
|
solution_ptr = use.Get_solution_ptr();
|
|
|
|
for (i = 0; i < (int)s.size(); i++)
|
|
{
|
|
s[i]->in = FALSE;
|
|
}
|
|
/*
|
|
* Set pe structure
|
|
*/
|
|
pe_x.clear();
|
|
default_pe_x.clear();
|
|
if (solution_ptr->Get_initial_data())
|
|
{
|
|
pe_x = solution_ptr->Get_initial_data()->Get_pe_reactions();
|
|
default_pe_x = solution_ptr->Get_initial_data()->Get_default_pe();
|
|
}
|
|
else
|
|
{
|
|
default_pe_x = "pe";
|
|
CReaction chem_rxn;
|
|
pe_x[default_pe_x] = chem_rxn;
|
|
}
|
|
|
|
/*
|
|
* Clear master species solution-dependent data
|
|
*/
|
|
const char * pe_str = string_hsave("pe");
|
|
for (i = 0; i < (int)master.size(); i++)
|
|
{
|
|
master[i]->in = FALSE;
|
|
master[i]->unknown = NULL;
|
|
if (solution_ptr->Get_initial_data())
|
|
{
|
|
master[i]->pe_rxn = solution_ptr->Get_initial_data()->Get_default_pe();
|
|
}
|
|
else
|
|
{
|
|
master[i]->pe_rxn = pe_str;
|
|
}
|
|
/*
|
|
* copy primary reaction to secondary reaction
|
|
*/
|
|
master[i]->rxn_secondary = master[i]->rxn_primary;
|
|
}
|
|
|
|
if (state == INITIAL_SOLUTION)
|
|
{
|
|
s_h2o->secondary->in = TRUE;
|
|
s_hplus->secondary->in = TRUE;
|
|
}
|
|
else
|
|
{
|
|
s_h2o->primary->in = TRUE;
|
|
s_hplus->primary->in = TRUE;
|
|
}
|
|
s_eminus->primary->in = TRUE;
|
|
/*
|
|
* Set all unknown pointers to NULL
|
|
*/
|
|
mb_unknown = NULL;
|
|
ah2o_unknown = NULL;
|
|
mass_hydrogen_unknown = NULL;
|
|
mass_oxygen_unknown = NULL;
|
|
mu_unknown = NULL;
|
|
alkalinity_unknown = NULL;
|
|
carbon_unknown = NULL;
|
|
ph_unknown = NULL;
|
|
pe_unknown = NULL;
|
|
charge_balance_unknown = NULL;
|
|
solution_phase_boundary_unknown = NULL;
|
|
pure_phase_unknown = NULL;
|
|
exchange_unknown = NULL;
|
|
surface_unknown = NULL;
|
|
gas_unknown = NULL;
|
|
ss_unknown = NULL;
|
|
/*
|
|
* Free arrays used in model
|
|
*/
|
|
free_model_allocs();
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
convert_units(cxxSolution *solution_ptr)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Converts solution concentrations to moles/kg water
|
|
* Uses totals.input conc to calculate totals.moles.
|
|
*/
|
|
LDBLE sum_solutes;
|
|
class master *master_ptr;
|
|
std::string token;
|
|
if (!solution_ptr->Get_new_def() || !solution_ptr->Get_initial_data())
|
|
{
|
|
input_error++;
|
|
error_msg("Missing data for convert_units", 1);
|
|
}
|
|
/*
|
|
* Convert units
|
|
*/
|
|
#ifdef ORIGINAL
|
|
sum_solutes = exp(-solution_ptr->Get_ph() * LOG_10);
|
|
#else
|
|
double g_h, g_oh;
|
|
compute_gfw("H", &g_h);
|
|
compute_gfw("OH", &g_oh);
|
|
if (density_iterations == 0)
|
|
{
|
|
sum_solutes = exp(-solution_ptr->Get_ph() * LOG_10) * g_h;
|
|
sum_solutes += exp((-14 + solution_ptr->Get_ph()) * LOG_10) * g_oh;
|
|
}
|
|
else
|
|
{
|
|
double soln_vol = calc_solution_volume();
|
|
sum_solutes = s_hplus->moles / soln_vol * g_h;
|
|
species* s_oh = s_search("OH-");
|
|
sum_solutes += s_oh->moles / soln_vol * g_oh;
|
|
}
|
|
#endif
|
|
cxxISolution *initial_data_ptr = solution_ptr->Get_initial_data();
|
|
std::map<std::string, cxxISolutionComp >::iterator jit = initial_data_ptr->Get_comps().begin();
|
|
for ( ; jit != initial_data_ptr->Get_comps().end(); jit++)
|
|
{
|
|
cxxISolutionComp &comp_ref = jit->second;
|
|
LDBLE moles;
|
|
master_ptr = master_bsearch(comp_ref.Get_description().c_str());
|
|
if (master_ptr != NULL)
|
|
{
|
|
if (master_ptr->minor_isotope == TRUE)
|
|
continue;
|
|
}
|
|
|
|
// initially store 0.0 for totals
|
|
solution_ptr->Get_totals()[comp_ref.Get_description()] = 0.0;
|
|
|
|
if (strcmp(comp_ref.Get_description().c_str(), "H(1)") == 0 ||
|
|
strcmp(comp_ref.Get_description().c_str(), "E") == 0)
|
|
{
|
|
continue;
|
|
}
|
|
if (comp_ref.Get_input_conc() <= 0)
|
|
continue;
|
|
/*
|
|
* Get gfw
|
|
*/
|
|
/* use given gfw if gfw > 0.0 */
|
|
/* use formula give with "as" */
|
|
if (comp_ref.Get_gfw() <= 0.0)
|
|
{
|
|
if (comp_ref.Get_as().size() > 0)
|
|
{
|
|
/* use given chemical formula to calculate gfw */
|
|
if (compute_gfw(comp_ref.Get_as().c_str(), &dummy) == ERROR)
|
|
{
|
|
error_string = sformatf( "Could not compute gfw, %s.",
|
|
comp_ref.Get_as().c_str());
|
|
error_msg(error_string, CONTINUE);
|
|
input_error++;
|
|
}
|
|
else
|
|
{
|
|
comp_ref.Set_gfw(dummy);
|
|
}
|
|
if (strcmp(comp_ref.Get_description().c_str(), "Alkalinity") == 0 &&
|
|
strcmp(comp_ref.Get_as().c_str(), "CaCO3") == 0)
|
|
{
|
|
comp_ref.Set_gfw(comp_ref.Get_gfw() / 2.0);
|
|
error_string = sformatf(
|
|
"Equivalent wt for alkalinity should be Ca.5(CO3).5. Using %g g/eq.",
|
|
(double) comp_ref.Get_gfw());
|
|
warning_msg(error_string);
|
|
}
|
|
/* use gfw of master species */
|
|
}
|
|
else
|
|
{
|
|
const char* cptr = comp_ref.Get_description().c_str();
|
|
copy_token(token, &cptr);
|
|
master_ptr = master_bsearch(token.c_str());
|
|
if (master_ptr != NULL)
|
|
{
|
|
/* use gfw for element redox state */
|
|
comp_ref.Set_gfw(master_ptr->gfw);
|
|
}
|
|
else
|
|
{
|
|
error_string = sformatf( "Could not find gfw, %s.",
|
|
comp_ref.Get_description().c_str());
|
|
error_msg(error_string, CONTINUE);
|
|
input_error++;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Convert liters to kg solution
|
|
*/
|
|
moles = comp_ref.Get_input_conc();
|
|
if (strstr(initial_data_ptr->Get_units().c_str(), "/l") != NULL)
|
|
{
|
|
moles *= 1.0 / (solution_ptr->Get_density());
|
|
}
|
|
/*
|
|
* Convert milli or micro
|
|
*/
|
|
char c = comp_ref.Get_units()[0];
|
|
if (c == 'm')
|
|
{
|
|
moles *= 1e-3;
|
|
}
|
|
else if (c == 'u')
|
|
{
|
|
moles *= 1e-6;
|
|
}
|
|
/*
|
|
* Sum grams of solute, convert from moles necessary
|
|
*/
|
|
if (strstr(comp_ref.Get_units().c_str(), "g/kgs") != NULL ||
|
|
strstr(comp_ref.Get_units().c_str(), "g/l") != NULL)
|
|
{
|
|
sum_solutes += moles;
|
|
}
|
|
else if (strstr(comp_ref.Get_units().c_str(), "Mol/kgs") != NULL ||
|
|
strstr(comp_ref.Get_units().c_str(), "Mol/l") != NULL ||
|
|
strstr(comp_ref.Get_units().c_str(), "eq/l") != NULL)
|
|
{
|
|
sum_solutes += moles * comp_ref.Get_gfw();
|
|
}
|
|
/*
|
|
* Convert grams to moles, if necessary
|
|
*/
|
|
if (strstr(comp_ref.Get_units().c_str(), "g/") != NULL && comp_ref.Get_gfw() != 0.0)
|
|
{
|
|
moles /= comp_ref.Get_gfw();
|
|
}
|
|
solution_ptr->Get_totals()[comp_ref.Get_description()] = moles;
|
|
}
|
|
/*
|
|
* Convert /kgs to /kgw
|
|
*/
|
|
if (strstr(initial_data_ptr->Get_units().c_str(), "kgs") != NULL ||
|
|
strstr(initial_data_ptr->Get_units().c_str(), "/l") != NULL)
|
|
{
|
|
mass_water_aq_x = 1.0 - 1e-3 * sum_solutes;
|
|
if (density_iterations > 0)
|
|
{
|
|
mass_water_aq_x = kgw_kgs;
|
|
}
|
|
if (mass_water_aq_x <= 0)
|
|
{
|
|
error_string = sformatf( "Solute mass exceeds solution mass in conversion from /kgs to /kgw.\n"
|
|
"Mass of water is negative.");
|
|
error_msg(error_string, CONTINUE);
|
|
input_error++;
|
|
}
|
|
cxxNameDouble::iterator it;
|
|
for (it = solution_ptr->Get_totals().begin(); it != solution_ptr->Get_totals().end(); it++)
|
|
{
|
|
it->second = it->second / mass_water_aq_x;
|
|
}
|
|
}
|
|
/*
|
|
* Scale by mass of water in solution
|
|
*/
|
|
mass_water_aq_x = solution_ptr->Get_mass_water();
|
|
cxxNameDouble::iterator it;
|
|
for (it = solution_ptr->Get_totals().begin(); it != solution_ptr->Get_totals().end(); it++)
|
|
{
|
|
it->second = it->second * mass_water_aq_x;
|
|
}
|
|
|
|
initial_data_ptr->Set_units(moles_per_kilogram_string);
|
|
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
std::vector<class master *> Phreeqc::
|
|
get_list_master_ptrs(const char* cptr, class master *master_ptr)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Input: cptr contains a list of one or more master species names
|
|
* Output: space is allocated and a list of master species pointers is
|
|
* returned.
|
|
*/
|
|
//int j, l, count_list;
|
|
int j, l;
|
|
char token[MAX_LENGTH];
|
|
std::vector<class master*> master_ptr_list;
|
|
class master *master_ptr0;
|
|
/*
|
|
* Make list of master species pointers
|
|
*/
|
|
//count_list = 0;
|
|
//master_ptr_list = unknown_alloc_master();
|
|
master_ptr0 = master_ptr;
|
|
if (master_ptr0 == master_ptr->s->primary)
|
|
{
|
|
/*
|
|
* First in list is primary species
|
|
*/
|
|
for (j = 0; j < (int)master.size(); j++)
|
|
{
|
|
if (master[j] == master_ptr0)
|
|
break;
|
|
}
|
|
j++;
|
|
/*
|
|
* Element has only one valence
|
|
*/
|
|
if (j >= (int)master.size() || master[j]->elt->primary != master_ptr0)
|
|
{
|
|
master_ptr_list.push_back(master_ptr0);
|
|
/*
|
|
* Element has multiple valences
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
if (master_ptr0->s->secondary == NULL)
|
|
{
|
|
error_string = sformatf(
|
|
"Master species for valence states of element %s are not correct.\n\tPossibly related to master species for %s.",
|
|
master_ptr0->elt->name, master[j]->elt->name);
|
|
error_msg(error_string, CONTINUE);
|
|
input_error++;
|
|
}
|
|
master_ptr_list.push_back(master_ptr0->s->secondary);
|
|
while (j < (int)master.size() && master[j]->elt->primary == master_ptr0)
|
|
{
|
|
if (master[j]->s->primary == NULL)
|
|
{
|
|
master_ptr_list.push_back(master[j]);
|
|
}
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* First in list is secondary species, Include all valences from input
|
|
*/
|
|
master_ptr_list.push_back(master_ptr0);
|
|
while (copy_token(token, &cptr, &l) != EMPTY)
|
|
{
|
|
master_ptr = master_bsearch(token);
|
|
if (master_ptr != NULL)
|
|
{
|
|
master_ptr_list.push_back(master_ptr);
|
|
}
|
|
}
|
|
}
|
|
return (master_ptr_list);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
inout(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
int i;
|
|
class rxn_token_temp *token_ptr;
|
|
/*
|
|
* Routine goes through trxn to determine if each master species is
|
|
* in this model.
|
|
* Assumes equation is written in terms of primary and secondary species
|
|
* Checks to see if in is TRUE or REWRITE for each species
|
|
* Returns TRUE if in model
|
|
* FALSE if not
|
|
*/
|
|
for (i = 1; i < count_trxn; i++)
|
|
{
|
|
token_ptr = &(trxn.token[i]);
|
|
/* Check primary master species in */
|
|
if (token_ptr->s->primary != NULL
|
|
&& (token_ptr->s->primary->in == TRUE))
|
|
continue;
|
|
/* Check secondary master species */
|
|
if ((token_ptr->s->secondary != NULL)
|
|
&& (token_ptr->s->secondary->in != FALSE))
|
|
{
|
|
continue;
|
|
}
|
|
/* Must be primary master species that is out */
|
|
return (FALSE);
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
is_special(class species *l_spec)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Checks to see if a species is composed of only H, O, and e-
|
|
* Returns TRUE if true
|
|
* FALSE if not
|
|
*/
|
|
int special;
|
|
class rxn_token *token_ptr;
|
|
|
|
special = TRUE;
|
|
for (token_ptr = &l_spec->rxn_s.token[0] + 1; token_ptr->s != NULL;
|
|
token_ptr++)
|
|
{
|
|
if (token_ptr->s != s_hplus &&
|
|
token_ptr->s != s_h2o && token_ptr->s != s_eminus)
|
|
{
|
|
special = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
return (special);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
store_mb_unknowns(class unknown *unknown_ptr, LDBLE * LDBLE_ptr, LDBLE coef,
|
|
LDBLE * gamma_ptr)
|
|
/* ---------------------------------------------------------------------- */
|
|
/*
|
|
* Takes an unknown pointer and a coefficient and puts in
|
|
* list of mb_unknowns
|
|
*/
|
|
{
|
|
if (equal(coef, 0.0, TOL) == TRUE)
|
|
return (OK);
|
|
size_t count_mb_unknowns = mb_unknowns.size();
|
|
mb_unknowns.resize(count_mb_unknowns + 1);
|
|
mb_unknowns[count_mb_unknowns].unknown = unknown_ptr;
|
|
mb_unknowns[count_mb_unknowns].source = LDBLE_ptr;
|
|
mb_unknowns[count_mb_unknowns].gamma_source = gamma_ptr;
|
|
mb_unknowns[count_mb_unknowns].coef = coef;
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
mb_for_species_aq(int n)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Make list of mass balance and charge balance equations in which
|
|
* to insert species n.
|
|
*
|
|
* count_mb_unknowns - number of equations and summation relations
|
|
* mb_unknowns.unknown - pointer to unknown which contains row number
|
|
* mb_unknowns.source - pointer to the LDBLE number to be multiplied
|
|
* by coef, usually moles.
|
|
* mb_unknowns.coef - coefficient of s[n] in equation or relation
|
|
*/
|
|
//int i, j;
|
|
int i;
|
|
class master *master_ptr;
|
|
class unknown *unknown_ptr;
|
|
|
|
mb_unknowns.clear();
|
|
/*
|
|
* e- does not appear in any mass balances
|
|
*/
|
|
if (s[n]->type == EMINUS)
|
|
return (OK);
|
|
/*
|
|
* Do not include diffuse layer in cb, alk, ah2o, mu
|
|
*/
|
|
if (charge_balance_unknown != NULL && s[n]->type < H2O)
|
|
{
|
|
store_mb_unknowns(charge_balance_unknown, &s[n]->moles, s[n]->z,
|
|
&s[n]->dg);
|
|
}
|
|
if (alkalinity_unknown != NULL && s[n]->type < H2O)
|
|
{
|
|
store_mb_unknowns(alkalinity_unknown, &s[n]->moles, s[n]->alk,
|
|
&s[n]->dg);
|
|
}
|
|
if (ah2o_unknown != NULL && s[n]->type < H2O)
|
|
{
|
|
store_mb_unknowns(ah2o_unknown, &s[n]->moles, 1.0, &s[n]->dg);
|
|
}
|
|
if (mu_unknown != NULL && s[n]->type < H2O)
|
|
{
|
|
store_mb_unknowns(mu_unknown, &s[n]->moles, s[n]->z * s[n]->z,
|
|
&s[n]->dg);
|
|
}
|
|
/*
|
|
* Include diffuse layer in hydrogen and oxygen mass balance
|
|
*/
|
|
if (mass_hydrogen_unknown != NULL)
|
|
{
|
|
if (dl_type_x != cxxSurface::NO_DL && state >= REACTION)
|
|
{
|
|
#ifdef COMBINE
|
|
store_mb_unknowns(mass_hydrogen_unknown, &s[n]->tot_g_moles,
|
|
s[n]->h - 2 * s[n]->o, &s[n]->dg_total_g);
|
|
#else
|
|
store_mb_unknowns(mass_hydrogen_unknown, &s[n]->tot_g_moles,
|
|
s[n]->h, &s[n]->dg_total_g);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifdef COMBINE
|
|
store_mb_unknowns(mass_hydrogen_unknown, &s[n]->moles,
|
|
s[n]->h - 2 * s[n]->o, &s[n]->dg);
|
|
#else
|
|
store_mb_unknowns(mass_hydrogen_unknown, &s[n]->moles, s[n]->h,
|
|
&s[n]->dg);
|
|
#endif
|
|
}
|
|
}
|
|
if (mass_oxygen_unknown != NULL)
|
|
{
|
|
if (dl_type_x != cxxSurface::NO_DL && state >= REACTION)
|
|
{
|
|
store_mb_unknowns(mass_oxygen_unknown, &s[n]->tot_g_moles,
|
|
s[n]->o, &s[n]->dg_total_g);
|
|
}
|
|
else
|
|
{
|
|
store_mb_unknowns(mass_oxygen_unknown, &s[n]->moles, s[n]->o,
|
|
&s[n]->dg);
|
|
}
|
|
}
|
|
/*
|
|
* Sum diffuse layer charge into (surface + DL) charge balance
|
|
*/
|
|
if (use.Get_surface_ptr() != NULL && s[n]->type < H2O && dl_type_x != cxxSurface::NO_DL)
|
|
{
|
|
//j = 0;
|
|
for (i = 0; i < count_unknowns; i++)
|
|
{
|
|
if (x[i]->type == SURFACE_CB)
|
|
{
|
|
cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[i]->surface_charge);
|
|
unknown_ptr = x[i];
|
|
if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC)
|
|
unknown_ptr = x[(size_t)i + 2];
|
|
|
|
store_mb_unknowns(unknown_ptr, s_diff_layer[n][charge_ptr->Get_name()].Get_g_moles_address(),
|
|
s[n]->z, s_diff_layer[n][charge_ptr->Get_name()].Get_dg_g_moles_address());
|
|
//j++;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Other mass balances
|
|
*/
|
|
for (i = 0; i < count_elts; i++)
|
|
{
|
|
if (elt_list[i].elt->master->s->type > AQ &&
|
|
elt_list[i].elt->master->s->type < SOLID)
|
|
continue;
|
|
master_ptr = elt_list[i].elt->master;
|
|
if (master_ptr->primary == TRUE)
|
|
{
|
|
if (master_ptr->s->secondary != NULL)
|
|
{
|
|
master_ptr = master_ptr->s->secondary;
|
|
}
|
|
}
|
|
if (master_ptr->unknown == ph_unknown)
|
|
{
|
|
continue;
|
|
}
|
|
else if (master_ptr->unknown == pe_unknown)
|
|
{
|
|
continue;
|
|
}
|
|
else if (master_ptr->unknown == charge_balance_unknown)
|
|
{
|
|
continue;
|
|
}
|
|
else if (master_ptr->unknown == alkalinity_unknown)
|
|
{
|
|
continue;
|
|
}
|
|
else if (master_ptr->unknown == NULL)
|
|
{
|
|
//std::cerr << "NULL: " << master_ptr->s->name << std::endl;
|
|
continue;
|
|
}
|
|
else if (master_ptr->unknown->type == SOLUTION_PHASE_BOUNDARY)
|
|
{
|
|
continue;
|
|
}
|
|
if (dl_type_x != cxxSurface::NO_DL && state >= REACTION)
|
|
{
|
|
store_mb_unknowns(master_ptr->unknown,
|
|
&s[n]->tot_g_moles,
|
|
elt_list[i].coef * master_ptr->coef,
|
|
&s[n]->dg_total_g);
|
|
}
|
|
else
|
|
{
|
|
store_mb_unknowns(master_ptr->unknown,
|
|
&s[n]->moles,
|
|
elt_list[i].coef * master_ptr->coef, &s[n]->dg);
|
|
}
|
|
}
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
mb_for_species_ex(int n)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Make list of mass balance and charge balance equations in which
|
|
* to insert exchange species n.
|
|
*
|
|
* count_mb_unknowns - number of equations and summation relations
|
|
* mb_unknowns.source - pointer to the LDBLE number to be multiplied
|
|
* by coef, usually moles.
|
|
* mb_unknowns.unknown - pointer to unknown which contains row number
|
|
* mb_unknowns.coef - coefficient of s[n] in equation or relation
|
|
*/
|
|
int i;
|
|
class master *master_ptr;
|
|
|
|
mb_unknowns.clear();
|
|
/*
|
|
* Master species for exchange do not appear in any mass balances
|
|
*/
|
|
if (s[n]->type == EX && s[n]->primary != NULL)
|
|
return (OK);
|
|
/*
|
|
* Include diffuse layer in hydrogen and oxygen mass balance
|
|
*/
|
|
if (charge_balance_unknown != NULL)
|
|
{
|
|
store_mb_unknowns(charge_balance_unknown, &s[n]->moles, s[n]->z,
|
|
&s[n]->dg);
|
|
}
|
|
if (mass_hydrogen_unknown != NULL)
|
|
{
|
|
#ifdef COMBINE
|
|
store_mb_unknowns(mass_hydrogen_unknown, &s[n]->moles,
|
|
s[n]->h - 2 * s[n]->o, &s[n]->dg);
|
|
#else
|
|
store_mb_unknowns(mass_hydrogen_unknown, &s[n]->moles, s[n]->h,
|
|
&s[n]->dg);
|
|
#endif
|
|
}
|
|
if (mass_oxygen_unknown != NULL)
|
|
{
|
|
store_mb_unknowns(mass_oxygen_unknown, &s[n]->moles, s[n]->o,
|
|
&s[n]->dg);
|
|
}
|
|
/*
|
|
* Other mass balances
|
|
*/
|
|
for (i = 0; i < count_elts; i++)
|
|
{
|
|
if (elt_list[i].elt->master->s->type > AQ &&
|
|
elt_list[i].elt->master->s->type < SOLID)
|
|
continue;
|
|
master_ptr = elt_list[i].elt->master;
|
|
if (master_ptr->primary == TRUE)
|
|
{
|
|
if (master_ptr->s->secondary != NULL)
|
|
{
|
|
master_ptr = master_ptr->s->secondary;
|
|
}
|
|
}
|
|
/*
|
|
* Special for ph_unknown, pe_unknown, and alkalinity_unknown
|
|
*/
|
|
if (master_ptr->unknown == ph_unknown)
|
|
{
|
|
continue;
|
|
}
|
|
else if (master_ptr->unknown == pe_unknown)
|
|
{
|
|
continue;
|
|
}
|
|
else if (master_ptr->unknown == alkalinity_unknown)
|
|
{
|
|
continue;
|
|
}
|
|
/*
|
|
* EX, sum exchange species only into EXCH mass balance in initial calculation
|
|
* into all mass balances in reaction calculation
|
|
*/
|
|
if (state >= REACTION || master_ptr->s->type == EX)
|
|
{
|
|
store_mb_unknowns(master_ptr->unknown, &s[n]->moles,
|
|
elt_list[i].coef * master_ptr->coef, &s[n]->dg);
|
|
}
|
|
}
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
mb_for_species_surf(int n)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Make list of mass balance and charge balance equations in which
|
|
* to insert species n.
|
|
*
|
|
* count_mb_unknowns - number of equations and summation relations
|
|
* mb_unknowns.source - pointer to the LDBLE number to be multiplied
|
|
* by coef, usually moles.
|
|
* mb_unknowns.unknown - pointer to unknown which contains row number
|
|
* mb_unknowns.coef - coefficient of s[n] in equation or relation
|
|
*/
|
|
int i;
|
|
class master *master_ptr;
|
|
|
|
mb_unknowns.clear();
|
|
/*
|
|
* Include in charge balance, if diffuse_layer_x == FALSE
|
|
*/
|
|
if (charge_balance_unknown != NULL && dl_type_x == cxxSurface::NO_DL)
|
|
{
|
|
store_mb_unknowns(charge_balance_unknown, &s[n]->moles, s[n]->z,
|
|
&s[n]->dg);
|
|
}
|
|
/*
|
|
* Include diffuse layer in hydrogen and oxygen mass balance
|
|
*/
|
|
if (mass_hydrogen_unknown != NULL)
|
|
{
|
|
#ifdef COMBINE
|
|
store_mb_unknowns(mass_hydrogen_unknown, &s[n]->moles,
|
|
s[n]->h - 2 * s[n]->o, &s[n]->dg);
|
|
#else
|
|
store_mb_unknowns(mass_hydrogen_unknown, &s[n]->moles, s[n]->h,
|
|
&s[n]->dg);
|
|
#endif
|
|
}
|
|
if (mass_oxygen_unknown != NULL)
|
|
{
|
|
store_mb_unknowns(mass_oxygen_unknown, &s[n]->moles, s[n]->o,
|
|
&s[n]->dg);
|
|
}
|
|
/*
|
|
* Other mass balances
|
|
*/
|
|
/*
|
|
* Other mass balances
|
|
*/
|
|
for (i = 0; i < count_elts; i++)
|
|
{
|
|
/* Skip H+, e-, and H2O */
|
|
if (elt_list[i].elt->master->s->type > AQ &&
|
|
elt_list[i].elt->master->s->type < SOLID)
|
|
continue;
|
|
master_ptr = elt_list[i].elt->master;
|
|
if (master_ptr->primary == TRUE)
|
|
{
|
|
if (master_ptr->s->secondary != NULL)
|
|
{
|
|
master_ptr = master_ptr->s->secondary;
|
|
}
|
|
}
|
|
/*
|
|
* SURF_PSI, sum surface species in (surface + DL) charge balance
|
|
*/
|
|
if (master_ptr->s->type == SURF_PSI
|
|
&& use.Get_surface_ptr()->Get_type() != cxxSurface::CD_MUSIC)
|
|
{
|
|
store_mb_unknowns(master_ptr->unknown, &s[n]->moles, s[n]->z,
|
|
&s[n]->dg);
|
|
continue;
|
|
}
|
|
if (master_ptr->s->type == SURF_PSI
|
|
&& use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC)
|
|
{
|
|
store_mb_unknowns(master_ptr->unknown, &s[n]->moles, s[n]->dz[0],
|
|
&s[n]->dg);
|
|
continue;
|
|
}
|
|
if (master_ptr->s->type == SURF_PSI1)
|
|
{
|
|
store_mb_unknowns(master_ptr->unknown, &s[n]->moles, s[n]->dz[1],
|
|
&s[n]->dg);
|
|
continue;
|
|
}
|
|
if (master_ptr->s->type == SURF_PSI2)
|
|
{
|
|
store_mb_unknowns(master_ptr->unknown, &s[n]->moles, s[n]->dz[2],
|
|
&s[n]->dg);
|
|
/*
|
|
if (diffuse_layer_x == TRUE) {
|
|
store_mb_unknowns(master_ptr->unknown, &s[n]->moles, s[n]->z, &s[n]->dg );
|
|
} else {
|
|
store_mb_unknowns(master_ptr->unknown, &s[n]->moles, s[n]->dz[2], &s[n]->dg );
|
|
}
|
|
*/
|
|
continue;
|
|
}
|
|
/*
|
|
* Special for ph_unknown, pe_unknown, and alkalinity_unknown
|
|
*/
|
|
if (master_ptr->unknown == ph_unknown)
|
|
{
|
|
continue;
|
|
}
|
|
else if (master_ptr->unknown == pe_unknown)
|
|
{
|
|
continue;
|
|
}
|
|
else if (master_ptr->unknown == alkalinity_unknown)
|
|
{
|
|
continue;
|
|
}
|
|
/*
|
|
* SURF, sum surface species only into SURFACE mass balance in initial calculation
|
|
* into all mass balances in reaction calculation
|
|
*/
|
|
if (state >= REACTION || master_ptr->s->type == SURF)
|
|
{
|
|
store_mb_unknowns(master_ptr->unknown, &s[n]->moles,
|
|
elt_list[i].coef * master_ptr->coef, &s[n]->dg);
|
|
}
|
|
}
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
reprep(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* If a basis species has been switched, makes new model.
|
|
* Unknowns are not changed, but mass-action equations are
|
|
* rewritten and lists for mass balance and jacobian are regenerated
|
|
*/
|
|
int i;
|
|
/*
|
|
* Initialize s, master, and unknown pointers
|
|
*/
|
|
for (i = 0; i < (int)master.size(); i++)
|
|
{
|
|
if (master[i]->in == FALSE)
|
|
continue;
|
|
master[i]->rxn_secondary = master[i]->rxn_primary;
|
|
}
|
|
resetup_master();
|
|
/*
|
|
* Set unknown pointers, unknown types, validity checks
|
|
*/
|
|
tidy_redox();
|
|
if (get_input_errors() > 0)
|
|
{
|
|
error_msg("Program terminating due to input errors.", STOP);
|
|
}
|
|
/*
|
|
* Free arrays built in build_model
|
|
*/
|
|
s_x.clear();
|
|
sum_mb1.clear();
|
|
sum_mb2.clear();
|
|
sum_jacob0.clear();
|
|
sum_jacob1.clear();
|
|
sum_jacob2.clear();
|
|
sum_delta.clear();
|
|
/*
|
|
* Build model again
|
|
*/
|
|
build_model();
|
|
k_temp(tc_x, patm_x);
|
|
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
resetup_master(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* For basis switch, rewrite equations for master species
|
|
* Set master_ptr->rxn_secondary,
|
|
* master_ptr->pe_rxn,
|
|
* and special cases for alkalinity, carbon, and pH.
|
|
*/
|
|
int i, j;
|
|
class master *master_ptr, *master_ptr0;
|
|
|
|
for (i = 0; i < count_unknowns; i++)
|
|
{
|
|
if (x[i]->type != MB)
|
|
continue;
|
|
master_ptr0 = x[i]->master[0];
|
|
for (j = 0; j < x[i]->master.size(); j++)
|
|
{
|
|
master_ptr = x[i]->master[j];
|
|
/*
|
|
* Set flags
|
|
*/
|
|
if (j == 0)
|
|
{
|
|
if (master_ptr->s->primary == NULL)
|
|
{
|
|
master_ptr->rxn_secondary = master_ptr->s->rxn_s;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (master_ptr0->s->primary == NULL)
|
|
{
|
|
rewrite_master_to_secondary(master_ptr, master_ptr0);
|
|
trxn_copy(master_ptr->rxn_secondary);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
write_mass_action_eqn_x(int stop)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Reduce mass-action equation to the master species that are in the model
|
|
*/
|
|
LDBLE coef_e;
|
|
int count, repeat;
|
|
int i;
|
|
size_t count_rxn_orig;
|
|
/*
|
|
* Rewrite any secondary master species flagged REWRITE
|
|
* Replace pe if necessary
|
|
*/
|
|
count = 0;
|
|
repeat = TRUE;
|
|
while (repeat == TRUE)
|
|
{
|
|
count++;
|
|
if (count > MAX_ADD_EQUATIONS)
|
|
{
|
|
std::string name;
|
|
name = "Unknown";
|
|
if (trxn.token[0].s != NULL)
|
|
{
|
|
name = trxn.token[0].s->name;
|
|
}
|
|
|
|
input_error++;
|
|
error_string = sformatf( "Could not reduce equation "
|
|
"to primary and secondary species that are "
|
|
"in the model. Species: %s.", name.c_str());
|
|
if (stop == STOP)
|
|
{
|
|
error_msg(error_string, CONTINUE);
|
|
}
|
|
else
|
|
{
|
|
warning_msg(error_string);
|
|
}
|
|
return (ERROR);
|
|
}
|
|
repeat = FALSE;
|
|
count_rxn_orig = count_trxn;
|
|
for (i = 1; i < count_rxn_orig; i++)
|
|
{
|
|
if (trxn.token[i].s->secondary == NULL)
|
|
continue;
|
|
if (trxn.token[i].s->secondary->in == REWRITE)
|
|
{
|
|
repeat = TRUE;
|
|
coef_e =
|
|
rxn_find_coef(trxn.token[i].s->secondary->rxn_secondary,
|
|
"e-");
|
|
trxn_add(trxn.token[i].s->secondary->rxn_secondary,
|
|
trxn.token[i].coef, false);
|
|
if (equal(coef_e, 0.0, TOL) == FALSE)
|
|
{
|
|
std::map < std::string, CReaction >::iterator chemRxnIt = pe_x.find(trxn.token[i].s->secondary->pe_rxn);
|
|
if ( chemRxnIt == pe_x.end() )
|
|
{
|
|
CReaction& rxn_ref = pe_x[trxn.token[i].s->secondary->pe_rxn];
|
|
trxn_add(rxn_ref, trxn.token[i].coef * coef_e, FALSE);
|
|
// Create temporary rxn object and add reactions together
|
|
CReaction rxn;
|
|
trxn_add(rxn, trxn.token[i].coef * coef_e, FALSE);
|
|
}
|
|
else
|
|
{
|
|
// Get reaction referred to by iterator and add reactions together
|
|
trxn_add(chemRxnIt->second, trxn.token[i].coef * coef_e, FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
trxn_combine();
|
|
}
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
add_potential_factor(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Add the potential factor to surface mass-action equations.
|
|
* Factor is essentially the activity coefficient, representing
|
|
* the work required to bring charged ions to the surface
|
|
*/
|
|
int i;
|
|
std::string token;
|
|
LDBLE sum_z;
|
|
class master *master_ptr;
|
|
class unknown *unknown_ptr;
|
|
|
|
if (use.Get_surface_ptr() == NULL)
|
|
{
|
|
input_error++;
|
|
error_string = sformatf(
|
|
"SURFACE not defined for surface species %s",
|
|
trxn.token[0].name);
|
|
error_msg(error_string, CONTINUE);
|
|
return(OK);
|
|
}
|
|
if (use.Get_surface_ptr()->Get_type() != cxxSurface::DDL && use.Get_surface_ptr()->Get_type() != cxxSurface::CCM)
|
|
return (OK);
|
|
sum_z = 0.0;
|
|
master_ptr = NULL;
|
|
/*
|
|
* Find sum of charge of aqueous species and surface master species
|
|
*/
|
|
for (i = 1; i < count_trxn; i++)
|
|
{
|
|
if (trxn.token[i].s->type == AQ || trxn.token[i].s == s_hplus ||
|
|
trxn.token[i].s == s_eminus)
|
|
{
|
|
sum_z += trxn.token[i].s->z * trxn.token[i].coef;
|
|
}
|
|
if (trxn.token[i].s->type == SURF)
|
|
{
|
|
master_ptr = trxn.token[i].s->primary;
|
|
}
|
|
}
|
|
/*
|
|
* Find potential unknown for surface species
|
|
*/
|
|
if (master_ptr == NULL)
|
|
{
|
|
error_string = sformatf(
|
|
"Did not find a surface species in equation defining %s",
|
|
trxn.token[0].name);
|
|
error_msg(error_string, CONTINUE);
|
|
error_string = sformatf(
|
|
"One of the following must be defined with SURFACE_SPECIES:");
|
|
error_msg(error_string, CONTINUE);
|
|
for (i = 1; i < count_trxn; i++)
|
|
{
|
|
error_string = sformatf( " %s", trxn.token[i].name);
|
|
error_msg(error_string, CONTINUE);
|
|
}
|
|
input_error++;
|
|
return (ERROR);
|
|
}
|
|
token = master_ptr->elt->name;
|
|
unknown_ptr = find_surface_charge_unknown(token, SURF_PSI);
|
|
if (unknown_ptr == NULL)
|
|
{
|
|
error_string = sformatf(
|
|
"No potential unknown found for surface species %s.", token.c_str());
|
|
error_msg(error_string, STOP);
|
|
}
|
|
else
|
|
{
|
|
master_ptr = unknown_ptr->master[0]; /* potential for surface component */
|
|
}
|
|
/*
|
|
* Make sure there is space
|
|
*/
|
|
if (count_trxn + 1 > trxn.token.size())
|
|
trxn.token.resize(count_trxn + 1);
|
|
/*
|
|
* Include psi in mass action equation
|
|
*/
|
|
if (master_ptr != NULL)
|
|
{
|
|
trxn.token[count_trxn].name = master_ptr->s->name;
|
|
trxn.token[count_trxn].s = master_ptr->s;
|
|
trxn.token[count_trxn].coef = -2.0 * sum_z;
|
|
count_trxn++;
|
|
}
|
|
else
|
|
{
|
|
output_msg(sformatf(
|
|
"How did this happen in add potential factor?\n"));
|
|
}
|
|
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
add_cd_music_factors(int n)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Add the potential factors for cd_music to surface mass-action equations.
|
|
* Factors are essentially the activity coefficient, representing
|
|
* the work required to bring charged ions to the three charge layers
|
|
* of the cd_music model
|
|
*/
|
|
int i;
|
|
std::string token;
|
|
class master *master_ptr;
|
|
class unknown *unknown_ptr;
|
|
if (use.Get_surface_ptr() == NULL)
|
|
{
|
|
input_error++;
|
|
error_string = sformatf(
|
|
"SURFACE not defined for surface species %s",
|
|
trxn.token[0].name);
|
|
error_msg(error_string, CONTINUE);
|
|
return(OK);
|
|
}
|
|
if (use.Get_surface_ptr()->Get_type() != cxxSurface::CD_MUSIC)
|
|
return (OK);
|
|
master_ptr = NULL;
|
|
/*
|
|
* Find sum of charge of aqueous species and surface master species
|
|
*/
|
|
for (i = 1; i < count_trxn; i++)
|
|
{
|
|
if (trxn.token[i].s->type == SURF)
|
|
{
|
|
master_ptr = trxn.token[i].s->primary;
|
|
}
|
|
}
|
|
/*
|
|
* Find potential unknown for surface species
|
|
*/
|
|
if (master_ptr == NULL)
|
|
{
|
|
error_string = sformatf(
|
|
"Did not find a surface species in equation defining %s",
|
|
trxn.token[0].name);
|
|
error_msg(error_string, CONTINUE);
|
|
error_string = sformatf(
|
|
"One of the following must be defined with SURFACE_SPECIES:");
|
|
error_msg(error_string, CONTINUE);
|
|
for (i = 1; i < count_trxn; i++)
|
|
{
|
|
error_string = sformatf( " %s", trxn.token[i].name);
|
|
error_msg(error_string, CONTINUE);
|
|
}
|
|
input_error++;
|
|
return (ERROR);
|
|
}
|
|
token = master_ptr->elt->name;
|
|
/*
|
|
* Plane 0
|
|
*/
|
|
unknown_ptr = find_surface_charge_unknown(token, SURF_PSI);
|
|
if (unknown_ptr == NULL)
|
|
{
|
|
error_string = sformatf(
|
|
"No potential unknown found for surface species %s.", token.c_str());
|
|
error_msg(error_string, STOP);
|
|
return (ERROR);
|
|
}
|
|
master_ptr = unknown_ptr->master[0]; /* potential for surface component */
|
|
/*
|
|
* Make sure there is space
|
|
*/
|
|
if (count_trxn + 3 > trxn.token.size())
|
|
trxn.token.resize(count_trxn + 3);
|
|
/*
|
|
* Include psi in mass action equation
|
|
*/
|
|
trxn.token[count_trxn].name = master_ptr->s->name;
|
|
trxn.token[count_trxn].s = master_ptr->s;
|
|
/*trxn.token[count_trxn].coef = s[n]->dz[0];*/
|
|
trxn.token[count_trxn].coef = trxn.dz[0];
|
|
|
|
count_trxn++;
|
|
|
|
/*
|
|
* Plane 1
|
|
*/
|
|
unknown_ptr = find_surface_charge_unknown(token, SURF_PSI1);
|
|
if (unknown_ptr == NULL)
|
|
{
|
|
error_string = sformatf(
|
|
"No potential unknown found for surface species %s.", token.c_str());
|
|
error_msg(error_string, STOP);
|
|
return (ERROR);
|
|
}
|
|
master_ptr = unknown_ptr->master[0]; /* potential for surface component */
|
|
/*
|
|
* Include psi in mass action equation
|
|
*/
|
|
trxn.token[count_trxn].name = master_ptr->s->name;
|
|
trxn.token[count_trxn].s = master_ptr->s;
|
|
/*trxn.token[count_trxn].coef = s[n]->dz[1];*/
|
|
trxn.token[count_trxn].coef = trxn.dz[1];
|
|
count_trxn++;
|
|
/*
|
|
* Plane 2
|
|
*/
|
|
unknown_ptr = find_surface_charge_unknown(token, SURF_PSI2);
|
|
if (unknown_ptr == NULL)
|
|
{
|
|
error_string = sformatf(
|
|
"No potential unknown found for surface species %s.", token.c_str());
|
|
error_msg(error_string, STOP);
|
|
return (ERROR);
|
|
}
|
|
master_ptr = unknown_ptr->master[0]; /* potential for surface component */
|
|
/*
|
|
* Include psi in mass action equation
|
|
*/
|
|
trxn.token[count_trxn].name = master_ptr->s->name;
|
|
trxn.token[count_trxn].s = master_ptr->s;
|
|
/*trxn.token[count_trxn].coef = s[n]->dz[2];*/
|
|
trxn.token[count_trxn].coef = trxn.dz[2];
|
|
count_trxn++;
|
|
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
add_surface_charge_balance(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Include charge balance in list for mass-balance equations
|
|
*/
|
|
int i;
|
|
const char* cptr;
|
|
std::string token;
|
|
|
|
class master *master_ptr;
|
|
class unknown *unknown_ptr;
|
|
if (use.Get_surface_ptr() == NULL)
|
|
{
|
|
input_error++;
|
|
error_string = sformatf(
|
|
"SURFACE not defined for surface species %s",
|
|
trxn.token[0].name);
|
|
error_msg(error_string, CONTINUE);
|
|
return(OK);
|
|
}
|
|
if (use.Get_surface_ptr()->Get_type() != cxxSurface::DDL && use.Get_surface_ptr()->Get_type() != cxxSurface::CCM)
|
|
return (OK);
|
|
master_ptr = NULL;
|
|
/*
|
|
* Find master species
|
|
*/
|
|
for (i = 0; i < count_elts; i++)
|
|
{
|
|
if (elt_list[i].elt->primary->s->type == SURF)
|
|
{
|
|
master_ptr = elt_list[i].elt->primary;
|
|
break;
|
|
}
|
|
}
|
|
if (i >= count_elts)
|
|
{
|
|
error_string = sformatf(
|
|
"No surface master species found for surface species.");
|
|
error_msg(error_string, STOP);
|
|
return(OK);
|
|
}
|
|
/*
|
|
* Find potential unknown for surface species
|
|
*/
|
|
token = master_ptr->elt->name;
|
|
unknown_ptr = find_surface_charge_unknown(token, SURF_PSI);
|
|
if (unknown_ptr == NULL)
|
|
{
|
|
error_string = sformatf(
|
|
"No potential unknown found for surface species %s.", token.c_str());
|
|
error_msg(error_string, STOP);
|
|
return(OK);
|
|
}
|
|
master_ptr = unknown_ptr->master[0]; /* potential for surface component */
|
|
/*
|
|
* Include charge balance in list for mass-balance equations
|
|
*/
|
|
cptr = master_ptr->elt->name;
|
|
get_secondary_in_species(&cptr, 1.0);
|
|
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
add_cd_music_charge_balances(int n)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Add the potential factor to surface mass-action equations.
|
|
* Factor is essentially the activity coefficient, representing
|
|
* the work required to bring charged ions to the surface
|
|
*/
|
|
int i;
|
|
std::string token;
|
|
|
|
class master *master_ptr;
|
|
class unknown *unknown_ptr;
|
|
if (use.Get_surface_ptr() == NULL)
|
|
{
|
|
input_error++;
|
|
error_string = sformatf(
|
|
"SURFACE not defined for surface species %s",
|
|
trxn.token[0].name);
|
|
error_msg(error_string, CONTINUE);
|
|
return(OK);
|
|
}
|
|
if (use.Get_surface_ptr()->Get_type() != cxxSurface::CD_MUSIC)
|
|
return (OK);
|
|
master_ptr = NULL;
|
|
/*
|
|
* Find master species
|
|
*/
|
|
for (i = 0; i < count_elts; i++)
|
|
{
|
|
if (elt_list[i].elt->primary->s->type == SURF)
|
|
{
|
|
master_ptr = elt_list[i].elt->primary;
|
|
break;
|
|
}
|
|
}
|
|
if (i >= count_elts || master_ptr == NULL)
|
|
{
|
|
error_string = sformatf(
|
|
"No surface master species found for surface species.");
|
|
error_msg(error_string, STOP);
|
|
return ERROR;
|
|
}
|
|
/*
|
|
* Find potential unknown for plane 0
|
|
*/
|
|
token = master_ptr->elt->name;
|
|
unknown_ptr = find_surface_charge_unknown(token, SURF_PSI);
|
|
master_ptr = unknown_ptr->master[0]; /* potential for surface component */
|
|
/*
|
|
* Include charge balance in list for mass-balance equations
|
|
*/
|
|
{
|
|
const char* cptr = master_ptr->elt->name;
|
|
get_secondary_in_species(&cptr, s[n]->dz[0]);
|
|
}
|
|
/*
|
|
* Find potential unknown for plane 1
|
|
*/
|
|
token = master_ptr->elt->name;
|
|
unknown_ptr = find_surface_charge_unknown(token, SURF_PSI1);
|
|
master_ptr = unknown_ptr->master[0]; /* potential for surface component */
|
|
/*
|
|
* Include charge balance in list for mass-balance equations
|
|
*/
|
|
{
|
|
const char* cptr = master_ptr->elt->name;
|
|
get_secondary_in_species(&cptr, s[n]->dz[1]);
|
|
}
|
|
/*
|
|
* Find potential unknown for plane 2
|
|
*/
|
|
token = master_ptr->elt->name;
|
|
unknown_ptr = find_surface_charge_unknown(token, SURF_PSI2);
|
|
master_ptr = unknown_ptr->master[0]; /* potential for surface component */
|
|
/*
|
|
* Include charge balance in list for mass-balance equations
|
|
*/
|
|
{
|
|
const char* cptr = master_ptr->elt->name;
|
|
get_secondary_in_species(&cptr, s[n]->dz[2]);
|
|
}
|
|
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
rewrite_master_to_secondary(class master *master_ptr1,
|
|
class master *master_ptr2)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Write equation for secondary master species in terms of another secondary master species
|
|
* Store result in rxn_secondary of master_ptr.
|
|
*/
|
|
LDBLE coef1, coef2;
|
|
class master *master_ptr_p1, *master_ptr_p2;
|
|
/*
|
|
* Check that the two master species have the same primary master species
|
|
*/
|
|
master_ptr_p1 = master_ptr1->elt->primary;
|
|
master_ptr_p2 = master_ptr2->elt->primary;
|
|
if (master_ptr_p1 != master_ptr_p2 || master_ptr_p1 == NULL)
|
|
{
|
|
error_string = sformatf(
|
|
"All redox states must be for the same element. %s\t%s.",
|
|
master_ptr1->elt->name, master_ptr2->elt->name);
|
|
error_msg(error_string, CONTINUE);
|
|
input_error++;
|
|
return (ERROR);
|
|
}
|
|
/*
|
|
* Find coefficient of primary master in reaction
|
|
*/
|
|
coef1 = rxn_find_coef(master_ptr1->rxn_primary, master_ptr_p1->s->name);
|
|
coef2 = rxn_find_coef(master_ptr2->rxn_primary, master_ptr_p1->s->name);
|
|
if (equal(coef1, 0.0, TOL) == TRUE || equal(coef2, 0.0, TOL) == TRUE)
|
|
{
|
|
error_string = sformatf(
|
|
"One of these equations does not contain master species for element, %s or %s.",
|
|
master_ptr1->s->name, master_ptr2->s->name);
|
|
error_msg(error_string, CONTINUE);
|
|
input_error++;
|
|
return (ERROR);
|
|
}
|
|
/*
|
|
* Rewrite equation to secondary master species
|
|
*/
|
|
count_trxn = 0;
|
|
trxn_add(master_ptr1->rxn_primary, 1.0, false);
|
|
trxn_add(master_ptr2->rxn_primary, -coef1 / coef2, true);
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
setup_exchange(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Fill in data for exchanger in unknowns structures
|
|
*/
|
|
class master *master_ptr;
|
|
std::vector<class master*> master_ptr_list;
|
|
|
|
if (use.Get_exchange_ptr() == NULL)
|
|
return (OK);
|
|
for (size_t j = 0; j < use.Get_exchange_ptr()->Get_exchange_comps().size(); j++)
|
|
{
|
|
cxxExchComp & comp_ref = use.Get_exchange_ptr()->Get_exchange_comps()[j];
|
|
//{
|
|
// element * elt_ptr = element_store(comp_ref.Get_formula().c_str());
|
|
// if (elt_ptr == NULL || elt_ptr->master == NULL)
|
|
// {
|
|
// error_string = sformatf( "Component not in database, %s", comp_ref.Get_formula().c_str());
|
|
// input_error++;
|
|
// error_msg(error_string, CONTINUE);
|
|
// continue;
|
|
// }
|
|
//}
|
|
|
|
cxxNameDouble nd(comp_ref.Get_totals());
|
|
cxxNameDouble::iterator it = nd.begin();
|
|
for ( ; it != nd.end(); it++)
|
|
{
|
|
/*
|
|
* Find master species
|
|
*/
|
|
element * elt_ptr = element_store(it->first.c_str());
|
|
if (elt_ptr == NULL || elt_ptr->master == NULL)
|
|
{
|
|
error_string = sformatf( "Master species not in database "
|
|
"for %s, skipping element.",
|
|
it->first.c_str());
|
|
input_error++;
|
|
error_msg(error_string, CONTINUE);
|
|
continue;
|
|
}
|
|
master_ptr = elt_ptr->master;
|
|
if (master_ptr->type != EX)
|
|
continue;
|
|
/*
|
|
* Check for data already given
|
|
*/
|
|
if (master_ptr->in != FALSE)
|
|
{
|
|
x[master_ptr->unknown->number]->moles +=
|
|
it->second;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Set flags
|
|
*/
|
|
master_ptr_list.clear();
|
|
master_ptr_list.push_back(master_ptr);
|
|
master_ptr->in = TRUE;
|
|
/*
|
|
* Set unknown data
|
|
*/
|
|
x[count_unknowns]->type = EXCH;
|
|
x[count_unknowns]->exch_comp = string_hsave(it->first.c_str());
|
|
x[count_unknowns]->description = elt_ptr->name;
|
|
x[count_unknowns]->moles = it->second;
|
|
x[count_unknowns]->master = master_ptr_list;
|
|
x[count_unknowns]->master[0]->unknown = x[count_unknowns];
|
|
count_unknowns++;
|
|
}
|
|
}
|
|
}
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
setup_gas_phase(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Fill in data for gas phase unknown (sum of partial pressures)
|
|
* in unknown structure
|
|
*/
|
|
if (use.Get_gas_phase_ptr() == NULL)
|
|
return (OK);
|
|
cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr();
|
|
if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME && (
|
|
gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume) && numerical_fixed_volume)
|
|
{
|
|
return setup_fixed_volume_gas();
|
|
}
|
|
|
|
/*
|
|
* One for total moles in gas
|
|
*/
|
|
x[count_unknowns]->type = GAS_MOLES;
|
|
x[count_unknowns]->description = string_hsave("gas moles");
|
|
x[count_unknowns]->moles = 0.0;
|
|
for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++)
|
|
{
|
|
cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]);
|
|
x[count_unknowns]->moles += gc_ptr->Get_moles();
|
|
}
|
|
if (x[count_unknowns]->moles <= 0)
|
|
x[count_unknowns]->moles = MIN_TOTAL;
|
|
x[count_unknowns]->ln_moles = log(x[count_unknowns]->moles);
|
|
gas_unknown = x[count_unknowns];
|
|
count_unknowns++;
|
|
return (OK);
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
setup_ss_assemblage(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Fill in data for solid solution unknowns (sum of partial pressures)
|
|
* in unknown structure
|
|
*/
|
|
if (use.Get_ss_assemblage_ptr() == NULL)
|
|
return (OK);
|
|
/*
|
|
* One for each component in each solid solution
|
|
*/
|
|
ss_unknown = NULL;
|
|
std::vector<cxxSS *> ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize();
|
|
for (size_t j = 0; j < ss_ptrs.size(); j++)
|
|
{
|
|
for (size_t i = 0; i < ss_ptrs[j]->Get_ss_comps().size(); i++)
|
|
{
|
|
cxxSScomp *comp_ptr = &(ss_ptrs[j]->Get_ss_comps()[i]);
|
|
int l;
|
|
class phase* phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE);
|
|
x[count_unknowns]->type = SS_MOLES;
|
|
x[count_unknowns]->description = string_hsave(comp_ptr->Get_name().c_str());
|
|
x[count_unknowns]->moles = 0.0;
|
|
if (comp_ptr->Get_moles() <= 0)
|
|
{
|
|
comp_ptr->Set_moles(MIN_TOTAL_SS);
|
|
}
|
|
x[count_unknowns]->moles = comp_ptr->Get_moles();
|
|
comp_ptr->Set_initial_moles(x[count_unknowns]->moles);
|
|
x[count_unknowns]->ln_moles = log(x[count_unknowns]->moles);
|
|
x[count_unknowns]->ss_name = string_hsave(ss_ptrs[j]->Get_name().c_str());
|
|
x[count_unknowns]->ss_ptr = ss_ptrs[j];
|
|
x[count_unknowns]->ss_comp_name = string_hsave(comp_ptr->Get_name().c_str());
|
|
x[count_unknowns]->ss_comp_ptr = comp_ptr;
|
|
x[count_unknowns]->ss_comp_number = (int) i;
|
|
x[count_unknowns]->phase = phase_ptr;
|
|
x[count_unknowns]->number = count_unknowns;
|
|
x[count_unknowns]->phase->dn = comp_ptr->Get_dn();
|
|
x[count_unknowns]->phase->dnb = comp_ptr->Get_dnb();
|
|
x[count_unknowns]->phase->dnc = comp_ptr->Get_dnc();
|
|
x[count_unknowns]->phase->log10_fraction_x = comp_ptr->Get_log10_fraction_x();
|
|
x[count_unknowns]->phase->log10_lambda =comp_ptr->Get_log10_lambda();
|
|
if (ss_unknown == NULL)
|
|
ss_unknown = x[count_unknowns];
|
|
count_unknowns++;
|
|
}
|
|
}
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
setup_surface(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Fill in data for surface assemblage in unknown structure
|
|
*/
|
|
std::vector<class master*> master_ptr_list;
|
|
size_t mb_unknown_number;
|
|
|
|
if (use.Get_surface_ptr() == NULL)
|
|
return (OK);
|
|
|
|
for (size_t i = 0; i < use.Get_surface_ptr()->Get_surface_comps().size(); i++)
|
|
{
|
|
cxxSurfaceComp *comp_ptr = &(use.Get_surface_ptr()->Get_surface_comps()[i]);
|
|
/*
|
|
* Find master species for each surface, setup unknown structure
|
|
*/
|
|
cxxNameDouble::iterator jit;
|
|
for (jit = comp_ptr->Get_totals().begin(); jit != comp_ptr->Get_totals().end(); jit++)
|
|
{
|
|
class element *elt_ptr = element_store(jit->first.c_str());
|
|
class master *master_ptr = elt_ptr->master;
|
|
if (master_ptr == NULL)
|
|
{
|
|
error_string = sformatf(
|
|
"Master species not in database for %s, skipping element.",
|
|
elt_ptr->name);
|
|
warning_msg(error_string);
|
|
continue;
|
|
}
|
|
if (master_ptr->type != SURF)
|
|
continue;
|
|
/*
|
|
* Check that data not already given
|
|
*/
|
|
if (master_ptr->in != FALSE)
|
|
{
|
|
error_string = sformatf(
|
|
"Analytical data entered twice for %s.",
|
|
master_ptr->s->name);
|
|
error_msg(error_string, CONTINUE);
|
|
input_error++;
|
|
continue;
|
|
}
|
|
/*
|
|
* Set flags
|
|
*/
|
|
master_ptr_list.clear();
|
|
master_ptr_list.push_back(master_ptr);
|
|
master_ptr->in = TRUE;
|
|
/*
|
|
* Setup mass balance unknown
|
|
*/
|
|
x[count_unknowns]->type = SURFACE;
|
|
x[count_unknowns]->description = string_hsave(jit->first.c_str());
|
|
x[count_unknowns]->number = count_unknowns;
|
|
x[count_unknowns]->surface_comp = string_hsave(comp_ptr->Get_formula().c_str());
|
|
x[count_unknowns]->master = master_ptr_list;
|
|
x[count_unknowns]->master[0]->unknown = x[count_unknowns];
|
|
x[count_unknowns]->moles = jit->second;
|
|
if (surface_unknown == NULL)
|
|
surface_unknown = x[count_unknowns];
|
|
x[count_unknowns]->potential_unknown = NULL;
|
|
count_unknowns++;
|
|
/*if (use.Get_surface_ptr()->edl == FALSE) continue; */
|
|
if (use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM)
|
|
{
|
|
/*
|
|
* Setup surface-potential unknown
|
|
*/
|
|
std::string token = master_ptr->elt->name;
|
|
class unknown *unknown_ptr = find_surface_charge_unknown(token, SURF_PSI);
|
|
if (unknown_ptr != NULL)
|
|
{
|
|
x[count_unknowns - 1]->potential_unknown = unknown_ptr;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Find master species
|
|
*/
|
|
replace("_CB", "_psi", token);
|
|
master_ptr = master_bsearch(token.c_str());
|
|
master_ptr_list.clear();
|
|
master_ptr_list.push_back(master_ptr);
|
|
master_ptr->in = TRUE;
|
|
/*
|
|
* Find surface charge structure
|
|
*/
|
|
cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->
|
|
Find_charge(comp_ptr->Get_charge_name());
|
|
if (charge_ptr == NULL)
|
|
{
|
|
input_error++;
|
|
error_msg(sformatf("Charge structure not defined for surface, %s", use.Get_surface_ptr()->Get_description().c_str()), CONTINUE);
|
|
continue;
|
|
}
|
|
x[count_unknowns]->type = SURFACE_CB;
|
|
x[count_unknowns]->surface_charge = string_hsave(charge_ptr->Get_name().c_str());
|
|
x[count_unknowns]->related_moles = charge_ptr->Get_grams();
|
|
x[count_unknowns]->mass_water = charge_ptr->Get_mass_water();
|
|
replace("_psi", "_CB", token);
|
|
x[count_unknowns]->description = string_hsave(token.c_str());
|
|
x[count_unknowns]->master = master_ptr_list;
|
|
x[count_unknowns]->master[0]->unknown = x[count_unknowns];
|
|
x[count_unknowns]->moles = 0.0;
|
|
x[count_unknowns - 1]->potential_unknown = x[count_unknowns];
|
|
x[count_unknowns]->surface_comp = x[count_unknowns - 1]->surface_comp;
|
|
count_unknowns++;
|
|
}
|
|
}
|
|
else if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC)
|
|
{
|
|
/*
|
|
* Setup 3 surface-potential unknowns
|
|
*/
|
|
mb_unknown_number = count_unknowns - 1;
|
|
std::string token(master_ptr->elt->name);
|
|
std::string mass_balance_name(token);
|
|
int plane;
|
|
for (plane = SURF_PSI; plane <= SURF_PSI2; plane++)
|
|
{
|
|
std::string cb_suffix("_CB");
|
|
std::string psi_suffix("_psi");
|
|
class unknown **unknown_target;
|
|
unknown_target = NULL;
|
|
int type = SURFACE_CB;
|
|
switch (plane)
|
|
{
|
|
case SURF_PSI:
|
|
type = SURFACE_CB;
|
|
unknown_target =
|
|
&(x[mb_unknown_number]->potential_unknown);
|
|
break;
|
|
case SURF_PSI1:
|
|
cb_suffix.append("b");
|
|
psi_suffix.append("b");
|
|
type = SURFACE_CB1;
|
|
unknown_target = &(x[mb_unknown_number]->potential_unknown1);
|
|
break;
|
|
case SURF_PSI2:
|
|
cb_suffix.append("d");
|
|
psi_suffix.append("d");
|
|
type = SURFACE_CB2;
|
|
unknown_target = &(x[mb_unknown_number]->potential_unknown2);
|
|
break;
|
|
}
|
|
class unknown *unknown_ptr = find_surface_charge_unknown(token, plane);
|
|
if (unknown_ptr != NULL)
|
|
{
|
|
*unknown_target = unknown_ptr;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Find master species
|
|
*/
|
|
replace(cb_suffix.c_str(), psi_suffix.c_str(), token);
|
|
master_ptr = master_bsearch(token.c_str());
|
|
master_ptr_list.clear();
|
|
master_ptr_list.push_back(master_ptr);
|
|
master_ptr->in = TRUE;
|
|
/*
|
|
* Find surface charge structure
|
|
*/
|
|
cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->
|
|
Find_charge(comp_ptr->Get_charge_name());
|
|
x[count_unknowns]->type = type;
|
|
x[count_unknowns]->surface_charge = string_hsave(charge_ptr->Get_name().c_str());
|
|
x[count_unknowns]->related_moles = charge_ptr->Get_grams();
|
|
x[count_unknowns]->mass_water = charge_ptr->Get_mass_water();
|
|
replace(psi_suffix.c_str(), cb_suffix.c_str(), token);
|
|
x[count_unknowns]->description = string_hsave(token.c_str());
|
|
x[count_unknowns]->master = master_ptr_list;
|
|
/*
|
|
* Find surface charge structure
|
|
*/
|
|
if (plane == SURF_PSI)
|
|
{
|
|
/*use.Get_surface_ptr()->charge[k].psi_master = x[count_unknowns]->master[0]; */
|
|
x[mb_unknown_number]->potential_unknown =
|
|
x[count_unknowns];
|
|
}
|
|
else if (plane == SURF_PSI1)
|
|
{
|
|
/*use.Get_surface_ptr()->charge[k].psi_master1 = x[count_unknowns]->master[0]; */
|
|
x[mb_unknown_number]->potential_unknown1 =
|
|
x[count_unknowns];
|
|
}
|
|
else if (plane == SURF_PSI2)
|
|
{
|
|
/*use.Get_surface_ptr()->charge[k].psi_master2 = x[count_unknowns]->master[0]; */
|
|
x[mb_unknown_number]->potential_unknown2 =
|
|
x[count_unknowns];
|
|
}
|
|
x[count_unknowns]->master[0]->unknown =
|
|
x[count_unknowns];
|
|
x[count_unknowns]->moles = 0.0;
|
|
x[count_unknowns]->surface_comp =
|
|
x[mb_unknown_number]->surface_comp;
|
|
count_unknowns++;
|
|
}
|
|
}
|
|
/* Add SURFACE unknown to a list for SURF_PSI */
|
|
class unknown *unknown_ptr = find_surface_charge_unknown(token, SURF_PSI);
|
|
unknown_ptr->comp_unknowns.push_back(x[mb_unknown_number]);
|
|
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* check related phases
|
|
*/
|
|
if (use.Get_surface_ptr()->Get_related_phases())
|
|
{
|
|
cxxPPassemblage *pp_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, use.Get_n_surface_user());
|
|
for (size_t i = 0; i < use.Get_surface_ptr()->Get_surface_comps().size(); i++)
|
|
{
|
|
if (use.Get_surface_ptr()->Get_surface_comps()[i].Get_phase_name().size() > 0)
|
|
{
|
|
if (pp_ptr == NULL ||
|
|
(pp_ptr->Get_pp_assemblage_comps().find(use.Get_surface_ptr()->Get_surface_comps()[i].Get_phase_name()) ==
|
|
pp_ptr->Get_pp_assemblage_comps().end()))
|
|
{
|
|
Rxn_new_surface.insert(use.Get_n_surface_user());
|
|
cxxSurface *surf_ptr = Utilities::Rxn_find(Rxn_surface_map, use.Get_n_surface_user());
|
|
surf_ptr->Set_new_def(true);
|
|
this->tidy_min_surface();
|
|
return (FALSE);
|
|
}
|
|
}
|
|
}
|
|
for (int i = 0; i < count_unknowns; i++)
|
|
{
|
|
if (x[i]->type != SURFACE_CB)
|
|
continue;
|
|
cxxSurfaceComp *comp_i_ptr = use.Get_surface_ptr()->Find_comp(x[i]->surface_comp);
|
|
for (int j = 0; j < count_unknowns; j++)
|
|
{
|
|
if (x[j]->type != SURFACE)
|
|
continue;
|
|
if (x[j]->potential_unknown != x[i])
|
|
continue;
|
|
cxxSurfaceComp *comp_j_ptr = use.Get_surface_ptr()->Find_comp(x[j]->surface_comp);
|
|
std::string name1, name2;
|
|
if (comp_j_ptr->Get_phase_name() !=
|
|
comp_i_ptr->Get_phase_name())
|
|
{
|
|
if (comp_i_ptr->Get_phase_name().size() == 0)
|
|
{
|
|
name1 = "None";
|
|
}
|
|
else
|
|
{
|
|
name1 = comp_i_ptr->Get_phase_name();
|
|
}
|
|
if (comp_j_ptr->Get_phase_name().size() == 0)
|
|
{
|
|
name2 = "None";
|
|
}
|
|
else
|
|
{
|
|
name2 = comp_j_ptr->Get_phase_name();
|
|
}
|
|
input_error++;
|
|
|
|
error_string = sformatf(
|
|
"All surface sites for a single component must be related to the same phase.\n\tSite: %s is related to %s, Site: %s is related to %s",
|
|
comp_i_ptr->Get_master_element().c_str(), name1.c_str(),
|
|
comp_j_ptr->Get_master_element().c_str(), name2.c_str());
|
|
error_msg(error_string, CONTINUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* check related kinetics
|
|
*/
|
|
if (use.Get_surface_ptr()->Get_related_rate())
|
|
{
|
|
cxxKinetics *kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_surface_user());
|
|
for (size_t i = 0; i < use.Get_surface_ptr()->Get_surface_comps().size(); i++)
|
|
{
|
|
if (use.Get_surface_ptr()->Get_surface_comps()[i].Get_rate_name().size() > 0)
|
|
{
|
|
if (kinetics_ptr == NULL ||
|
|
(kinetics_ptr->Find(use.Get_surface_ptr()->Get_surface_comps()[i].Get_rate_name()) == NULL))
|
|
{
|
|
Rxn_new_surface.insert(use.Get_n_surface_user());
|
|
this->tidy_kin_surface();
|
|
return (FALSE);
|
|
}
|
|
}
|
|
}
|
|
for (int i = 0; i < count_unknowns; i++)
|
|
{
|
|
if (x[i]->type != SURFACE_CB)
|
|
continue;
|
|
cxxSurfaceComp *comp_i_ptr = use.Get_surface_ptr()->Find_comp(x[i]->surface_comp);
|
|
for (int j = 0; j < count_unknowns; j++)
|
|
{
|
|
if (x[j]->type != SURFACE)
|
|
continue;
|
|
if (x[j]->potential_unknown != x[i])
|
|
continue;
|
|
cxxSurfaceComp *comp_j_ptr = use.Get_surface_ptr()->Find_comp(x[j]->surface_comp);
|
|
if (comp_j_ptr->Get_rate_name() !=
|
|
comp_i_ptr->Get_rate_name())
|
|
{
|
|
std::string name1, name2;
|
|
if (comp_i_ptr->Get_rate_name().size() == 0)
|
|
{
|
|
name1 = "None";
|
|
}
|
|
else
|
|
{
|
|
name1 = comp_i_ptr->Get_rate_name();
|
|
}
|
|
if (comp_j_ptr->Get_rate_name().size() == 0)
|
|
{
|
|
name2 = "None";
|
|
}
|
|
else
|
|
{
|
|
name2 = comp_j_ptr->Get_rate_name();
|
|
}
|
|
input_error++;
|
|
error_string = sformatf(
|
|
"All surface sites for a single component must be related to the same kinetic reaction.\n\tSite: %s is related to %s, Site: %s is related to %s",
|
|
comp_i_ptr->Get_master_element().c_str(), name1.c_str(),
|
|
comp_j_ptr->Get_master_element().c_str(), name2.c_str());
|
|
error_msg(error_string, CONTINUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
class unknown * Phreeqc::
|
|
find_surface_charge_unknown(std::string &str, int plane)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Makes name for the potential unknown and returns in str_ptr
|
|
* Returns NULL if this unknown not in unknown list else
|
|
* returns a pointer to the potential unknown
|
|
*/
|
|
std::string token;
|
|
Utilities::replace("_", " ", str);
|
|
std::string::iterator b = str.begin();
|
|
std::string::iterator e = str.end();
|
|
CParser::copy_token(token, b, e);
|
|
if (plane == SURF_PSI)
|
|
{
|
|
token.append("_CB");
|
|
}
|
|
else if (plane == SURF_PSI1)
|
|
{
|
|
token.append("_CBb");
|
|
}
|
|
else if (plane == SURF_PSI2)
|
|
{
|
|
token.append("_CBd");
|
|
}
|
|
str = token;
|
|
for (int i = 0; i < count_unknowns; i++)
|
|
{
|
|
if (strcmp(str.c_str(), x[i]->description) == 0)
|
|
{
|
|
return (x[i]);
|
|
}
|
|
}
|
|
return (NULL);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
setup_master_rxn(const std::vector<class master *> &master_ptr_list, const std::string &pe_rxn)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Rewrites rxn_secondary for all redox states in list
|
|
* First, in = TRUE; others, in = REWRITE
|
|
*/
|
|
class master *master_ptr, *master_ptr0;
|
|
/*
|
|
* Set master_ptr->in, master_ptr->rxn
|
|
*/
|
|
master_ptr0 = master_ptr_list[0];
|
|
for (size_t j = 0; j < master_ptr_list.size(); j++)
|
|
{
|
|
master_ptr = master_ptr_list[j];
|
|
/*
|
|
* Check that data not already given
|
|
*/
|
|
if (master_ptr->s == s_h2o)
|
|
{
|
|
error_string = sformatf(
|
|
"Cannot enter concentration data for O(-2),\n\tdissolved oxygen is O(0),\n\tfor mass of water, use -water identifier.");
|
|
error_msg(error_string, CONTINUE);
|
|
input_error++;
|
|
continue;
|
|
}
|
|
|
|
if (master_ptr->in != FALSE)
|
|
{
|
|
if (master_ptr->s != s_eminus && master_ptr->s != s_hplus)
|
|
{
|
|
error_string = sformatf(
|
|
"Analytical data entered twice for %s.",
|
|
master_ptr->s->name);
|
|
error_msg(error_string, CONTINUE);
|
|
input_error++;
|
|
continue;
|
|
}
|
|
}
|
|
/*
|
|
* Set flags
|
|
*/
|
|
if (j == 0)
|
|
{
|
|
master_ptr->in = TRUE;
|
|
if (master_ptr->s->primary == NULL)
|
|
{
|
|
master_ptr->rxn_secondary = master_ptr->s->rxn_s;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
master_ptr->in = REWRITE;
|
|
if (master_ptr0->s->primary == NULL)
|
|
{
|
|
rewrite_master_to_secondary(master_ptr, master_ptr0);
|
|
trxn_copy(master_ptr->rxn_secondary);
|
|
}
|
|
}
|
|
master_ptr->pe_rxn = string_hsave(pe_rxn.c_str());
|
|
}
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
LDBLE Phreeqc::
|
|
calc_PR(std::vector<class phase *> phase_ptrs, LDBLE P, LDBLE TK, LDBLE V_m)
|
|
/* ---------------------------------------------------------------------- */
|
|
/* Calculate fugacity and fugacity coefficient for gas pressures if critical T and P
|
|
are defined.
|
|
1) Solve molar volume V_m or total pressure P from Peng-Robinson's EOS:
|
|
P = R * T / (V_m - b) - a * aa / (V_m^2 + 2 * b * V_m - b^2)
|
|
a = 0.457235 * (R * T_c)^2 / P_c
|
|
b = 0.077796 * R * T_c / P_c
|
|
aa = (1 + kk * (1 - T_r^0.5))^2
|
|
kk = 0.37464 + 1.54226 * omega - 0.26992 * omega^2
|
|
T_r = T / T_c
|
|
multicomponent gas phase:
|
|
use: b_sum = Sum(x_i * b), x_i is mole-fraction
|
|
a_aa_sum = Sum_i( Sum_j(x_i * x_j * (a_i * aa_i * a_j * aa_j)^0.5) )
|
|
2) Find the fugacity coefficient phi for gas i:
|
|
log(phi_i) = B_ratio * (z - 1) - log(z - B) + A / (2.8284 * B) * (B_ratio - 2 / a_aa_sum * a_aa_sum2) *\
|
|
log((z + 2.4142 * B) / (z - 0.4142 * B))
|
|
B_ratio = b_i / b_sum
|
|
A = a_aa_sum * P / R_TK^2
|
|
B = b_sum * P / R_TK
|
|
a_aa_sum2 = Sum_j(x_j * (a_aa_i * a_aa_j)^0.5
|
|
3) correct the solubility of gas i with:
|
|
pr_si_f = log10(phi_i) - Delta_V_i * (P - 1) / (2.303 * R * TK);
|
|
*/
|
|
{
|
|
int i, i1, n_g = (int) phase_ptrs.size();
|
|
LDBLE T_c, P_c;
|
|
LDBLE A, B, B_r, /*b2,*/ kk, oo, a_aa, T_r;
|
|
LDBLE m_sum, /*b_sum, a_aa_sum,*/ a_aa_sum2;
|
|
LDBLE phi;
|
|
LDBLE /*R_TK,*/ R = R_LITER_ATM; /* L atm / (K mol) */
|
|
LDBLE r3[4], r3_12, rp, rp3, rq, rz, ri, ri1, one_3 = 0.33333333333333333;
|
|
LDBLE disct, vinit, v1, ddp, dp_dv, dp_dv2;
|
|
int it;
|
|
class phase *phase_ptr, *phase_ptr1;
|
|
cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr();
|
|
bool halved;
|
|
R_TK = R * TK;
|
|
m_sum = b_sum = a_aa_sum = 0.0;
|
|
for (i = 0; i < n_g; i++)
|
|
{
|
|
phase_ptr = phase_ptrs[i];
|
|
if (n_g > 1)
|
|
{
|
|
if (phase_ptr->moles_x == 0)
|
|
continue;
|
|
m_sum += phase_ptr->moles_x;
|
|
}
|
|
if (phase_ptr->t_c == 0.0 || phase_ptr->p_c == 0.0)
|
|
error_msg("Cannot calculate a mixture of ideal and Peng_Robinson gases,\n please define Tc and Pc for the active gases in PHASES.", STOP);
|
|
//continue;
|
|
if (!phase_ptr->pr_a)
|
|
{
|
|
T_c = phase_ptr->t_c;
|
|
P_c = phase_ptr->p_c;
|
|
phase_ptr->pr_a = 0.457235 * R * R * T_c * T_c / P_c;
|
|
phase_ptr->pr_b = 0.077796 * R * T_c / P_c;
|
|
T_r = TK / T_c;
|
|
oo = phase_ptr->omega;
|
|
kk = 0.37464 + oo * (1.54226 - 0.26992 * oo);
|
|
phase_ptr->pr_alpha = pow(1 + kk * (1 - sqrt(T_r)), 2);
|
|
phase_ptr->pr_tk = TK;
|
|
// phase_ptr->pr_in = true;
|
|
}
|
|
if (phase_ptr->pr_tk != TK)
|
|
{
|
|
T_r = TK / phase_ptr->t_c;
|
|
oo = phase_ptr->omega;
|
|
kk = 0.37464 + oo * (1.54226 - 0.26992 * oo);
|
|
phase_ptr->pr_alpha = pow(1 + kk * (1 - sqrt(T_r)), 2);
|
|
phase_ptr->pr_tk = TK;
|
|
// phase_ptr->pr_in = true;
|
|
}
|
|
}
|
|
for (i = 0; i < n_g; i++)
|
|
{
|
|
phase_ptr = phase_ptrs[i];
|
|
if (n_g == 1)
|
|
{
|
|
phase_ptr->fraction_x = 1.0;
|
|
break;
|
|
}
|
|
if (m_sum == 0)
|
|
return (OK);
|
|
phase_ptr->fraction_x = phase_ptr->moles_x / m_sum;
|
|
}
|
|
|
|
for (i = 0; i < n_g; i++)
|
|
{
|
|
a_aa_sum2 = 0.0;
|
|
phase_ptr = phase_ptrs[i];
|
|
//if (phase_ptr->t_c == 0.0 || phase_ptr->p_c == 0.0)
|
|
// continue;
|
|
b_sum += phase_ptr->fraction_x * phase_ptr->pr_b;
|
|
for (i1 = 0; i1 < n_g; i1++)
|
|
{
|
|
phase_ptr1 = phase_ptrs[i1];
|
|
//if (phase_ptr1->t_c == 0.0 || phase_ptr1->p_c == 0.0)
|
|
// continue;
|
|
if (phase_ptr1->fraction_x == 0)
|
|
continue;
|
|
a_aa = sqrt(phase_ptr->pr_a * phase_ptr->pr_alpha *
|
|
phase_ptr1->pr_a * phase_ptr1->pr_alpha);
|
|
a_aa *= calc_gas_binary_parameter(phase_ptr->name, phase_ptr1->name);
|
|
//if (!strcmp(phase_ptr->name, "H2O(g)"))
|
|
//{
|
|
// if (!strcmp(phase_ptr1->name, "CO2(g)"))
|
|
// a_aa *= 0.81; // Soreide and Whitson, 1992, FPE 77, 217
|
|
// else if (!strcmp(phase_ptr1->name, "H2S(g)") || !strcmp(phase_ptr1->name, "H2Sg(g)"))
|
|
// a_aa *= 0.81;
|
|
// else if (!strcmp(phase_ptr1->name, "CH4(g)") || !strcmp(phase_ptr1->name, "Mtg(g)") || !strcmp(phase_ptr1->name, "Methane(g)"))
|
|
// a_aa *= 0.51;
|
|
// else if (!strcmp(phase_ptr1->name, "N2(g)") || !strcmp(phase_ptr1->name, "Ntg(g)"))
|
|
// a_aa *= 0.51;
|
|
// else if (!strcmp(phase_ptr1->name, "Ethane(g)"))
|
|
// a_aa *= 0.51;
|
|
// else if (!strcmp(phase_ptr1->name, "Propane(g)"))
|
|
// a_aa *= 0.45;
|
|
//}
|
|
//if (!strcmp(phase_ptr1->name, "H2O(g)"))
|
|
//{
|
|
// if (!strcmp(phase_ptr->name, "CO2(g)"))
|
|
// a_aa *= 0.81;
|
|
// else if (!strcmp(phase_ptr->name, "H2S(g)") || !strcmp(phase_ptr->name, "H2Sg(g)"))
|
|
// a_aa *= 0.81;
|
|
// else if (!strcmp(phase_ptr->name, "CH4(g)") || !strcmp(phase_ptr->name, "Mtg(g)") || !strcmp(phase_ptr->name, "Methane(g)"))
|
|
// a_aa *= 0.51;
|
|
// else if (!strcmp(phase_ptr->name, "N2(g)") || !strcmp(phase_ptr->name, "Ntg(g)"))
|
|
// a_aa *= 0.51;
|
|
// else if (!strcmp(phase_ptr->name, "Ethane(g)"))
|
|
// a_aa *= 0.51;
|
|
// else if (!strcmp(phase_ptr->name, "Propane(g)"))
|
|
// a_aa *= 0.45;
|
|
//}
|
|
a_aa_sum += phase_ptr->fraction_x * phase_ptr1->fraction_x * a_aa;
|
|
a_aa_sum2 += phase_ptr1->fraction_x * a_aa;
|
|
}
|
|
phase_ptr->pr_aa_sum2 = a_aa_sum2;
|
|
}
|
|
b2 = b_sum * b_sum;
|
|
|
|
if (V_m)
|
|
{
|
|
P = R_TK / (V_m - b_sum) - a_aa_sum / (V_m * (V_m + 2 * b_sum) - b2);
|
|
if (iterations > 0 && P < 150 && V_m < 1.01)
|
|
{
|
|
// check for 3-roots...
|
|
r3[1] = b_sum - R_TK / P;
|
|
r3[2] = -3.0 * b2 + (a_aa_sum - R_TK * 2.0 * b_sum) / P;
|
|
r3[3] = b2 * b_sum + (R_TK * b2 - b_sum * a_aa_sum) / P;
|
|
// the discriminant of the cubic eqn...
|
|
disct = 18. * r3[1] * r3[2] * r3[3] -
|
|
4. * pow(r3[1], 3) * r3[3] +
|
|
r3[1] * r3[1] * r3[2] * r3[2] -
|
|
4. * pow(r3[2], 3) -
|
|
27. * r3[3] * r3[3];
|
|
//if (iterations > 50)
|
|
// it = 0; // debug
|
|
if (disct > 0)
|
|
{
|
|
// 3-roots, find the largest P...
|
|
it = 0;
|
|
halved = false;
|
|
ddp = 1e-9;
|
|
v1 = vinit = 0.729;
|
|
dp_dv = f_Vm(v1, this);
|
|
while (fabs(dp_dv) > 1e-11 && it < 40)
|
|
{
|
|
it +=1;
|
|
dp_dv2 = f_Vm(v1 - ddp, this);
|
|
v1 -= (dp_dv * ddp / (dp_dv - dp_dv2));
|
|
if (!halved && (v1 > vinit || v1 < 0.03))
|
|
{
|
|
if (vinit > 0.329)
|
|
vinit -= 0.1;
|
|
else
|
|
vinit -=0.05;
|
|
if (vinit < 0.03)
|
|
{
|
|
vinit = halve(f_Vm, 0.03, 1.0, 1e-3);
|
|
if (f_Vm(vinit - 2e-3, this) < 0)
|
|
vinit = halve(f_Vm, vinit + 2e-3, 1.0, 1e-3);
|
|
halved = true;
|
|
}
|
|
v1 = vinit;
|
|
}
|
|
dp_dv = f_Vm(v1, this);
|
|
if (fabs(dp_dv) < 1e-11)
|
|
{
|
|
if (f_Vm(v1 - 1e-4, this) < 0)
|
|
{
|
|
v1 = halve(f_Vm, v1 + 1e-4, 1.0, 1e-3);
|
|
dp_dv = f_Vm(v1, this);
|
|
}
|
|
}
|
|
}
|
|
if (it == 40)
|
|
{
|
|
// accept a (possible) whobble in the curve...
|
|
// error_msg("No convergence when calculating P in Peng-Robinson.", STOP);
|
|
}
|
|
if (V_m < v1 && it < 40)
|
|
P = R_TK / (v1 - b_sum) - a_aa_sum / (v1 * (v1 + 2 * b_sum) - b2);
|
|
}
|
|
}
|
|
if (P <= 0) // iterations = -1
|
|
P = 1;
|
|
}
|
|
else
|
|
{
|
|
if (P < 1e-10)
|
|
P = 1e-10;
|
|
r3[1] = b_sum - R_TK / P;
|
|
r3_12 = r3[1] * r3[1];
|
|
r3[2] = -3.0 * b2 + (a_aa_sum - R_TK * 2.0 * b_sum) / P;
|
|
r3[3] = b2 * b_sum + (R_TK * b2 - b_sum * a_aa_sum) / P;
|
|
// solve t^3 + rp*t + rq = 0.
|
|
// molar volume V_m = t - r3[1] / 3...
|
|
rp = r3[2] - r3_12 / 3;
|
|
rp3 = rp * rp * rp;
|
|
rq = (2.0 * r3_12 * r3[1] - 9.0 * r3[1] * r3[2]) / 27 + r3[3];
|
|
rz = rq * rq / 4 + rp3 / 27;
|
|
if (rz >= 0) // Cardono's method...
|
|
{
|
|
ri = sqrt(rz);
|
|
if (ri + rq / 2 <= 0)
|
|
{
|
|
V_m = pow(ri - rq / 2, one_3) + pow(- ri - rq / 2, one_3) - r3[1] / 3;
|
|
}
|
|
else
|
|
{
|
|
ri = - pow(ri + rq / 2, one_3);
|
|
V_m = ri - rp / (3.0 * ri) - r3[1] / 3;
|
|
}
|
|
}
|
|
else // use complex plane...
|
|
{
|
|
ri = sqrt(- rp3 / 27); // rp < 0
|
|
ri1 = acos(- rq / 2 / ri);
|
|
V_m = 2.0 * pow(ri, one_3) * cos(ri1 / 3) - r3[1] / 3;
|
|
}
|
|
}
|
|
// calculate the fugacity coefficients...
|
|
for (i = 0; i < n_g; i++)
|
|
{
|
|
phase_ptr = phase_ptrs[i];
|
|
if (phase_ptr->fraction_x == 0.0)
|
|
{
|
|
phase_ptr->pr_p = 0;
|
|
phase_ptr->pr_phi = 1;
|
|
phase_ptr->pr_si_f = 0.0;
|
|
continue;
|
|
}
|
|
phase_ptr->pr_p = phase_ptr->fraction_x * P;
|
|
rz = P * V_m / R_TK;
|
|
A = a_aa_sum * P / (R_TK * R_TK);
|
|
B = b_sum * P / R_TK;
|
|
B_r = phase_ptr->pr_b / b_sum;
|
|
if (rz > B)
|
|
{
|
|
phi = B_r * (rz - 1) - log(rz - B) + A / (2.828427 * B) * (B_r - 2.0 * phase_ptr->pr_aa_sum2 / a_aa_sum) *
|
|
log((rz + 2.41421356 * B) / (rz - 0.41421356 * B));
|
|
phi = (phi > 4.44 ? 4.44 : (phi < -4.6 ? -4.6 : phi));
|
|
//if (phi > 4.44)
|
|
// phi = 4.44;
|
|
}
|
|
else
|
|
phi = -4.6; // fugacity coefficient = 0.01
|
|
//if (!strcmp(phase_ptr->name, "H2O(g)") && phi < -4.6)
|
|
//{
|
|
//// avoid such phi...
|
|
// phi = -4.6;
|
|
//}
|
|
phase_ptr->pr_phi = exp(phi);
|
|
phase_ptr->pr_si_f = phi / LOG_10;
|
|
// for initial equilibrations, adapt log_k of the gas phase...
|
|
if (state < REACTION)
|
|
{
|
|
rho_0 = calc_rho_0(TK - 273.15, P);
|
|
calc_dielectrics(TK - 273.15, P);
|
|
phase_ptr->lk = calc_lk_phase(phase_ptr, TK, P);
|
|
}
|
|
phase_ptr->pr_in = true;
|
|
}
|
|
if (gas_phase_ptr && iterations > 2)
|
|
{
|
|
if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME)
|
|
{
|
|
gas_phase_ptr->Set_total_p(P);
|
|
}
|
|
gas_phase_ptr->Set_v_m(V_m);
|
|
return (OK);
|
|
}
|
|
return (V_m);
|
|
}
|
|
|
|
LDBLE Phreeqc::
|
|
f_Vm(LDBLE v1, void *cookie)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
LDBLE ff;
|
|
Phreeqc * pThis;
|
|
pThis = (Phreeqc *) cookie;
|
|
|
|
ff = v1 * (v1 + 2 * pThis->b_sum) - pThis->b2;
|
|
LDBLE dp_dv = -pThis->R_TK / pow(v1 - pThis->b_sum, 2) +
|
|
pThis->a_aa_sum * 2 * (v1 + pThis->b_sum) / (ff * ff);
|
|
return dp_dv;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
setup_pure_phases(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
//LDBLE si_org;
|
|
/*
|
|
* Fills in data for pure_phase assemglage in unknown structure
|
|
*/
|
|
|
|
if (use.Get_pp_assemblage_ptr() == NULL)
|
|
return (OK);
|
|
cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr();
|
|
/*
|
|
* Setup unknowns
|
|
*/
|
|
std::map<std::string, cxxPPassemblageComp>::iterator it;
|
|
it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin();
|
|
for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++)
|
|
{
|
|
cxxPPassemblageComp * comp_ptr = &(it->second);
|
|
int j;
|
|
class phase * phase_ptr = phase_bsearch(it->first.c_str(), &j, FALSE);
|
|
assert(phase_ptr);
|
|
x[count_unknowns]->type = PP;
|
|
x[count_unknowns]->description = string_hsave(comp_ptr->Get_name().c_str());
|
|
x[count_unknowns]->pp_assemblage_comp_name = x[count_unknowns]->description;
|
|
x[count_unknowns]->pp_assemblage_comp_ptr = comp_ptr;
|
|
x[count_unknowns]->moles = comp_ptr->Get_moles();
|
|
x[count_unknowns]->phase = phase_ptr;
|
|
x[count_unknowns]->si = comp_ptr->Get_si();
|
|
//si_org = comp_ptr->Get_si_org();
|
|
/* si_org is used for Peng-Robinson gas, with the fugacity
|
|
coefficient added later in adjust_pure_phases,
|
|
when rxn_x has been defined for each phase in the model */
|
|
x[count_unknowns]->delta = comp_ptr->Get_delta();
|
|
x[count_unknowns]->dissolve_only = comp_ptr->Get_dissolve_only();
|
|
if (pure_phase_unknown == NULL)
|
|
pure_phase_unknown = x[count_unknowns];
|
|
count_unknowns++;
|
|
}
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
adjust_setup_pure_phases(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
int i;
|
|
class phase *phase_ptr;
|
|
LDBLE si_org, p, t;
|
|
/*
|
|
* Fills in data for pure_phase assemglage in unknown structure
|
|
*/
|
|
|
|
if (use.Get_pp_assemblage_ptr() == NULL)
|
|
return (OK);
|
|
/*
|
|
* Adjust si for gases
|
|
*/
|
|
for (i = 0; i < count_unknowns; i++)
|
|
{
|
|
std::vector<class phase *> phase_ptrs;
|
|
if (x[i]->type == PP)
|
|
{
|
|
phase_ptr = x[i]->phase;
|
|
phase_ptrs.push_back(phase_ptr);
|
|
//cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[i]->pp_assemblage_comp_name);
|
|
cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp * ) x[i]->pp_assemblage_comp_ptr;
|
|
si_org = comp_ptr->Get_si_org();
|
|
if (phase_ptr->p_c > 0 && phase_ptr->t_c > 0)
|
|
{
|
|
if (si_org > 3.5)
|
|
si_org = 3.5;
|
|
p = exp(si_org * LOG_10);
|
|
patm_x = p;
|
|
t = use.Get_solution_ptr()->Get_tc() + 273.15;
|
|
if (!phase_ptr->pr_in || p != phase_ptr->pr_p || t != phase_ptr->pr_tk)
|
|
{
|
|
calc_PR(phase_ptrs, p, t, 0);
|
|
}
|
|
x[i]->si = si_org + phase_ptr->pr_si_f;
|
|
}
|
|
}
|
|
}
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
setup_solution(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Fills in data in unknown structure for the solution
|
|
*/
|
|
class master *master_ptr;
|
|
cxxSolution *solution_ptr;
|
|
const char* cptr;
|
|
std::string token;
|
|
class master_isotope *master_isotope_ptr;
|
|
class phase *phase_ptr;
|
|
|
|
solution_ptr = use.Get_solution_ptr();
|
|
count_unknowns = 0;
|
|
|
|
/*
|
|
* Treat minor isotopes as special in initial solution calculation
|
|
*/
|
|
if (solution_ptr->Get_initial_data())
|
|
{
|
|
std::map<std::string, cxxISolutionComp >::iterator comp_it = solution_ptr->Get_initial_data()->Get_comps().begin();
|
|
for ( ; comp_it != solution_ptr->Get_initial_data()->Get_comps().end(); comp_it++)
|
|
{
|
|
master_ptr = master_bsearch(comp_it->first.c_str());
|
|
if ((master_ptr != NULL)
|
|
&& (master_ptr->minor_isotope == TRUE)
|
|
&& (initial_solution_isotopes == FALSE))
|
|
{
|
|
master_isotope_ptr = master_isotope_search(comp_it->first.c_str());
|
|
if (master_isotope_ptr != NULL)
|
|
{
|
|
master_isotope_ptr->ratio = comp_it->second.Get_input_conc();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
cxxNameDouble::iterator it = solution_ptr->Get_totals().begin();
|
|
for ( ; it != solution_ptr->Get_totals().end(); it++)
|
|
{
|
|
cxxISolutionComp *comp_ptr = NULL;
|
|
if (solution_ptr->Get_initial_data())
|
|
{
|
|
std::map<std::string, cxxISolutionComp >::iterator comp_it;
|
|
comp_it = solution_ptr->Get_initial_data()->Get_comps().find(it->first.c_str());
|
|
comp_ptr = &(comp_it->second);
|
|
}
|
|
cptr = it->first.c_str();
|
|
copy_token(token, &cptr);
|
|
master_ptr = master_bsearch(token.c_str());
|
|
/*
|
|
* Check that total not <= zero
|
|
*/
|
|
if (it->second <= 0.0)
|
|
{
|
|
if (strcmp(token.c_str(), "H(1)") != 0 && strcmp(token.c_str(), "E") != 0)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
/*
|
|
* Find master species
|
|
*/
|
|
master_ptr = master_bsearch(token.c_str());
|
|
if (master_ptr == NULL)
|
|
{
|
|
error_string = sformatf(
|
|
"Master species not in database for %s, skipping element.",
|
|
it->first.c_str());
|
|
warning_msg(error_string);
|
|
continue;
|
|
}
|
|
if (master_ptr->type != AQ)
|
|
{
|
|
/* solution_ptr->totals[i].skip = TRUE; */
|
|
error_string = sformatf(
|
|
"Only aqueous concentrations are allowed in solution data, ignoring %s.",
|
|
it->first.c_str());
|
|
warning_msg(error_string);
|
|
continue;
|
|
}
|
|
/*
|
|
* Store list of master species pointers, set master[i].in and master[i].rxn for list
|
|
*/
|
|
x[count_unknowns]->master = get_list_master_ptrs(cptr, master_ptr);
|
|
if (comp_ptr)
|
|
{
|
|
setup_master_rxn(x[count_unknowns]->master, comp_ptr->Get_pe_reaction());
|
|
}
|
|
else
|
|
{
|
|
setup_master_rxn(x[count_unknowns]->master, "pe");
|
|
}
|
|
|
|
/*
|
|
* Set default unknown data
|
|
*/
|
|
x[count_unknowns]->type = MB;
|
|
x[count_unknowns]->description = string_hsave(it->first.c_str());
|
|
for (size_t j = 0; j < x[count_unknowns]->master.size(); j++)
|
|
{
|
|
x[count_unknowns]->master[j]->unknown = x[count_unknowns];
|
|
}
|
|
x[count_unknowns]->moles = it->second;
|
|
/*
|
|
* Set pointers
|
|
*/
|
|
cptr = it->first.c_str();
|
|
copy_token(token, &cptr);
|
|
Utilities::str_tolower(token);
|
|
if (strstr(token.c_str(), "alk") != NULL)
|
|
{
|
|
if (alkalinity_unknown == NULL)
|
|
{
|
|
x[count_unknowns]->type = ALK;
|
|
alkalinity_unknown = x[count_unknowns];
|
|
}
|
|
else
|
|
{
|
|
error_msg("Alkalinity entered more than once.", CONTINUE);
|
|
input_error++;
|
|
}
|
|
}
|
|
else if (strcmp(token.c_str(), "c") == 0 || strcmp(token.c_str(), "c(4)") == 0)
|
|
{
|
|
if (carbon_unknown == NULL)
|
|
{
|
|
carbon_unknown = x[count_unknowns];
|
|
}
|
|
else
|
|
{
|
|
error_msg("Carbon entered more than once.", CONTINUE);
|
|
input_error++;
|
|
}
|
|
}
|
|
else if (strcmp(token.c_str(), "h(1)") == 0)
|
|
{
|
|
if (ph_unknown == NULL)
|
|
{
|
|
ph_unknown = x[count_unknowns];
|
|
}
|
|
else
|
|
{
|
|
error_msg("pH entered more than once.", CONTINUE);
|
|
input_error++;
|
|
}
|
|
}
|
|
else if (strcmp(token.c_str(), "e") == 0)
|
|
{
|
|
if (pe_unknown == NULL)
|
|
{
|
|
pe_unknown = x[count_unknowns];
|
|
}
|
|
else
|
|
{
|
|
error_msg("pe entered more than once.", CONTINUE);
|
|
input_error++;
|
|
}
|
|
}
|
|
/*
|
|
* Charge balance unknown
|
|
*/
|
|
if (comp_ptr && comp_ptr->Get_equation_name().size() > 0)
|
|
{
|
|
cptr = comp_ptr->Get_equation_name().c_str();
|
|
copy_token(token, &cptr);
|
|
Utilities::str_tolower(token);
|
|
if (strstr(token.c_str(), "charge") != NULL)
|
|
{
|
|
if (charge_balance_unknown == NULL)
|
|
{
|
|
charge_balance_unknown = x[count_unknowns];
|
|
x[count_unknowns]->type = CB;
|
|
if (charge_balance_unknown == ph_unknown)
|
|
{
|
|
x[count_unknowns]->moles = solution_ptr->Get_cb();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error_msg("Charge balance specified for more"
|
|
" than one species.", CONTINUE);
|
|
input_error++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Solution phase boundaries
|
|
*/
|
|
int l;
|
|
phase_ptr = phase_bsearch(comp_ptr->Get_equation_name().c_str(), &l, FALSE);
|
|
if (phase_ptr == NULL)
|
|
{
|
|
error_string = sformatf( "Expected a mineral name, %s.",
|
|
comp_ptr->Get_equation_name().c_str());
|
|
error_msg(error_string, CONTINUE);
|
|
input_error++;
|
|
}
|
|
x[count_unknowns]->type = SOLUTION_PHASE_BOUNDARY;
|
|
x[count_unknowns]->phase = phase_ptr;
|
|
x[count_unknowns]->si = comp_ptr->Get_phase_si();
|
|
/* For Peng-Robinson gas, the fugacity
|
|
coefficient is added later in adjust_setup_solution,
|
|
when rxn_x has been defined for each phase in the model */
|
|
if (solution_phase_boundary_unknown == NULL)
|
|
{
|
|
solution_phase_boundary_unknown = x[count_unknowns];
|
|
}
|
|
}
|
|
}
|
|
count_unknowns++;
|
|
}
|
|
/*
|
|
* Set mb_unknown
|
|
*/
|
|
if (count_unknowns > 0)
|
|
mb_unknown = x[0];
|
|
/*
|
|
* Special for alkalinity
|
|
*/
|
|
if (alkalinity_unknown != NULL)
|
|
{
|
|
if (carbon_unknown != NULL)
|
|
{
|
|
/*
|
|
* pH adjusted to obtain given alkalinity
|
|
*/
|
|
if (ph_unknown == NULL)
|
|
{
|
|
output_msg(sformatf("\npH will be adjusted to obtain desired alkalinity.\n\n"));
|
|
ph_unknown = alkalinity_unknown;
|
|
master_ptr = master_bsearch("H(1)");
|
|
alkalinity_unknown->master[0] = master_ptr;
|
|
master_ptr->in = TRUE;
|
|
master_ptr->unknown = ph_unknown;
|
|
ph_unknown->master[0] = master_ptr;
|
|
ph_unknown->description = string_hsave("H(1)");
|
|
}
|
|
else
|
|
{
|
|
error_msg("pH adjustment is needed for alkalinity but"
|
|
" charge balance or a phase boundary was also specified.",
|
|
CONTINUE);
|
|
input_error++;
|
|
}
|
|
/*
|
|
* Carbonate ion adjusted to obtain given alkalintiy
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
if (alkalinity_unknown->master[0]->s->secondary != NULL)
|
|
{
|
|
alkalinity_unknown->master[0]->s->secondary->in = TRUE;
|
|
alkalinity_unknown->master[0]->s->secondary->unknown =
|
|
alkalinity_unknown;
|
|
}
|
|
else
|
|
{
|
|
error_msg
|
|
("Error in definition of Alkalinity in SOLUTION_MASTER_SPECIES and SOLUTION_SPECIES.\n\tAlkalinity master species should be same as master species for C(4).",
|
|
CONTINUE);
|
|
input_error++;
|
|
}
|
|
}
|
|
}
|
|
//if (pitzer_model == FALSE && sit_model == FALSE)
|
|
{
|
|
/*
|
|
* Ionic strength
|
|
*/
|
|
mu_unknown = x[count_unknowns];
|
|
x[count_unknowns]->description = string_hsave("Mu");
|
|
x[count_unknowns]->type = MU;
|
|
x[count_unknowns]->number = count_unknowns;
|
|
x[count_unknowns]->moles = 0.0;
|
|
count_unknowns++;
|
|
}
|
|
/*
|
|
* Activity of water
|
|
*/
|
|
ah2o_unknown = x[count_unknowns];
|
|
ah2o_unknown->description = string_hsave("A(H2O)");
|
|
ah2o_unknown->type = AH2O;
|
|
ah2o_unknown->number = count_unknowns;
|
|
ah2o_unknown->master.push_back(master_bsearch("O"));
|
|
ah2o_unknown->master[0]->unknown = ah2o_unknown;
|
|
ah2o_unknown->moles = 0.0;
|
|
count_unknowns++;
|
|
|
|
if (state >= REACTION)
|
|
{
|
|
/*
|
|
|
|
* Reaction: pH for charge balance
|
|
*/
|
|
ph_unknown = x[count_unknowns];
|
|
ph_unknown->description = string_hsave("pH");
|
|
ph_unknown->type = CB;
|
|
ph_unknown->moles = solution_ptr->Get_cb();
|
|
ph_unknown->number = count_unknowns;
|
|
ph_unknown->master.push_back(s_hplus->primary);
|
|
ph_unknown->master[0]->unknown = ph_unknown;
|
|
charge_balance_unknown = ph_unknown;
|
|
count_unknowns++;
|
|
/*
|
|
* Reaction: pe for total hydrogen
|
|
*/
|
|
pe_unknown = x[count_unknowns];
|
|
mass_hydrogen_unknown = x[count_unknowns];
|
|
mass_hydrogen_unknown->description = string_hsave("Hydrogen");
|
|
mass_hydrogen_unknown->type = MH;
|
|
#ifdef COMBINE
|
|
mass_hydrogen_unknown->moles =
|
|
solution_ptr->Get_total_h() - 2 * solution_ptr->Get_total_o();
|
|
#else
|
|
mass_hydrogen_unknown->moles = solution_ptr->total_h;
|
|
#endif
|
|
mass_hydrogen_unknown->number = count_unknowns;
|
|
mass_hydrogen_unknown->master.push_back(s_eminus->primary);
|
|
mass_hydrogen_unknown->master[0]->unknown = mass_hydrogen_unknown;
|
|
count_unknowns++;
|
|
/*
|
|
* Reaction H2O for total oxygen
|
|
*/
|
|
mass_oxygen_unknown = x[count_unknowns];
|
|
mass_oxygen_unknown->description = string_hsave("Oxygen");
|
|
mass_oxygen_unknown->type = MH2O;
|
|
mass_oxygen_unknown->moles = solution_ptr->Get_total_o();
|
|
mass_oxygen_unknown->number = count_unknowns;
|
|
mass_oxygen_unknown->master.push_back(s_h2o->primary);
|
|
count_unknowns++;
|
|
}
|
|
/*
|
|
* Validity tests
|
|
*/
|
|
if ((ph_unknown != NULL) &&
|
|
(ph_unknown == charge_balance_unknown)
|
|
&& (alkalinity_unknown != NULL))
|
|
{
|
|
error_msg("pH adjustment cannot attain charge balance"
|
|
" when alkalinity is fixed.", CONTINUE);
|
|
input_error++;
|
|
}
|
|
if ((alkalinity_unknown != NULL) &&
|
|
(alkalinity_unknown->type == CB ||
|
|
alkalinity_unknown->type == SOLUTION_PHASE_BOUNDARY))
|
|
{
|
|
error_msg("Alkalinity cannot be used with charge balance"
|
|
" or solution phase boundary constraints.", CONTINUE);
|
|
input_error++;
|
|
}
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
adjust_setup_solution(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Fills in data in unknown structure for the solution
|
|
*/
|
|
int i;
|
|
class phase *phase_ptr;
|
|
LDBLE p, t;
|
|
|
|
for (i = 0; i < count_unknowns; i++)
|
|
{
|
|
std::vector<class phase *> phase_ptrs;
|
|
if (x[i]->type == SOLUTION_PHASE_BOUNDARY)
|
|
{
|
|
x[count_unknowns]->type = SOLUTION_PHASE_BOUNDARY;
|
|
phase_ptr = x[i]->phase;
|
|
phase_ptrs.push_back(phase_ptr);
|
|
if (phase_ptr->p_c > 0 && phase_ptr->t_c > 0)
|
|
{
|
|
if (x[i]->si > 3.5)
|
|
x[i]->si = 3.5;
|
|
p = exp(x[i]->si * LOG_10);
|
|
patm_x = p;
|
|
t = use.Get_solution_ptr()->Get_tc() + 273.15;
|
|
if (!phase_ptr->pr_in || p != phase_ptr->pr_p || t != phase_ptr->pr_tk)
|
|
{
|
|
calc_PR(phase_ptrs, p, t, 0);
|
|
}
|
|
x[i]->si += phase_ptr->pr_si_f;
|
|
}
|
|
}
|
|
}
|
|
return (OK);
|
|
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
setup_unknowns(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Counts unknowns and allocates space for unknown structures
|
|
*/
|
|
int i;
|
|
cxxSolution *solution_ptr;
|
|
|
|
solution_ptr = use.Get_solution_ptr();
|
|
/*
|
|
* Calculate maximum number of unknowns
|
|
*/
|
|
max_unknowns = 0;
|
|
/*
|
|
* Count mass balance in solution
|
|
*/
|
|
if (solution_ptr->Get_initial_data())
|
|
{
|
|
max_unknowns += (int) solution_ptr->Get_initial_data()->Get_comps().size();
|
|
}
|
|
else
|
|
{
|
|
max_unknowns += (int) solution_ptr->Get_totals().size();
|
|
}
|
|
/*
|
|
* Add 5 for ionic strength, activity of water, charge balance, total H, total O
|
|
*/
|
|
max_unknowns += 5;
|
|
/*
|
|
* Count pure phases
|
|
*/
|
|
if (use.Get_pp_assemblage_ptr() != NULL)
|
|
{
|
|
cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr();
|
|
max_unknowns += (int) pp_assemblage_ptr->Get_pp_assemblage_comps().size();
|
|
}
|
|
/*
|
|
* Count exchange
|
|
*/
|
|
if (use.Get_exchange_ptr() != NULL)
|
|
{
|
|
cxxExchange *exchange_ptr = use.Get_exchange_ptr();
|
|
for (size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++)
|
|
{
|
|
cxxExchComp & comp_ref = exchange_ptr->Get_exchange_comps()[j];
|
|
cxxNameDouble nd(comp_ref.Get_totals());
|
|
cxxNameDouble::iterator it = nd.begin();
|
|
for ( ; it != nd.end(); it++)
|
|
{
|
|
element * elt_ptr = element_store(it->first.c_str());
|
|
if (elt_ptr == NULL || elt_ptr->master == NULL)
|
|
{
|
|
error_string = sformatf(
|
|
"Master species missing for element %s",
|
|
it->first.c_str());
|
|
error_msg(error_string, STOP);
|
|
}
|
|
if (elt_ptr->master->type == EX)
|
|
{
|
|
max_unknowns++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Count surfaces
|
|
*/
|
|
if (use.Get_surface_ptr() != NULL)
|
|
{
|
|
if (use.Get_surface_ptr()->Get_type() != cxxSurface::CD_MUSIC)
|
|
{
|
|
max_unknowns += (int) use.Get_surface_ptr()->Get_surface_comps().size() +
|
|
(int) use.Get_surface_ptr()->Get_surface_charges().size();
|
|
}
|
|
else
|
|
{
|
|
max_unknowns += (int)(use.Get_surface_ptr()->Get_surface_comps().size() +
|
|
4 * use.Get_surface_ptr()->Get_surface_charges().size());
|
|
}
|
|
}
|
|
/*
|
|
* Count gas components
|
|
*/
|
|
if (use.Get_gas_phase_ptr() != NULL)
|
|
{
|
|
cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr();
|
|
if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME &&
|
|
(gas_phase_ptr->Get_pr_in() || force_numerical_fixed_volume) && numerical_fixed_volume)
|
|
{
|
|
max_unknowns += (int) gas_phase_ptr->Get_gas_comps().size();
|
|
}
|
|
else
|
|
{
|
|
max_unknowns++;
|
|
}
|
|
}
|
|
/*
|
|
* Count solid solutions
|
|
*/
|
|
if (use.Get_ss_assemblage_ptr() != NULL)
|
|
{
|
|
std::vector<cxxSS *> ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize();
|
|
for (size_t i = 0; i < ss_ptrs.size(); i++)
|
|
{
|
|
max_unknowns += (int) ss_ptrs[i]->Get_ss_comps().size();
|
|
}
|
|
}
|
|
/*
|
|
* Pitzer/Sit
|
|
*/
|
|
max_unknowns++;
|
|
if (pitzer_model == TRUE || sit_model == TRUE)
|
|
{
|
|
max_unknowns += (int)s.size();
|
|
}
|
|
|
|
/*
|
|
* Allocate space for pointer array and structures
|
|
*/
|
|
x.resize(max_unknowns);
|
|
for (i = 0; i < max_unknowns; i++)
|
|
{
|
|
x[i] = (class unknown *) unknown_alloc();
|
|
x[i]->number = i;
|
|
}
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
store_dn(int k, LDBLE * source, int row, LDBLE coef_in, LDBLE * gamma_source)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Stores the terms for d moles of species k in solution into row, multiplied
|
|
* by coef_in
|
|
*/
|
|
size_t col;
|
|
LDBLE coef;
|
|
class rxn_token *rxn_ptr;
|
|
class master *master_ptr;
|
|
|
|
if (equal(coef_in, 0.0, TOL) == TRUE)
|
|
{
|
|
return (OK);
|
|
}
|
|
/* Gamma term for d molality of species */
|
|
/* Note dg includes molality as a factor */
|
|
|
|
row = row * ((int)count_unknowns + 1);
|
|
if (s[k]->type != SURF && s[k] != s_h2o)
|
|
{
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d",
|
|
"Activity coefficient", (double) (-1.0 * coef_in),
|
|
row / (count_unknowns + 1), mu_unknown->number));
|
|
}
|
|
/* mu term */
|
|
if (gamma_source != NULL)
|
|
{
|
|
store_jacob(gamma_source, &my_array[(size_t)row + (size_t)mu_unknown->number],
|
|
-1.0 * coef_in);
|
|
}
|
|
}
|
|
/*
|
|
* Mass of water factor
|
|
*/
|
|
if (mass_oxygen_unknown != NULL && s[k]->type != EX && s[k]->type != SURF)
|
|
{
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d",
|
|
mass_oxygen_unknown->master[0]->s->name,
|
|
(double) coef_in, row / (count_unknowns + 1),
|
|
mass_oxygen_unknown->number));
|
|
}
|
|
store_jacob(source, &(my_array[(size_t)row + (size_t)mass_oxygen_unknown->number]),
|
|
coef_in);
|
|
}
|
|
if (s[k] == s_h2o)
|
|
return (OK);
|
|
for (rxn_ptr = &s[k]->rxn_x.token[0] + 1; rxn_ptr->s != NULL; rxn_ptr++)
|
|
{
|
|
if (rxn_ptr->s->secondary != NULL
|
|
&& rxn_ptr->s->secondary->in == TRUE)
|
|
{
|
|
master_ptr = rxn_ptr->s->secondary;
|
|
}
|
|
else
|
|
{
|
|
master_ptr = rxn_ptr->s->primary;
|
|
}
|
|
//if (debug_prep == TRUE)
|
|
//{
|
|
// output_msg(sformatf( "\t\t%s\n", master_ptr->s->name));
|
|
//}
|
|
if (master_ptr == NULL ||master_ptr->unknown == NULL)
|
|
continue;
|
|
col = master_ptr->unknown->number;
|
|
coef = coef_in * rxn_ptr->coef;
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf( "\t\t%-24s%10.3f\t%d\t%d",
|
|
master_ptr->s->name, (double) coef,
|
|
row / (count_unknowns + 1), col));
|
|
}
|
|
store_jacob(source, &(my_array[(size_t)row + (size_t)col]), coef);
|
|
}
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
store_jacob(LDBLE * source, LDBLE * target, LDBLE coef)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Adds a new item to either sum_jacob1 or sum_jacob2
|
|
* If coef is 1.0, adds to sum_jacob1, which does not require a multiply
|
|
* Otherwise, adds to sum_jacob2, which allows multiply by coef
|
|
*/
|
|
if (equal(coef, 1.0, TOL) == TRUE)
|
|
{
|
|
size_t count_sum_jacob1 = sum_jacob1.size();
|
|
sum_jacob1.resize(count_sum_jacob1 + 1);
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf( "\tjacob1 %d\n", (int)count_sum_jacob1));
|
|
}
|
|
sum_jacob1[count_sum_jacob1].source = source;
|
|
sum_jacob1[count_sum_jacob1].target = target;
|
|
}
|
|
else
|
|
{
|
|
size_t count_sum_jacob2 = sum_jacob2.size();
|
|
sum_jacob2.resize(count_sum_jacob2 + 1);
|
|
if (debug_prep == TRUE)
|
|
{
|
|
output_msg(sformatf("\tjacob2 %d\n", count_sum_jacob2));
|
|
}
|
|
sum_jacob2[count_sum_jacob2].source = source;
|
|
sum_jacob2[count_sum_jacob2].target = target;
|
|
sum_jacob2[count_sum_jacob2].coef = coef;
|
|
}
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
store_jacob0(int row, int column, LDBLE coef)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Stores in list a constant coef which will be added into jacobian array
|
|
*/
|
|
size_t count_sum_jacob0 = sum_jacob0.size();
|
|
sum_jacob0.resize(count_sum_jacob0 + 1);
|
|
sum_jacob0[count_sum_jacob0].target =
|
|
&(my_array[(size_t)row * (count_unknowns + 1) + (size_t)column]);
|
|
sum_jacob0[count_sum_jacob0].coef = coef;
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
store_mb(LDBLE * source, LDBLE * target, LDBLE coef)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Adds item to list sum_mb1 or sum_mb2
|
|
* If coef is 1.0, adds to sum_mb1, which does not require a multiply
|
|
* else, adds to sum_mb2, which will multiply by coef
|
|
*/
|
|
if (equal(coef, 1.0, TOL) == TRUE)
|
|
{
|
|
size_t count_sum_mb1 = sum_mb1.size();
|
|
sum_mb1.resize(count_sum_mb1 + 1);
|
|
sum_mb1[count_sum_mb1].source = source;
|
|
sum_mb1[count_sum_mb1].target = target;
|
|
}
|
|
else
|
|
{
|
|
size_t count_sum_mb2 = sum_mb2.size();
|
|
sum_mb2.resize(count_sum_mb2 + 1);
|
|
sum_mb2[count_sum_mb2].source = source;
|
|
sum_mb2[count_sum_mb2].coef = coef;
|
|
sum_mb2[count_sum_mb2].target = target;
|
|
}
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
store_sum_deltas(LDBLE * source, LDBLE * target, LDBLE coef)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* List sum_delta is summed to determine the change in the mass of
|
|
* each element due to mass transfers of minerals, changes show up
|
|
* in x[i]->delta. These may be multiplied by a factor under some
|
|
* situations where the entire calculated step is not taken
|
|
*/
|
|
size_t count_sum_delta = sum_delta.size();
|
|
sum_delta.resize(count_sum_delta + 1);
|
|
sum_delta[count_sum_delta].source = source;
|
|
sum_delta[count_sum_delta].target = target;
|
|
sum_delta[count_sum_delta].coef = coef;
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
switch_bases(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Check if activity of first master species is predominant among activities of
|
|
* secondary master species included in mass balance.
|
|
*/
|
|
int i;
|
|
int first;
|
|
int return_value;
|
|
LDBLE la, la1;
|
|
class master *master_ptr;
|
|
|
|
return_value = FALSE;
|
|
for (i = 0; i < count_unknowns; i++)
|
|
{
|
|
if (x[i]->type != MB)
|
|
continue;
|
|
if (x[i]->type == PITZER_GAMMA)
|
|
break;
|
|
first = 0;
|
|
la = x[i]->master[0]->s->la;
|
|
for (size_t j = 1; j < x[i]->master.size(); j++)
|
|
{
|
|
la1 = x[i]->master[j]->s->lm + x[i]->master[j]->s->lg;
|
|
if (first == 0 && la1 > la + 10.)
|
|
{
|
|
la = la1;
|
|
first = (int)j;
|
|
}
|
|
else if (first != 0 && la1 > la)
|
|
{
|
|
la = la1;
|
|
first = (int)j;
|
|
}
|
|
}
|
|
if (first != 0)
|
|
{
|
|
master_ptr = x[i]->master[0];
|
|
x[i]->master[0] = x[i]->master[first];
|
|
x[i]->master[0]->in = TRUE;
|
|
x[i]->master[first] = master_ptr;
|
|
x[i]->master[first]->in = REWRITE;
|
|
/*
|
|
fprintf(stderr, "Switching bases to %s.\tIteration %d\n",
|
|
x[i]->master[0]->s->name, iterations, la, x[i]->master[0]->s->la);
|
|
*/
|
|
x[i]->master[0]->s->la = la;
|
|
x[i]->la = la;
|
|
log_msg(sformatf( "Switching bases to %s.\tIteration %d\n",
|
|
x[i]->master[0]->s->name, iterations));
|
|
return_value = TRUE;
|
|
}
|
|
}
|
|
return (return_value);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
tidy_redox(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Write pe redox reactions (rxn in struct pe_data) in terms of master species
|
|
* defined in analytical data
|
|
*
|
|
*/
|
|
std::string token, tok1, tok2;
|
|
class master *master_ptr1, *master_ptr2;
|
|
/*
|
|
* Keep valences of oxygen and hydrogen in model, if not already in
|
|
*/
|
|
for (int i = 0; i < (int)master.size(); i++)
|
|
{
|
|
if (master[i]->primary == TRUE &&
|
|
(master[i]->s == s_hplus || master[i]->s == s_h2o))
|
|
{
|
|
int j = i + 1;
|
|
while (j < (int)master.size() && master[j]->elt->primary == master[i])
|
|
{
|
|
if (master[j]->in == FALSE && master[j]->s != master[i]->s)
|
|
{
|
|
master[j]->in = REWRITE;
|
|
master[j]->pe_rxn = master[i]->pe_rxn;
|
|
}
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Writes equations for e- for each redox couple used in solution n
|
|
*/
|
|
std::map < std::string, CReaction >::iterator it;
|
|
for (it = pe_x.begin(); it != pe_x.end(); it++)
|
|
{
|
|
if (strcmp_nocase(it->first.c_str(), "pe") == 0)
|
|
{
|
|
CReaction temp_rxn(s_eminus->rxn);
|
|
it->second = temp_rxn;
|
|
}
|
|
else
|
|
{
|
|
token = it->first;
|
|
replace("/", " ", token);
|
|
std::string::iterator b = token.begin();
|
|
std::string::iterator e = token.end();
|
|
|
|
/*
|
|
* Get redox states and elements from redox couple
|
|
*/
|
|
CParser::copy_token(tok1, b, e);
|
|
CParser::copy_token(tok2, b, e);
|
|
/*
|
|
* Find master species
|
|
*/
|
|
master_ptr1 = master_bsearch(tok1.c_str());
|
|
master_ptr2 = master_bsearch(tok2.c_str());
|
|
if (master_ptr1 != NULL && master_ptr2 != NULL)
|
|
{
|
|
rewrite_master_to_secondary(master_ptr1, master_ptr2);
|
|
/*
|
|
* Rewrite equation to e-
|
|
*/
|
|
trxn_swap("e-");
|
|
}
|
|
else
|
|
{
|
|
error_string = sformatf(
|
|
"Cannot find master species for redox couple, %s.",
|
|
it->first.c_str());
|
|
error_msg(error_string, STOP);
|
|
}
|
|
if (inout() == FALSE)
|
|
{
|
|
error_string = sformatf(
|
|
"Analytical data missing for redox couple, %s\n\t Using pe instead.",
|
|
it->first.c_str());
|
|
warning_msg(error_string);
|
|
CReaction temp_rxn(s_eminus->rxn);
|
|
it->second = temp_rxn;
|
|
}
|
|
else
|
|
{
|
|
CReaction rxn(count_trxn + 1);
|
|
trxn_copy(rxn);
|
|
CReaction temp_rxn(rxn);
|
|
it->second = temp_rxn;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Rewrite equations to master species that are "in" the model
|
|
*/
|
|
for (it = pe_x.begin(); it != pe_x.end(); it++)
|
|
{
|
|
count_trxn = 0;
|
|
trxn_add(it->second, 1.0, FALSE);
|
|
if (write_mass_action_eqn_x(CONTINUE) == FALSE)
|
|
{
|
|
error_string = sformatf( "Could not rewrite redox "
|
|
"couple equation for %s\n\t Possibly missing data for one "
|
|
"of the redox states.", it->first.c_str());
|
|
warning_msg(error_string);
|
|
error_string = sformatf( "Using pe instead of %s.",
|
|
it->first.c_str());
|
|
warning_msg(error_string);
|
|
CReaction temp_rxn(s_eminus->rxn);
|
|
it->second = temp_rxn;
|
|
}
|
|
else
|
|
{
|
|
CReaction rxn(count_trxn + 1);
|
|
trxn_copy(rxn);
|
|
CReaction temp_rxn(rxn);
|
|
it->second = temp_rxn;
|
|
}
|
|
}
|
|
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
write_mb_eqn_x(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
int count, repeat;
|
|
int i;
|
|
size_t count_rxn_orig;
|
|
class master *master_ptr;
|
|
/*
|
|
* Rewrite any secondary master species flagged REWRITE
|
|
* Don`t add in any pe reactions
|
|
*/
|
|
count = 0;
|
|
repeat = TRUE;
|
|
while (repeat == TRUE)
|
|
{
|
|
count++;
|
|
if (count > MAX_ADD_EQUATIONS)
|
|
{
|
|
std::string name;
|
|
name = "Unknown";
|
|
if (trxn.token[0].s != NULL)
|
|
{
|
|
name = trxn.token[0].s->name;
|
|
}
|
|
error_string = sformatf( "Could not reduce equation "
|
|
"to primary and secondary species that are "
|
|
"in the model. Species: %s.", name.c_str());
|
|
error_msg(error_string, CONTINUE);
|
|
return (ERROR);
|
|
}
|
|
repeat = FALSE;
|
|
count_rxn_orig = count_trxn;
|
|
for (i = 1; i < count_rxn_orig; i++)
|
|
{
|
|
if (trxn.token[i].s->secondary == NULL)
|
|
continue;
|
|
if (trxn.token[i].s->secondary->in == REWRITE)
|
|
{
|
|
repeat = TRUE;
|
|
trxn_add(trxn.token[i].s->secondary->rxn_secondary,
|
|
trxn.token[i].coef, false);
|
|
}
|
|
}
|
|
trxn_combine();
|
|
}
|
|
/*
|
|
*
|
|
*/
|
|
count_elts = 0;
|
|
paren_count = 0;
|
|
for (size_t i = 1; i < count_trxn; i++)
|
|
{
|
|
size_t j = count_elts;
|
|
const char* cptr = trxn.token[i].s->name;
|
|
get_elts_in_species(&cptr, trxn.token[i].coef);
|
|
for (size_t k = j; k < count_elts; k++)
|
|
{
|
|
if (trxn.token[i].s->secondary != NULL)
|
|
{
|
|
master_ptr = trxn.token[i].s->secondary->elt->primary;
|
|
}
|
|
else
|
|
{
|
|
master_ptr = trxn.token[i].s->primary;
|
|
}
|
|
if (elt_list[k].elt == master_ptr->elt)
|
|
{
|
|
elt_list[k].coef = 0.0;
|
|
break;
|
|
}
|
|
}
|
|
if (trxn.token[i].s->secondary == NULL)
|
|
{
|
|
const char* cptr = trxn.token[i].s->primary->elt->name;
|
|
get_secondary_in_species(&cptr, trxn.token[i].coef);
|
|
}
|
|
else
|
|
{
|
|
cptr = trxn.token[i].s->secondary->elt->name;
|
|
get_secondary_in_species(&cptr, trxn.token[i].coef);
|
|
}
|
|
}
|
|
elt_list_combine();
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
write_mb_for_species_list(int n)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Sets up data to add to species_list
|
|
* Original secondary redox states are retained
|
|
*/
|
|
int i;
|
|
/*
|
|
* Start with secondary reaction
|
|
*/
|
|
count_trxn = 0;
|
|
trxn_add(s[n]->rxn_s, 1.0, false);
|
|
/*
|
|
* Copy to elt_list
|
|
*/
|
|
count_elts = 0;
|
|
paren_count = 0;
|
|
for (i = 1; i < count_trxn; i++)
|
|
{
|
|
if (trxn.token[i].s->secondary == NULL)
|
|
{
|
|
const char* cptr = trxn.token[i].s->primary->elt->name;
|
|
get_secondary_in_species(&cptr, trxn.token[i].coef);
|
|
}
|
|
else
|
|
{
|
|
const char* cptr = trxn.token[i].s->secondary->elt->name;
|
|
if (get_secondary_in_species(&cptr, trxn.token[i].coef) == ERROR)
|
|
{
|
|
input_error++;
|
|
error_string = sformatf( "Error parsing %s.", trxn.token[i].s->secondary->elt->name);
|
|
error_msg(error_string, CONTINUE);
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < count_elts; i++)
|
|
{
|
|
if (strcmp(elt_list[i].elt->name, "O(-2)") == 0)
|
|
{
|
|
if (count_elts >= (int)elt_list.size())
|
|
{
|
|
elt_list.resize(count_elts + 1);
|
|
}
|
|
elt_list[count_elts].elt = element_h_one;
|
|
elt_list[count_elts].coef = elt_list[i].coef * 2;
|
|
count_elts++;
|
|
}
|
|
}
|
|
elt_list_combine();
|
|
s[n]->next_sys_total.clear();
|
|
s[n]->next_sys_total = elt_list_vsave();
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
write_phase_sys_total(int n)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Sets up data to add to species_list
|
|
* Original secondary redox states are retained
|
|
*/
|
|
int i;
|
|
/*
|
|
* Start with secondary reaction
|
|
*/
|
|
count_trxn = 0;
|
|
trxn_add_phase(phases[n]->rxn_s, 1.0, false);
|
|
/*
|
|
* Copy to elt_list
|
|
*/
|
|
count_elts = 0;
|
|
paren_count = 0;
|
|
for (i = 1; i < count_trxn; i++)
|
|
{
|
|
if (trxn.token[i].s->secondary == NULL)
|
|
{
|
|
const char* cptr = trxn.token[i].s->primary->elt->name;
|
|
get_secondary_in_species(&cptr, trxn.token[i].coef);
|
|
}
|
|
else
|
|
{
|
|
const char* cptr = trxn.token[i].s->secondary->elt->name;
|
|
get_secondary_in_species(&cptr, trxn.token[i].coef);
|
|
}
|
|
}
|
|
for (i = 0; i < count_elts; i++)
|
|
{
|
|
if (strcmp(elt_list[i].elt->name, "O(-2)") == 0)
|
|
{
|
|
if (count_elts >= (int)elt_list.size())
|
|
{
|
|
elt_list.resize(count_elts + 1);
|
|
}
|
|
elt_list[count_elts].elt = element_h_one;
|
|
elt_list[count_elts].coef = elt_list[i].coef * 2;
|
|
count_elts++;
|
|
}
|
|
}
|
|
elt_list_combine();
|
|
phases[n]->next_sys_total.clear();
|
|
phases[n]->next_sys_total = elt_list_vsave();
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
LDBLE Phreeqc::
|
|
calc_lk_phase(phase *p_ptr, LDBLE TK, LDBLE pa)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* calculate log_k for a single phase, correct for pressure
|
|
* see calc_vm (below) for details.
|
|
*/
|
|
|
|
CReaction *r_ptr = (p_ptr->rxn_x.size() ? &p_ptr->rxn_x :\
|
|
(p_ptr->rxn_s.size() ? &p_ptr->rxn_s : NULL));
|
|
if (!r_ptr)
|
|
return 0.0;
|
|
if (!r_ptr->logk[vm0]) // in case Vm of the phase is 0...
|
|
return k_calc(r_ptr->logk, TK, pa * PASCAL_PER_ATM);
|
|
|
|
LDBLE tc = TK - 273.15;
|
|
LDBLE pb_s = 2600. + pa * 1.01325, TK_s = tc + 45.15, sqrt_mu = sqrt(mu_x);
|
|
LDBLE d_v = 0.0;
|
|
species * s_ptr;
|
|
|
|
for (size_t i = 0; r_ptr->token[i].name; i++)
|
|
{
|
|
if (!r_ptr->token[i].s)
|
|
continue;
|
|
s_ptr = r_ptr->token[i].s;
|
|
//if (!strcmp(s_ptr->name, "H+"))
|
|
if (s_ptr == s_hplus)
|
|
continue;
|
|
//if (!strcmp(s_ptr->name, "e-"))
|
|
if (s_ptr == s_eminus)
|
|
continue;
|
|
//if (!strcmp(s_ptr->name, "H2O"))
|
|
if (s_ptr == s_h2o)
|
|
{
|
|
d_v += r_ptr->token[i].coef * 18.016 / calc_rho_0(tc, pa);
|
|
continue;
|
|
}
|
|
else if (s_ptr->logk[vma1])
|
|
{
|
|
/* supcrt volume at I = 0... */
|
|
d_v += r_ptr->token[i].coef *
|
|
(s_ptr->logk[vma1] + s_ptr->logk[vma2] / pb_s +
|
|
(s_ptr->logk[vma3] + s_ptr->logk[vma4] / pb_s) / TK_s -
|
|
s_ptr->logk[wref] * QBrn);
|
|
//if (dgdP && s_ptr->z)
|
|
//{
|
|
// LDBLE re = s_ptr->z * s_ptr->z / (s_ptr->logk[wref] / 1.66027e5 + s_ptr->z / 3.082);
|
|
// LDBLE Z3 = fabs(pow(s_ptr->z, 3)) / re / re - s_ptr->z / 9.498724;
|
|
// d_v += r_ptr->token[i].coef * ZBrn * 1.66027e5 * Z3 * dgdP;
|
|
//}
|
|
if (s_ptr->z)
|
|
{
|
|
/* the ionic strength term * I^0.5... */
|
|
if (s_ptr->logk[b_Av] < 1e-5)
|
|
d_v += s_ptr->z * s_ptr->z * 0.5 * DH_Av * sqrt_mu;
|
|
else
|
|
{
|
|
/* limit the Debye-Hueckel slope by b... */
|
|
d_v += s_ptr->z * s_ptr->z * 0.5 * DH_Av *
|
|
sqrt_mu / (1 + s_ptr->logk[b_Av] * DH_B * sqrt_mu);
|
|
}
|
|
/* plus the volume terms * I... */
|
|
if (s_ptr->logk[vmi1] != 0.0 || s_ptr->logk[vmi2] != 0.0 || s_ptr->logk[vmi3] != 0.0)
|
|
{
|
|
LDBLE bi = s_ptr->logk[vmi1] + s_ptr->logk[vmi2] / TK_s + s_ptr->logk[vmi3] * TK_s;
|
|
if (s_ptr->logk[vmi4] == 1.0)
|
|
d_v += bi * mu_x;
|
|
else
|
|
d_v += bi * pow(mu_x, s_ptr->logk[vmi4]);
|
|
}
|
|
}
|
|
}
|
|
//else if (s_x[i]->millero[0])
|
|
else if (s_ptr->millero[0])
|
|
{
|
|
/* Millero volume at I = 0... */
|
|
d_v += s_ptr->millero[0] + tc * (s_ptr->millero[1] + tc * s_ptr->millero[2]);
|
|
if (s_ptr->z)
|
|
{
|
|
/* the ionic strength terms... */
|
|
d_v += s_ptr->z * s_ptr->z * 0.5 * DH_Av * sqrt_mu +
|
|
(s_ptr->millero[3] + tc * (s_ptr->millero[4] + tc * s_ptr->millero[5])) * mu_x;
|
|
}
|
|
}
|
|
else
|
|
continue;
|
|
}
|
|
d_v -= p_ptr->logk[vm0];
|
|
r_ptr->logk[delta_v] = d_v;
|
|
if (r_ptr->token[0].name && !strcmp(r_ptr->token[0].name, "H2O(g)"))
|
|
r_ptr->logk[delta_v] = 0.0;
|
|
|
|
return k_calc(r_ptr->logk, TK, pa * PASCAL_PER_ATM);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
calc_vm(LDBLE tc, LDBLE pa)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Calculate molar volumes for aqueous species with a Redlich type eqn:
|
|
Vm = Vm0(tc) + (Av / 2) * z^2 * I^0.5 + coef(tc) * I^(b4).
|
|
* Vm0(tc) is calc'd using supcrt parms, or from millero[0] + millero[1] * tc + millero[2] * tc^2
|
|
* for Av * z^2 * I^0.5, see Redlich and Meyer, Chem. Rev. 64, 221.
|
|
Av is in (cm3/mol)(mol/kg)^-0.5, = DH_Av.
|
|
If b_Av != 0, the extended DH formula is used: I^0.5 /(1 + b_Av * DH_B * I^0.5).
|
|
DH_Av and DH_B are from calc_dielectrics(tc, pa).
|
|
* coef(tc) = logk[vmi1] + logk[vmi2] / (TK - 228) + logk[vmi3] * (TK - 228).
|
|
* b4 = logk[vmi4], or
|
|
* coef(tc) = millero[3] + millero[4] * tc + millero[5] * tc^2
|
|
*/
|
|
if (llnl_temp.size() > 0) return OK;
|
|
LDBLE pb_s = 2600. + pa * 1.01325, TK_s = tc + 45.15, sqrt_mu = sqrt(mu_x);
|
|
for (int i = 0; i < (int)this->s_x.size(); i++)
|
|
{
|
|
//if (!strcmp(s_x[i]->name, "H2O"))
|
|
if (s_x[i] == s_h2o)
|
|
{
|
|
s_x[i]->logk[vm_tc] = 18.016 / rho_0;
|
|
continue;
|
|
}
|
|
if (s_x[i]->logk[vma1])
|
|
{
|
|
/* supcrt volume at I = 0... */
|
|
s_x[i]->rxn_x.logk[vm_tc] = s_x[i]->logk[vma1] + s_x[i]->logk[vma2] / pb_s +
|
|
(s_x[i]->logk[vma3] + s_x[i]->logk[vma4] / pb_s) / TK_s -
|
|
s_x[i]->logk[wref] * QBrn;
|
|
/* A (small) correction by Shock et al., 1992, for 155 < tc < 255, P_sat < P < 1e3.
|
|
The vma1..a4 and wref numbers are refitted for major cations and anions on xpts,
|
|
probably invalidates the correction. */
|
|
//if (dgdP && s_x[i]->z)
|
|
//{
|
|
// LDBLE re = s_x[i]->z * s_x[i]->z / (s_x[i]->logk[wref] / 1.66027e5 + s_x[i]->z / 3.082);
|
|
// LDBLE Z3 = fabs(pow(s_x[i]->z, 3)) / re / re - s_x[i]->z / 9.498724;
|
|
// s_x[i]->rxn_x.logk[vm_tc] += ZBrn * 1.66027e5 * Z3 * dgdP;
|
|
//}
|
|
if (s_x[i]->z)
|
|
{
|
|
/* the ionic strength term * I^0.5... */
|
|
if (s_x[i]->logk[b_Av] < 1e-5)
|
|
s_x[i]->rxn_x.logk[vm_tc] += s_x[i]->z * s_x[i]->z * 0.5 * DH_Av * sqrt_mu;
|
|
else
|
|
{
|
|
/* limit the Debye-Hueckel slope by b... */
|
|
/* pitzer... */
|
|
//s_x[i]->rxn_x.logk[vm_tc] += s_x[i]->z * s_x[i]->z * 0.5 * DH_Av *
|
|
// log(1 + s_x[i]->logk[b_Av] * sqrt(mu_x)) / s_x[i]->logk[b_Av];
|
|
/* extended DH... */
|
|
s_x[i]->rxn_x.logk[vm_tc] += s_x[i]->z * s_x[i]->z * 0.5 * DH_Av *
|
|
sqrt_mu / (1 + s_x[i]->logk[b_Av] * DH_B * sqrt_mu);
|
|
}
|
|
/* plus the volume terms * I... */
|
|
if (s_x[i]->logk[vmi1] != 0.0 || s_x[i]->logk[vmi2] != 0.0 || s_x[i]->logk[vmi3] != 0.0)
|
|
{
|
|
LDBLE bi = s_x[i]->logk[vmi1] + s_x[i]->logk[vmi2] / TK_s + s_x[i]->logk[vmi3] * TK_s;
|
|
if (s_x[i]->logk[vmi4] == 1.0)
|
|
s_x[i]->rxn_x.logk[vm_tc] += bi * mu_x;
|
|
else
|
|
s_x[i]->rxn_x.logk[vm_tc] += bi * pow(mu_x, s_x[i]->logk[vmi4]);
|
|
}
|
|
}
|
|
}
|
|
else if (s_x[i]->millero[0])
|
|
{
|
|
/* Millero volume at I = 0... */
|
|
s_x[i]->rxn_x.logk[vm_tc] = s_x[i]->millero[0] + tc * (s_x[i]->millero[1] + tc * s_x[i]->millero[2]);
|
|
if (s_x[i]->z)
|
|
{
|
|
/* the ionic strength terms... */
|
|
s_x[i]->rxn_x.logk[vm_tc] += s_x[i]->z * s_x[i]->z * 0.5 * DH_Av * sqrt_mu +
|
|
(s_x[i]->millero[3] + tc * (s_x[i]->millero[4] + tc * s_x[i]->millero[5])) * mu_x;
|
|
}
|
|
}
|
|
else
|
|
continue;
|
|
|
|
/* for calculating delta_v of the reaction... */
|
|
s_x[i]->logk[vm_tc] = s_x[i]->rxn_x.logk[vm_tc];
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
LDBLE Phreeqc::calc_vm0(const char * species_name, LDBLE tc, LDBLE pa, LDBLE mu)
|
|
{
|
|
/*
|
|
* Calculate molar volume of an aqueous species at tc, pa and mu
|
|
*/
|
|
if (llnl_temp.size() > 0) return OK;
|
|
class species *s_ptr;
|
|
LDBLE g = 0;
|
|
s_ptr = s_search(species_name);
|
|
if (s_ptr == s_h2o)
|
|
return 18.016 / rho_0;
|
|
if (s_ptr != NULL && s_ptr->in != FALSE && s_ptr->type < EMINUS && s_ptr->logk[vma1])
|
|
{
|
|
LDBLE pb_s = 2600. + pa * 1.01325, TK_s = tc + 45.15, sqrt_mu = sqrt(mu);
|
|
/* supcrt volume at I = 0... */
|
|
g = s_ptr->logk[vma1] + s_ptr->logk[vma2] / pb_s +
|
|
(s_ptr->logk[vma3] + s_ptr->logk[vma4] / pb_s) / TK_s -
|
|
s_ptr->logk[wref] * QBrn;
|
|
if (s_ptr->z)
|
|
{
|
|
/* the ionic strength term * I^0.5... */
|
|
if (s_ptr->logk[b_Av] < 1e-5)
|
|
g += s_ptr->z * s_ptr->z * 0.5 * DH_Av * sqrt_mu;
|
|
else
|
|
{
|
|
/* limit the Debye-Hueckel slope by b... */
|
|
/* pitzer... */
|
|
//s_ptr->rxn_x.logk[vm_tc] += s_ptr->z * s_ptr->z * 0.5 * DH_Av *
|
|
// log(1 + s_ptr->logk[b_Av] * sqrt(mu_x)) / s_ptr->logk[b_Av];
|
|
/* extended DH... */
|
|
g += s_ptr->z * s_ptr->z * 0.5 * DH_Av *
|
|
sqrt_mu / (1 + s_ptr->logk[b_Av] * DH_B * sqrt_mu);
|
|
}
|
|
/* plus the volume terms * I... */
|
|
if (s_ptr->logk[vmi1] != 0.0 || s_ptr->logk[vmi2] != 0.0 || s_ptr->logk[vmi3] != 0.0)
|
|
{
|
|
LDBLE bi = s_ptr->logk[vmi1] + s_ptr->logk[vmi2] / TK_s + s_ptr->logk[vmi3] * TK_s;
|
|
if (s_ptr->logk[vmi4] == 1.0)
|
|
g += bi * mu;
|
|
else
|
|
g += bi * pow(mu, s_ptr->logk[vmi4]);
|
|
}
|
|
}
|
|
}
|
|
return g;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
k_temp(LDBLE tc, LDBLE pa) /* pa - pressure in atm */
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Calculates log k's for all species and pure_phases
|
|
*/
|
|
|
|
// if (tc == current_tc && pa == current_pa && ((fabs(mu_x - current_mu) < 1e-3 * mu_x) || !mu_terms_in_logk))
|
|
// return OK;
|
|
if (tc != current_tc) goto proceed;
|
|
if (pa != current_pa) goto proceed;
|
|
if (fabs(mu_x - current_mu) > 1e-3 * mu_x) goto proceed;
|
|
if (mu_terms_in_logk) goto proceed;
|
|
return OK;
|
|
|
|
proceed:
|
|
|
|
int i;
|
|
LDBLE tempk = tc + 273.15;
|
|
/*
|
|
* Calculate log k for all aqueous species
|
|
*/
|
|
/* calculate relative molar volumes for tc... */
|
|
rho_0 = calc_rho_0(tc, pa);
|
|
|
|
pa = patm_x;
|
|
calc_dielectrics(tc, pa);
|
|
|
|
calc_vm(tc, pa);
|
|
|
|
mu_terms_in_logk = false;
|
|
for (i = 0; i < (int)this->s_x.size(); i++)
|
|
{
|
|
//if (s_x[i]->rxn_x.logk[vm_tc])
|
|
/* calculate delta_v for the reaction... */
|
|
s_x[i]->rxn_x.logk[delta_v] = calc_delta_v(*&s_x[i]->rxn_x, false);
|
|
if (tc == current_tc && s_x[i]->rxn_x.logk[delta_v] == 0)
|
|
continue;
|
|
mu_terms_in_logk = true;
|
|
s_x[i]->lk = k_calc(s_x[i]->rxn_x.logk, tempk, pa * PASCAL_PER_ATM);
|
|
}
|
|
/*
|
|
* Calculate log k for all pure phases
|
|
*/
|
|
for (i = 0; i < (int)phases.size(); i++)
|
|
{
|
|
if (phases[i]->in == TRUE)
|
|
{
|
|
|
|
phases[i]->rxn_x.logk[delta_v] = calc_delta_v(*&phases[i]->rxn_x, true) -
|
|
phases[i]->logk[vm0];
|
|
if (phases[i]->rxn_x.logk[delta_v])
|
|
mu_terms_in_logk = true;
|
|
phases[i]->lk = k_calc(phases[i]->rxn_x.logk, tempk, pa * PASCAL_PER_ATM);
|
|
|
|
}
|
|
}
|
|
/*
|
|
* Calculate miscibility gaps for solid solutions
|
|
*/
|
|
if (use.Get_ss_assemblage_ptr() != NULL)
|
|
{
|
|
std::vector<cxxSS *> ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize();
|
|
for (size_t i = 0; i < ss_ptrs.size(); i++)
|
|
{
|
|
if (fabs(tempk - ss_ptrs[i]->Get_tk()) > 0.01)
|
|
{
|
|
ss_prep(tempk, ss_ptrs[i], FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
current_tc = tc;
|
|
current_pa = pa;
|
|
current_mu = mu_x;
|
|
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
LDBLE Phreeqc::
|
|
k_calc(LDBLE * l_logk, LDBLE tempk, LDBLE presPa)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Calculates log k at specified temperature and pressure
|
|
* Returns calculated log k.
|
|
*
|
|
* delta_v is in cm3/mol.
|
|
*/
|
|
|
|
/* Molar energy */
|
|
LDBLE me = tempk * R_KJ_DEG_MOL;
|
|
|
|
/* Pressure difference */
|
|
LDBLE delta_p = presPa - REF_PRES_PASCAL;
|
|
|
|
/* Calculate new log k value for this temperature and pressure */
|
|
LDBLE lk = l_logk[logK_T0]
|
|
- l_logk[delta_h] * (298.15 - tempk) / (LOG_10 * me * 298.15)
|
|
+ l_logk[T_A1]
|
|
+ l_logk[T_A2] * tempk
|
|
+ l_logk[T_A3] / tempk
|
|
+ l_logk[T_A4] * log10(tempk)
|
|
+ l_logk[T_A5] / (tempk * tempk)
|
|
+ l_logk[T_A6] * tempk * tempk;
|
|
if (delta_p > 0)
|
|
/* cm3 * J /mol = 1e-9 m3 * kJ /mol */
|
|
lk -= l_logk[delta_v] * 1E-9 * delta_p / (LOG_10 * me);
|
|
return lk;
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
save_model(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
int i;
|
|
/*
|
|
* mark master species
|
|
*/
|
|
for (i = 0; i < (int)master.size(); i++)
|
|
{
|
|
master[i]->last_model = FALSE;
|
|
if (master[i]->total > 0)
|
|
{
|
|
if (master[i]->primary == TRUE)
|
|
{
|
|
master[i]->last_model = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* mark primary master */
|
|
master[i]->s->secondary->elt->primary->last_model = TRUE;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* save list of phase pointers for gas phase
|
|
*/
|
|
if (use.Get_gas_phase_ptr() != NULL)
|
|
{
|
|
cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr();
|
|
last_model.gas_phase_type = gas_phase_ptr->Get_type();
|
|
last_model.gas_phase.resize(gas_phase_ptr->Get_gas_comps().size());
|
|
for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++)
|
|
{
|
|
cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]);
|
|
int k;
|
|
class phase *phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str() , &k, FALSE);
|
|
assert(phase_ptr);
|
|
last_model.gas_phase[i] = phase_ptr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
last_model.gas_phase_type = cxxGasPhase::GP_UNKNOWN;
|
|
last_model.gas_phase.clear();
|
|
}
|
|
/*
|
|
* save list of names of solid solutions
|
|
*/
|
|
if (use.Get_ss_assemblage_ptr() != NULL)
|
|
{
|
|
last_model.ss_assemblage.resize(use.Get_ss_assemblage_ptr()->Get_SSs().size());
|
|
std::vector<cxxSS *> ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize();
|
|
for (size_t j = 0; j < ss_ptrs.size(); j++)
|
|
{
|
|
last_model.ss_assemblage[j] = string_hsave(ss_ptrs[j]->Get_name().c_str());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
last_model.ss_assemblage.clear();
|
|
}
|
|
/*
|
|
* save list of phase pointers for pp_assemblage
|
|
*/
|
|
if (use.Get_pp_assemblage_ptr() != NULL)
|
|
{
|
|
cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr();
|
|
last_model.pp_assemblage.resize(pp_assemblage_ptr->Get_pp_assemblage_comps().size());
|
|
last_model.add_formula.resize(pp_assemblage_ptr->Get_pp_assemblage_comps().size());
|
|
last_model.si.resize(pp_assemblage_ptr->Get_pp_assemblage_comps().size());
|
|
std::map<std::string, cxxPPassemblageComp>::iterator it;
|
|
it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin();
|
|
i = 0;
|
|
for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++)
|
|
{
|
|
int j;
|
|
class phase * phase_ptr = phase_bsearch(it->first.c_str(), &j, false);
|
|
assert(phase_ptr);
|
|
last_model.pp_assemblage[i] = phase_ptr;
|
|
last_model.add_formula[i] = string_hsave(it->second.Get_add_formula().c_str());
|
|
last_model.si[i] = it->second.Get_si();
|
|
i++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
last_model.pp_assemblage.clear();
|
|
last_model.add_formula.clear();
|
|
last_model.si.clear();
|
|
}
|
|
/*
|
|
* save data for surface
|
|
*/
|
|
if (use.Get_surface_ptr() != NULL)
|
|
{
|
|
/* comps */
|
|
last_model.surface_comp.resize(use.Get_surface_ptr()->Get_surface_comps().size());
|
|
for (i = 0; i < (int) use.Get_surface_ptr()->Get_surface_comps().size(); i++)
|
|
{
|
|
last_model.surface_comp[i] = string_hsave(use.Get_surface_ptr()->Get_surface_comps()[i].Get_formula().c_str());
|
|
}
|
|
/* charge */
|
|
last_model.surface_charge.resize(use.Get_surface_ptr()->Get_surface_charges().size());
|
|
for (i = 0; i < (int) use.Get_surface_ptr()->Get_surface_charges().size(); i++)
|
|
{
|
|
last_model.surface_charge[i] = string_hsave(use.Get_surface_ptr()->Get_surface_charges()[i].Get_name().c_str());
|
|
}
|
|
last_model.dl_type = use.Get_surface_ptr()->Get_dl_type();
|
|
/*last_model.edl = use.Get_surface_ptr()->edl; */
|
|
last_model.surface_type = use.Get_surface_ptr()->Get_type();
|
|
}
|
|
else
|
|
{
|
|
last_model.dl_type = cxxSurface::NO_DL;
|
|
/*last_model.edl = -1; */
|
|
last_model.surface_type = cxxSurface::UNKNOWN_DL;
|
|
last_model.surface_comp.clear();
|
|
last_model.surface_charge.clear();
|
|
}
|
|
|
|
current_tc = NAN;
|
|
current_pa = NAN;
|
|
current_mu = NAN;
|
|
mu_terms_in_logk = true;
|
|
|
|
last_model.numerical_fixed_volume = numerical_fixed_volume;
|
|
|
|
return (OK);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
check_same_model(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
int i;
|
|
/*
|
|
* Force new model to be built in prep
|
|
*/
|
|
if (last_model.force_prep)
|
|
{
|
|
last_model.force_prep = false;
|
|
return (FALSE);
|
|
}
|
|
if (state == TRANSPORT && cell_data[cell_no].same_model)
|
|
return TRUE;
|
|
/*
|
|
* Check master species
|
|
*/
|
|
for (i = 0; i < (int)master.size(); i++)
|
|
{
|
|
/*
|
|
output_msg(sformatf("%s\t%e\t%d\n", master[i]->elt->name,
|
|
master[i]->total, master[i]->last_model);
|
|
*/
|
|
if (master[i]->s == s_hplus || master[i]->s == s_h2o)
|
|
continue;
|
|
if (master[i]->total > MIN_TOTAL && master[i]->last_model == TRUE)
|
|
{
|
|
if (master[i]->s->secondary != NULL)
|
|
{
|
|
if (master[i]->s->secondary->unknown != NULL)
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (master[i]->unknown != NULL)
|
|
continue;
|
|
}
|
|
}
|
|
if (master[i]->total <= MIN_TOTAL && master[i]->last_model == FALSE)
|
|
continue;
|
|
return (FALSE);
|
|
}
|
|
/*
|
|
* Check gas_phase
|
|
*/
|
|
if (use.Get_gas_phase_ptr() != NULL)
|
|
{
|
|
cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr();
|
|
if (last_model.gas_phase.size() != (int)gas_phase_ptr->Get_gas_comps().size())
|
|
return (FALSE);
|
|
if (last_model.numerical_fixed_volume != numerical_fixed_volume)
|
|
return (FALSE);
|
|
if (last_model.gas_phase_type != gas_phase_ptr->Get_type())
|
|
return (FALSE);
|
|
for (i = 0; i < (int) gas_phase_ptr->Get_gas_comps().size(); i++)
|
|
{
|
|
cxxGasComp *gc_ptr = &(gas_phase_ptr->Get_gas_comps()[i]);
|
|
int k;
|
|
class phase *phase_ptr = phase_bsearch(gc_ptr->Get_phase_name().c_str() , &k, FALSE);
|
|
assert(phase_ptr);
|
|
if (last_model.gas_phase[i] != phase_ptr)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (last_model.gas_phase.size() > 0)
|
|
return (FALSE);
|
|
}
|
|
/*
|
|
* Check solid solutions
|
|
*/
|
|
if (use.Get_ss_assemblage_ptr() != NULL)
|
|
{
|
|
if (last_model.ss_assemblage.size() != (int) use.Get_ss_assemblage_ptr()->Get_SSs().size())
|
|
return (FALSE);
|
|
std::vector<cxxSS *> ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize();
|
|
for (size_t i = 0; i < ss_ptrs.size(); i++)
|
|
{
|
|
if (last_model.ss_assemblage[i] != string_hsave(ss_ptrs[i]->Get_name().c_str()))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (last_model.ss_assemblage.size() > 0)
|
|
return (FALSE);
|
|
}
|
|
/*
|
|
* Check pure_phases
|
|
*/
|
|
if (use.Get_pp_assemblage_ptr() != NULL)
|
|
{
|
|
cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr();
|
|
if (last_model.pp_assemblage.size() != (int) pp_assemblage_ptr->Get_pp_assemblage_comps().size())
|
|
return (FALSE);
|
|
|
|
std::map<std::string, cxxPPassemblageComp>::iterator it;
|
|
it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin();
|
|
i = 0;
|
|
for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++)
|
|
{
|
|
int j;
|
|
class phase * phase_ptr = phase_bsearch(it->first.c_str(), &j, FALSE);
|
|
assert(phase_ptr);
|
|
if (last_model.pp_assemblage[i] != phase_ptr)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
if (last_model.add_formula[i] !=
|
|
string_hsave(it->second.Get_add_formula().c_str()))
|
|
{
|
|
return (FALSE);
|
|
}
|
|
i++;
|
|
/* A. Crapsi
|
|
if (last_model.si[i] != use.Get_pp_assemblage_ptr()->pure_phases[i].si)
|
|
{
|
|
return (FALSE);
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (last_model.pp_assemblage.size() > 0)
|
|
return (FALSE);
|
|
}
|
|
/*
|
|
* Check surface
|
|
*/
|
|
if (use.Get_surface_ptr() != NULL)
|
|
{
|
|
if (last_model.surface_comp.size() != (int) use.Get_surface_ptr()->Get_surface_comps().size())
|
|
return (FALSE);
|
|
if (last_model.surface_charge.size() != (int) use.Get_surface_ptr()->Get_surface_charges().size())
|
|
return (FALSE);
|
|
if (last_model.dl_type != use.Get_surface_ptr()->Get_dl_type())
|
|
return (FALSE);
|
|
/*if (last_model.edl != use.Get_surface_ptr()->edl) return(FALSE); */
|
|
if (last_model.surface_type != use.Get_surface_ptr()->Get_type())
|
|
return (FALSE);
|
|
/*
|
|
if (last_model.only_counter_ions != use.Get_surface_ptr()->only_counter_ions) return(FALSE);
|
|
*/
|
|
for (i = 0; i < (int) use.Get_surface_ptr()->Get_surface_comps().size(); i++)
|
|
{
|
|
if (last_model.surface_comp[i] !=
|
|
string_hsave(use.Get_surface_ptr()->Get_surface_comps()[i].Get_formula().c_str()))
|
|
return (FALSE);
|
|
if (use.Get_surface_ptr()->Get_surface_comps()[i].Get_phase_name().size() > 0)
|
|
{
|
|
cxxPPassemblage *pp_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, use.Get_n_surface_user());
|
|
if (pp_ptr == NULL || (pp_ptr->Get_pp_assemblage_comps().find(use.Get_surface_ptr()->Get_surface_comps()[i].Get_phase_name()) ==
|
|
pp_ptr->Get_pp_assemblage_comps().end()))
|
|
{
|
|
Rxn_new_surface.insert(use.Get_n_surface_user());
|
|
cxxSurface *surf_ptr = Utilities::Rxn_find(Rxn_surface_map, use.Get_n_surface_user());
|
|
surf_ptr->Set_new_def(true);
|
|
this->tidy_min_surface();
|
|
return (FALSE);
|
|
}
|
|
}
|
|
if (use.Get_surface_ptr()->Get_surface_comps()[i].Get_rate_name().size() > 0)
|
|
{
|
|
cxxKinetics *kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, use.Get_n_surface_user());
|
|
if (kinetics_ptr == NULL ||
|
|
(kinetics_ptr->Find(use.Get_surface_ptr()->Get_surface_comps()[i].Get_rate_name()) == NULL))
|
|
{
|
|
Rxn_new_surface.insert(use.Get_n_surface_user());
|
|
cxxSurface *surf_ptr = Utilities::Rxn_find(Rxn_surface_map, use.Get_n_surface_user());
|
|
surf_ptr->Set_new_def(true);
|
|
this->tidy_kin_surface();
|
|
return (FALSE);
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < (int) use.Get_surface_ptr()->Get_surface_charges().size(); i++)
|
|
{
|
|
if (last_model.surface_charge[i] !=
|
|
string_hsave(use.Get_surface_ptr()->Get_surface_charges()[i].Get_name().c_str()))
|
|
return (FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (last_model.surface_comp.size() > 0)
|
|
return (FALSE);
|
|
}
|
|
/*
|
|
* Model is the same
|
|
*/
|
|
return (TRUE);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
build_min_exch(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Defines proportionality factor between mineral and exchanger to
|
|
* jacob0
|
|
*/
|
|
int j, k, jj;
|
|
size_t row;
|
|
class master *master_ptr;
|
|
class unknown *unknown_ptr;
|
|
LDBLE coef;
|
|
|
|
if (use.Get_exchange_ptr() == NULL)
|
|
return (OK);
|
|
cxxExchange *ex_ptr = use.Get_exchange_ptr();
|
|
int n_user = ex_ptr->Get_n_user();
|
|
cxxExchange * exchange_ptr = Utilities::Rxn_find(Rxn_exchange_map, n_user);
|
|
if (exchange_ptr == NULL)
|
|
{
|
|
input_error++;
|
|
error_string = sformatf( "Exchange %d not found.",
|
|
use.Get_n_exchange_user());
|
|
error_msg(error_string, CONTINUE);
|
|
return ERROR;
|
|
}
|
|
n_user = exchange_ptr->Get_n_user();
|
|
if (!exchange_ptr->Get_related_phases())
|
|
return (OK);
|
|
for (size_t i = 0; i < exchange_ptr->Get_exchange_comps().size(); i++)
|
|
{
|
|
cxxExchComp & comp_ref = exchange_ptr->Get_exchange_comps()[i];
|
|
if (comp_ref.Get_phase_name().size() == 0)
|
|
continue;
|
|
// Find exchange master
|
|
cxxNameDouble nd(comp_ref.Get_totals());
|
|
cxxNameDouble::iterator it = nd.begin();
|
|
class master *exchange_master = NULL;
|
|
for ( ; it != nd.end(); it++)
|
|
{
|
|
element * elt_ptr = element_store(it->first.c_str());
|
|
assert (elt_ptr);
|
|
if (elt_ptr->master->type == EX)
|
|
{
|
|
exchange_master = elt_ptr->master;
|
|
}
|
|
}
|
|
if (exchange_master == NULL)
|
|
{
|
|
input_error++;
|
|
error_string = sformatf(
|
|
"Did not find master exchange species for %s",
|
|
comp_ref.Get_formula().c_str());
|
|
error_msg(error_string, CONTINUE);
|
|
continue;
|
|
}
|
|
/* find unknown number */
|
|
for (j = (int)count_unknowns - 1; j >= 0; j--)
|
|
{
|
|
if (x[j]->type != EXCH)
|
|
continue;
|
|
if (x[j]->master[0] == exchange_master)
|
|
break;
|
|
}
|
|
for (k = (int)count_unknowns - 1; k >= 0; k--)
|
|
{
|
|
if (x[k]->type != PP)
|
|
continue;
|
|
//if (x[k]->phase->name == string_hsave(comp_ref.Get_phase_name().c_str()))
|
|
if (strcmp_nocase(x[k]->phase->name, comp_ref.Get_phase_name().c_str()) == 0)
|
|
break;
|
|
}
|
|
if (j == -1)
|
|
{
|
|
input_error++;
|
|
error_string = sformatf(
|
|
"Did not find unknown for master exchange species %s",
|
|
exchange_master->s->name);
|
|
error_msg(error_string, CONTINUE);
|
|
}
|
|
if (j == -1 || k == -1)
|
|
continue;
|
|
/*
|
|
* Build jacobian
|
|
*/
|
|
|
|
/* charge balance */
|
|
store_jacob0((int)charge_balance_unknown->number, (int)x[k]->number,
|
|
comp_ref.Get_formula_z() * comp_ref.Get_phase_proportion());
|
|
store_sum_deltas(&delta[k], &charge_balance_unknown->delta,
|
|
-comp_ref.Get_formula_z() * comp_ref.Get_phase_proportion());
|
|
|
|
|
|
/* mole balance balance */
|
|
count_elts = 0;
|
|
paren_count = 0;
|
|
{
|
|
const char* cptr = comp_ref.Get_formula().c_str();
|
|
get_elts_in_species(&cptr, 1.0);
|
|
}
|
|
#ifdef COMBINE
|
|
change_hydrogen_in_elt_list(0);
|
|
#endif
|
|
for (jj = 0; jj < count_elts; jj++)
|
|
{
|
|
master_ptr = elt_list[jj].elt->primary;
|
|
if (master_ptr == NULL)
|
|
{
|
|
input_error++;
|
|
error_string = sformatf(
|
|
"Did not find unknown for %s, exchange related to mineral %s",
|
|
elt_list[jj].elt->primary->elt->name, comp_ref.Get_phase_name().c_str());
|
|
error_msg(error_string, STOP);
|
|
}
|
|
if (master_ptr->in == FALSE)
|
|
{
|
|
master_ptr = master_ptr->s->secondary;
|
|
}
|
|
if (master_ptr->s->type == EX)
|
|
{
|
|
if (equal
|
|
(x[j]->moles,
|
|
x[k]->moles * elt_list[jj].coef *
|
|
comp_ref.Get_phase_proportion(),
|
|
5.0 * convergence_tolerance) == FALSE)
|
|
{
|
|
error_string = sformatf(
|
|
"Resetting number of sites in exchanger %s (=%e) to be consistent with moles of phase %s (=%e).\n%s",
|
|
master_ptr->s->name, (double) x[j]->moles,
|
|
comp_ref.Get_phase_name().c_str(),
|
|
(double) (x[k]->moles * elt_list[jj].coef *
|
|
comp_ref.Get_phase_proportion()),
|
|
"\tHas equilibrium_phase assemblage been redefined?\n");
|
|
warning_msg(error_string);
|
|
x[j]->moles =
|
|
x[k]->moles * elt_list[jj].coef *
|
|
comp_ref.Get_phase_proportion();
|
|
}
|
|
}
|
|
coef = elt_list[jj].coef;
|
|
if (master_ptr->s == s_hplus)
|
|
{
|
|
row = mass_hydrogen_unknown->number;
|
|
unknown_ptr = mass_hydrogen_unknown;
|
|
}
|
|
else if (master_ptr->s == s_h2o)
|
|
{
|
|
row = mass_oxygen_unknown->number;
|
|
unknown_ptr = mass_oxygen_unknown;
|
|
}
|
|
else
|
|
{
|
|
row = master_ptr->unknown->number;
|
|
unknown_ptr = master_ptr->unknown;
|
|
}
|
|
store_jacob0((int)row, (int)x[k]->number,
|
|
coef * comp_ref.Get_phase_proportion());
|
|
store_sum_deltas(&delta[k], &unknown_ptr->delta,
|
|
-coef * comp_ref.Get_phase_proportion());
|
|
}
|
|
}
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
build_min_surface(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Defines proportionality factor between mineral and surface to
|
|
* jacob0
|
|
*/
|
|
if (use.Get_surface_ptr() == NULL)
|
|
return (OK);
|
|
cxxSurface *surface_ptr = use.Get_surface_ptr();
|
|
if (!surface_ptr->Get_related_phases())
|
|
return (OK);
|
|
for (size_t i = 0; i < surface_ptr->Get_surface_comps().size(); i++)
|
|
{
|
|
cxxSurfaceComp *comp_ptr = &(surface_ptr->Get_surface_comps()[i]);
|
|
if (comp_ptr->Get_phase_name().size() == 0)
|
|
continue;
|
|
class element *elt_ptr = element_store(comp_ptr->Get_master_element().c_str());
|
|
/* find unknown number */
|
|
int j;
|
|
for (j = (int)count_unknowns - 1; j >= 0; j--)
|
|
{
|
|
if (x[j]->type != SURFACE)
|
|
continue;
|
|
if (x[j]->master[0] == elt_ptr->master)
|
|
break;
|
|
}
|
|
int k;
|
|
for (k = (int)count_unknowns - 1; k >= 0; k--)
|
|
{
|
|
if (x[k]->type != PP)
|
|
continue;
|
|
//if (x[k]->phase->name == string_hsave(comp_ptr->Get_phase_name().c_str()))
|
|
if (strcmp_nocase(x[k]->phase->name, comp_ptr->Get_phase_name().c_str()) == 0)
|
|
break;
|
|
}
|
|
if (j == -1)
|
|
{
|
|
input_error++;
|
|
error_string = sformatf(
|
|
"Did not find unknown for master surface species %s",
|
|
elt_ptr->master->s->name);
|
|
error_msg(error_string, CONTINUE);
|
|
}
|
|
if (j == -1 || k == -1)
|
|
continue;
|
|
|
|
/* update grams == moles in this case */
|
|
if (j < count_unknowns - 1 && x[(size_t)j + 1]->type == SURFACE_CB)
|
|
{
|
|
store_sum_deltas(&delta[k], &(x[(size_t)j + 1]->related_moles), -1.0);
|
|
}
|
|
|
|
/* charge balance */
|
|
store_jacob0((int)charge_balance_unknown->number, (int)x[k]->number,
|
|
comp_ptr->Get_formula_z() * comp_ptr->Get_phase_proportion());
|
|
store_sum_deltas(&delta[k], &charge_balance_unknown->delta,
|
|
-comp_ptr->Get_formula_z() * comp_ptr->Get_phase_proportion());
|
|
count_elts = 0;
|
|
paren_count = 0;
|
|
{
|
|
/* Add specified formula for all types of surfaces */
|
|
const char* cptr1 = comp_ptr->Get_formula().c_str();
|
|
get_elts_in_species(&cptr1, 1.0);
|
|
}
|
|
#ifdef COMBINE
|
|
change_hydrogen_in_elt_list(0);
|
|
#endif
|
|
for (int jj = 0; jj < count_elts; jj++)
|
|
{
|
|
class master * master_ptr = elt_list[jj].elt->primary;
|
|
if (master_ptr->in == FALSE)
|
|
{
|
|
master_ptr = master_ptr->s->secondary;
|
|
}
|
|
if (master_ptr == NULL)
|
|
{
|
|
input_error++;
|
|
error_string = sformatf(
|
|
"Did not find unknown for %s, surface related to mineral %s",
|
|
elt_list[jj].elt->primary->elt->name, comp_ptr->Get_phase_name().c_str());
|
|
error_msg(error_string, STOP);
|
|
}
|
|
if (master_ptr->s->type == SURF)
|
|
{
|
|
if (equal
|
|
(x[j]->moles,
|
|
x[k]->moles * elt_list[jj].coef *
|
|
comp_ptr->Get_phase_proportion(),
|
|
5.0 * convergence_tolerance) == FALSE)
|
|
{
|
|
error_string = sformatf(
|
|
"Resetting number of sites in surface %s (=%e) to be consistent with moles of phase %s (=%e).\n%s",
|
|
master_ptr->s->name, (double) x[j]->moles,
|
|
comp_ptr->Get_phase_name().c_str(),
|
|
(double) (x[k]->moles * elt_list[jj].coef *
|
|
comp_ptr->Get_phase_proportion()),
|
|
"\tHas equilibrium_phase assemblage been redefined?\n");
|
|
warning_msg(error_string);
|
|
x[j]->moles =
|
|
x[k]->moles * elt_list[jj].coef *
|
|
comp_ptr->Get_phase_proportion();
|
|
}
|
|
}
|
|
LDBLE coef = elt_list[jj].coef;
|
|
size_t row;
|
|
class unknown *unknown_ptr;
|
|
if (master_ptr->s == s_hplus)
|
|
{
|
|
row = mass_hydrogen_unknown->number;
|
|
unknown_ptr = mass_hydrogen_unknown;
|
|
}
|
|
else if (master_ptr->s == s_h2o)
|
|
{
|
|
row = mass_oxygen_unknown->number;
|
|
unknown_ptr = mass_oxygen_unknown;
|
|
}
|
|
else
|
|
{
|
|
row = master_ptr->unknown->number;
|
|
unknown_ptr = master_ptr->unknown;
|
|
}
|
|
store_jacob0((int)row, (int)x[k]->number,
|
|
coef * comp_ptr->Get_phase_proportion());
|
|
store_sum_deltas(&delta[k], &unknown_ptr->delta,
|
|
-coef * comp_ptr->Get_phase_proportion());
|
|
}
|
|
|
|
}
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int Phreeqc::
|
|
setup_related_surface(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Fill in data for surface assemblage in unknown structure
|
|
*/
|
|
if (use.Get_surface_ptr() == NULL)
|
|
return (OK);
|
|
if (!use.Get_surface_ptr()->Get_related_phases())
|
|
return (OK);
|
|
|
|
for (int i = 0; i < count_unknowns; i++)
|
|
{
|
|
if (x[i]->type == SURFACE)
|
|
{
|
|
cxxSurfaceComp *comp_ptr = use.Get_surface_ptr()->Find_comp(x[i]->surface_comp);
|
|
if (comp_ptr->Get_phase_name().size() > 0)
|
|
{
|
|
int k;
|
|
for (k = (int)count_unknowns - 1; k >= 0; k--)
|
|
{
|
|
if (x[k]->type != PP)
|
|
continue;
|
|
//if (x[k]->phase->name == string_hsave(comp_ptr->Get_phase_name().c_str()))
|
|
if (strcmp_nocase(x[k]->phase->name, comp_ptr->Get_phase_name().c_str()) == 0)
|
|
break;
|
|
}
|
|
if (k == -1)
|
|
continue;
|
|
|
|
x[i]->phase_unknown = x[k];
|
|
/* !!!!! */
|
|
x[i]->moles = x[k]->moles * comp_ptr->Get_phase_proportion();
|
|
|
|
}
|
|
}
|
|
else if (x[i]->type == SURFACE_CB)
|
|
{
|
|
cxxSurfaceComp *comp_ptr = use.Get_surface_ptr()->Find_comp(x[(size_t)i-1]->surface_comp);
|
|
if (comp_ptr->Get_phase_name().size() > 0)
|
|
{
|
|
cxxSurfaceComp *comp_i_ptr = use.Get_surface_ptr()->Find_comp(x[i]->surface_comp);
|
|
int k;
|
|
for (k = (int)count_unknowns - 1; k >= 0; k--)
|
|
{
|
|
if (x[k]->type != PP)
|
|
continue;
|
|
//if (x[k]->phase->name == string_hsave(comp_i_ptr->Get_phase_name().c_str()))
|
|
if (strcmp_nocase(x[k]->phase->name, comp_i_ptr->Get_phase_name().c_str()) == 0)
|
|
break;
|
|
}
|
|
if (k == -1)
|
|
continue;
|
|
|
|
x[i]->phase_unknown = x[k];
|
|
/* !!!! Added for security, not checked... */
|
|
x[i]->related_moles = x[k]->moles * comp_i_ptr->Get_phase_proportion();
|
|
}
|
|
}
|
|
}
|
|
return (OK);
|
|
}
|