mirror of
https://git.gfz-potsdam.de/naaice/iphreeqc.git
synced 2025-12-16 16:44:49 +01:00
Will remove cpp and header files and make phreeqc an external directory. git-svn-id: svn://136.177.114.72/svn_GW/phreeqcpp/trunk@785 1feff8c3-07ed-0310-ac33-dd36852eb9cd
4056 lines
128 KiB
C++
4056 lines
128 KiB
C++
#define EXTERNAL extern
|
|
#include "global.h"
|
|
#include "phqalloc.h"
|
|
#include "output.h"
|
|
#include "phrqproto.h"
|
|
|
|
static char const svnid[] = "$Id: prep.c 655 2005-11-14 23:06:29Z dlpark $";
|
|
|
|
static int add_potential_factor(void);
|
|
static int add_surface_charge_balance(void);
|
|
static int build_gas_phase(void);
|
|
static int build_jacobian_sums (int k);
|
|
static int build_mb_sums(void);
|
|
static int build_min_exch(void);
|
|
static int build_model(void);
|
|
static int build_pure_phases(void);
|
|
static int build_s_s_assemblage(void);
|
|
static int build_solution_phase_boundaries(void);
|
|
static int build_species_list(int n);
|
|
static int build_min_surface(void);
|
|
static int change_hydrogen_in_elt_list(LDBLE charge);
|
|
static int clear (void);
|
|
static int convert_units(struct solution *solution_ptr);
|
|
static struct unknown *find_surface_charge_unknown(char *str_ptr);
|
|
static struct master **get_list_master_ptrs(char *ptr, struct master *master_ptr);
|
|
static int inout(void);
|
|
static int is_special(struct species *spec);
|
|
static int mb_for_species_aq(int n);
|
|
static int mb_for_species_ex(int n);
|
|
static int mb_for_species_surf(int n);
|
|
static int quick_setup (void);
|
|
static int resetup_master (void);
|
|
static int save_model(void);
|
|
static int setup_exchange (void);
|
|
static int setup_gas_phase(void);
|
|
static int setup_master_rxn(struct master **master_ptr_list, struct reaction **pe_rxn);
|
|
static int setup_pure_phases(void);
|
|
static int setup_related_surface (void);
|
|
static int setup_s_s_assemblage(void);
|
|
static int setup_solution (void);
|
|
static int setup_surface (void);
|
|
static int setup_unknowns (void);
|
|
static int store_dn (int k, LDBLE *source, int row, LDBLE coef_in, LDBLE *gamma_source);
|
|
static int store_jacob(LDBLE *source, LDBLE *target, LDBLE coef);
|
|
static int store_jacob0(int row, int column, LDBLE coef);
|
|
int store_mb(LDBLE *source, LDBLE *target, LDBLE coef);
|
|
static int store_mb_unknowns(struct unknown *unknown_ptr, LDBLE *LDBLE_ptr, LDBLE coef, LDBLE *gamma_ptr);
|
|
static int store_sum_deltas(LDBLE *source, LDBLE *target, LDBLE coef);
|
|
static int tidy_redox (void);
|
|
static struct master **unknown_alloc_master(void);
|
|
static int write_mb_eqn_x (void);
|
|
static int write_mb_for_species_list (int n);
|
|
static int write_mass_action_eqn_x (int stop);
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int prep(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Input is model defined by the structure use.
|
|
* Most of routine is skipped if model, as defined by master.total
|
|
* plus use.pure_phases, is same as previous calculation.
|
|
* Routine sets up struct unknown for each unknown.
|
|
* Determines elements, species, and phases that are in the model.
|
|
* Calculates mass-action equations for each species and phase.
|
|
* Routine builds a set of lists for calculating mass balance and
|
|
* for building jacobian.
|
|
*/
|
|
struct solution *solution_ptr;
|
|
if (svnid == NULL) fprintf(stderr," ");
|
|
|
|
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.solution_ptr;
|
|
if (solution_ptr == NULL) {
|
|
error_msg("Solution needed for calculation not found, stopping.", STOP);
|
|
}
|
|
description_x = (char *) free_check_null(description_x);
|
|
description_x = string_duplicate(solution_ptr->description);
|
|
/*
|
|
* Allocate space for unknowns
|
|
* Must allocate all necessary space before pointers to
|
|
* X are set.
|
|
*/
|
|
|
|
if (same_model == FALSE) {
|
|
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_s_s_assemblage();
|
|
setup_related_surface();
|
|
tidy_redox();
|
|
if (input_error > 0) {
|
|
error_msg("Program terminating due to input errors.", STOP);
|
|
}
|
|
/*
|
|
* Allocate space for array
|
|
*/
|
|
/*
|
|
array = (LDBLE *) PHRQ_malloc( (size_t) (count_unknowns+1) * count_unknowns * sizeof( LDBLE ));
|
|
if (array == NULL) malloc_error();
|
|
delta = (LDBLE *) PHRQ_malloc( (size_t) count_unknowns * sizeof( LDBLE ));
|
|
if (delta == NULL) malloc_error();
|
|
residual = (LDBLE *) PHRQ_malloc( (size_t) count_unknowns * sizeof( LDBLE ));
|
|
if (residual == NULL) malloc_error();
|
|
*/
|
|
array = (LDBLE *) PHRQ_malloc( (size_t) (max_unknowns+1) * max_unknowns * sizeof( LDBLE ));
|
|
if (array == NULL) malloc_error();
|
|
delta = (LDBLE *) PHRQ_malloc( (size_t) max_unknowns * sizeof( LDBLE ));
|
|
if (delta == NULL) malloc_error();
|
|
residual = (LDBLE *) PHRQ_malloc( (size_t) max_unknowns * sizeof( LDBLE ));
|
|
if (residual == NULL) malloc_error();
|
|
/*
|
|
* Build lists to fill Jacobian array and species list
|
|
*/
|
|
build_model();
|
|
} else {
|
|
/*
|
|
* If model is same, just update masses, don't rebuild unknowns and lists
|
|
*/
|
|
quick_setup();
|
|
}
|
|
if (input_error > 0) {
|
|
error_msg("Program stopping due to input errors.", STOP);
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
static int 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, j, k, l;
|
|
|
|
for (i = 0; i < count_master; i++) {
|
|
if (master[i]->s->type == SURF_PSI) continue;
|
|
if (master[i]->s == s_eminus ||
|
|
master[i]->s == s_hplus ||
|
|
master[i]->s == s_h2o ||
|
|
master[i]->s == s_h2 ||
|
|
master[i]->s == s_o2) continue;
|
|
if(master[i]->total > 0) {
|
|
if (master[i]->s->secondary != NULL) {
|
|
master[i]->s->secondary->unknown->moles = master[i]->total;
|
|
} else {
|
|
master[i]->unknown->moles = master[i]->total;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Reaction: pH for charge balance
|
|
*/
|
|
ph_unknown->moles = use.solution_ptr->cb;
|
|
/*
|
|
* Reaction: pe for total hydrogen
|
|
*/
|
|
if (mass_hydrogen_unknown != NULL) {
|
|
#define COMBINE
|
|
/*#define COMBINE_CHARGE*/
|
|
#ifdef COMBINE
|
|
#ifndef COMBINE_CHARGE
|
|
mass_hydrogen_unknown->moles = use.solution_ptr->total_h - 2 * use.solution_ptr->total_o;
|
|
#else
|
|
mass_hydrogen_unknown->moles = use.solution_ptr->total_h - 2 * use.solution_ptr->total_o - use.solution_ptr->cb;
|
|
#endif
|
|
#else
|
|
mass_hydrogen_unknown->moles = use.solution_ptr->total_h;
|
|
#endif
|
|
}
|
|
/*
|
|
* Reaction H2O for total oxygen
|
|
*/
|
|
if (mass_oxygen_unknown != NULL) {
|
|
mass_oxygen_unknown->moles = use.solution_ptr->total_o;
|
|
}
|
|
|
|
/*
|
|
* pp_assemblage
|
|
*/
|
|
j = 0;
|
|
for (i = 0; i < count_unknowns; i++) {
|
|
if (x[i]->type == PP) {
|
|
x[i]->moles = use.pp_assemblage_ptr->pure_phases[j].moles;
|
|
x[i]->dissolve_only = use.pp_assemblage_ptr->pure_phases[j].dissolve_only;
|
|
use.pp_assemblage_ptr->pure_phases[j].delta = 0.0;
|
|
x[i]->pure_phase = &(use.pp_assemblage_ptr->pure_phases[j]);
|
|
j++;
|
|
}
|
|
}
|
|
/*
|
|
* gas phase
|
|
*/
|
|
if (gas_unknown != NULL) {
|
|
gas_unknown->moles = 0.0;
|
|
for (i=0; i < use.gas_phase_ptr->count_comps; i++) {
|
|
gas_unknown->moles += use.gas_phase_ptr->comps[i].moles;
|
|
}
|
|
if (gas_unknown->moles <= 0) gas_unknown->moles = MIN_TOTAL;
|
|
gas_unknown->ln_moles = log(gas_unknown->moles);
|
|
gas_unknown->gas_phase = use.gas_phase_ptr;
|
|
}
|
|
/*
|
|
* s_s_assemblage
|
|
*/
|
|
if (s_s_unknown != NULL) {
|
|
for (i = 0; i < count_unknowns; i++) {
|
|
if (x[i]->type == S_S_MOLES) break;
|
|
}
|
|
for (j = 0; j < use.s_s_assemblage_ptr->count_s_s; j++) {
|
|
for (k = 0; k < use.s_s_assemblage_ptr->s_s[j].count_comps; k++) {
|
|
x[i]->s_s = &(use.s_s_assemblage_ptr->s_s[j]);
|
|
x[i]->s_s_comp = &(use.s_s_assemblage_ptr->s_s[j].comps[k]);
|
|
x[i]->s_s_comp_number = j;
|
|
x[i]->moles = x[i]->s_s_comp->moles;
|
|
if (x[i]->moles <= 0) {
|
|
x[i]->moles = MIN_TOTAL;
|
|
x[i]->s_s_comp->moles = MIN_TOTAL;
|
|
}
|
|
x[i]->s_s_comp->initial_moles = x[i]->moles;
|
|
x[i]->ln_moles = log(x[i]->moles);
|
|
|
|
x[i]->phase->dn = x[i]->s_s_comp->dn;
|
|
x[i]->phase->dnb = x[i]->s_s_comp->dnb;
|
|
x[i]->phase->dnc = x[i]->s_s_comp->dnc;
|
|
x[i]->phase->log10_fraction_x = x[i]->s_s_comp->log10_fraction_x;
|
|
x[i]->phase->log10_lambda = x[i]->s_s_comp->log10_lambda;
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* exchange
|
|
*/
|
|
if (use.exchange_ptr != NULL) {
|
|
k = 0;
|
|
for (i = 0 ; i < count_unknowns; i++) {
|
|
if (x[i]->type == EXCH) {
|
|
x[i]->exch_comp = &(use.exchange_ptr->comps[k++]);
|
|
if (x[i]->exch_comp->rate_name != NULL) {
|
|
for (l = 0; x[i]->exch_comp->totals[l].elt != NULL; l++) {
|
|
if (x[i]->exch_comp->totals[l].elt->master->type != EX) continue;
|
|
if (strcmp_nocase(x[i]->description, x[i]->exch_comp->totals[l].elt->name) == 0) {
|
|
x[i]->moles = x[i]->exch_comp->totals[l].coef;
|
|
/* printf("%s moles %e\n", x[i]->description, x[i]->moles); */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef SKIP
|
|
if (count_kin_exch > 0) {
|
|
for (l = 0; x[i]->exch_comp->totals[l].elt != NULL; l++) {
|
|
if (x[i]->exch_comp->totals[l].elt->master->type != EX) continue;
|
|
if (strcmp_nocase(x[i]->description, x[i]->exch_comp->totals[l].elt->name) == 0) {
|
|
x[i]->moles = x[i]->exch_comp->totals[l].coef;
|
|
/* printf("%s moles %e\n", x[i]->description, x[i]->moles); */
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* surface
|
|
*/
|
|
if (use.surface_ptr != NULL) {
|
|
for (i = 0; i < count_unknowns; i++) {
|
|
if (x[i]->type == SURFACE) {
|
|
break;
|
|
}
|
|
}
|
|
j = 0;
|
|
k = 0;
|
|
for ( ; i < count_unknowns; i++) {
|
|
if (x[i]->type == SURFACE_CB) {
|
|
x[i]->surface_charge = &(use.surface_ptr->charge[j]);
|
|
x[i]->related_moles = x[i]->surface_charge->grams;
|
|
x[i]->mass_water = use.surface_ptr->charge[j++].mass_water;
|
|
x[i]->surface_comp = x[i-1]->surface_comp;
|
|
/* moles picked up from master->total */
|
|
} else if (x[i]->type == SURFACE) {
|
|
x[i]->surface_comp = &(use.surface_ptr->comps[k++]);
|
|
/* moles picked up from master->total
|
|
except for surfaces related to kinetic minerals ... */
|
|
if (x[i]->surface_comp->rate_name != NULL) {
|
|
for (l = 0; x[i]->surface_comp->totals[l].elt != NULL; l++) {
|
|
if (x[i]->surface_comp->totals[l].elt->master->type != SURF) continue;
|
|
if (strcmp_nocase(x[i]->description, x[i]->surface_comp->totals[l].elt->name) == 0) {
|
|
x[i]->moles = x[i]->surface_comp->totals[l].coef;
|
|
/* printf("%s moles %e\n", x[i]->description, x[i]->moles); */
|
|
}
|
|
}
|
|
}
|
|
/* !!!! */
|
|
#ifdef SKIP
|
|
if (count_kin_surf > 0) {
|
|
for (l = 0; x[i]->surface_comp->totals[l].elt != NULL; l++) {
|
|
if (x[i]->surface_comp->totals[l].elt->master->type != SURF) continue;
|
|
|
|
if (strcmp_nocase(x[i]->description, x[i]->surface_comp->totals[l].elt->name) == 0) {
|
|
x[i]->moles = x[i]->surface_comp->totals[l].coef;
|
|
/* printf("%s moles %e\n", x[i]->description, x[i]->moles); */
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
save_model();
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int build_gas_phase(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Put coefficients into lists to sum iaps to test for equilibrium
|
|
* Put coefficients into lists to build jacobian for
|
|
* sum of partial pressures equation and
|
|
* mass balance equations for elements contained in gases
|
|
*/
|
|
int i, j;
|
|
int row, col;
|
|
struct master *master_ptr;
|
|
struct rxn_token *rxn_ptr;
|
|
struct gas_comp *gas_comp_ptr;
|
|
struct phase *phase_ptr;
|
|
struct unknown *unknown_ptr;
|
|
LDBLE coef, coef_elt;
|
|
|
|
if (gas_unknown == NULL) return(OK);
|
|
for (i = 0; i < use.gas_phase_ptr->count_comps; i++) {
|
|
/*
|
|
* Determine elements in gas component
|
|
*/
|
|
count_elts = 0;
|
|
paren_count = 0;
|
|
gas_comp_ptr = &(use.gas_phase_ptr->comps[i]);
|
|
phase_ptr = gas_comp_ptr->phase;
|
|
if (phase_ptr->rxn_x == NULL) continue;
|
|
add_elt_list(phase_ptr->next_elt, 1.0);
|
|
#ifdef COMBINE
|
|
change_hydrogen_in_elt_list(0);
|
|
#endif
|
|
/*
|
|
* Build mass balance sums for each element in gas
|
|
*/
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\n\tMass balance summations %s.\n\n",
|
|
gas_comp_ptr->phase->name);
|
|
}
|
|
|
|
/* All elements in gas */
|
|
for (j = 0; j < count_elts; j++) {
|
|
unknown_ptr = NULL;
|
|
if (strcmp(elt_list[j].elt->name,"H") == 0) {
|
|
unknown_ptr = mass_hydrogen_unknown;
|
|
} else if (strcmp(elt_list[j].elt->name,"O") == 0) {
|
|
unknown_ptr = mass_oxygen_unknown;
|
|
} else {
|
|
if (elt_list[j].elt->primary->in == TRUE) {
|
|
unknown_ptr = elt_list[j].elt->primary->unknown;
|
|
} else if (elt_list[j].elt->primary->s->secondary != NULL) {
|
|
unknown_ptr = elt_list[j].elt->primary->s->secondary->unknown;
|
|
}
|
|
}
|
|
if (unknown_ptr != NULL) {
|
|
coef = elt_list[j].coef;
|
|
store_mb(&(gas_comp_ptr->phase->moles_x), &(unknown_ptr->f), coef);
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\t\t%-24s%10.3f\n",
|
|
unknown_ptr->description,
|
|
(double) coef);
|
|
}
|
|
}
|
|
}
|
|
if (use.gas_phase_ptr->type == PRESSURE) {
|
|
/* Total pressure of gases */
|
|
store_mb(&(gas_comp_ptr->phase->p_soln_x), &(gas_unknown->f), 1.0);
|
|
}
|
|
/*
|
|
* Build jacobian sums for mass balance equations
|
|
*/
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\n\tJacobian summations %s.\n\n",
|
|
phase_ptr->name);
|
|
}
|
|
for (j = 0; j < count_elts; j++) {
|
|
unknown_ptr = NULL;
|
|
if (strcmp(elt_list[j].elt->name,"H") == 0) {
|
|
unknown_ptr = mass_hydrogen_unknown;
|
|
} else if (strcmp(elt_list[j].elt->name,"O") == 0) {
|
|
unknown_ptr = mass_oxygen_unknown;
|
|
} else {
|
|
if (elt_list[j].elt->primary->in == TRUE) {
|
|
unknown_ptr = elt_list[j].elt->primary->unknown;
|
|
} else if (elt_list[j].elt->primary->s->secondary != NULL) {
|
|
unknown_ptr = elt_list[j].elt->primary->s->secondary->unknown;
|
|
}
|
|
}
|
|
if (unknown_ptr == NULL) {
|
|
#ifdef SKIP
|
|
error_msg("NULL pointer in subroutine build_gas_phase.", STOP);
|
|
#endif
|
|
continue;
|
|
}
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\n\t%s.\n",
|
|
unknown_ptr->description);
|
|
}
|
|
row = unknown_ptr->number * (count_unknowns + 1);
|
|
coef_elt = elt_list[j].coef;
|
|
for (rxn_ptr = phase_ptr->rxn_x->token+1;
|
|
rxn_ptr->s != NULL; rxn_ptr++) {
|
|
|
|
if (rxn_ptr->s->secondary != NULL && rxn_ptr->s->secondary->in == TRUE ) {
|
|
master_ptr=rxn_ptr->s->secondary;
|
|
} else {
|
|
master_ptr=rxn_ptr->s->primary;
|
|
}
|
|
if (master_ptr == NULL) {
|
|
sprintf(error_string, "Element needed for gas component, %s, is not in model.", phase_ptr->name);
|
|
error_msg(error_string, CONTINUE);
|
|
input_error++;
|
|
continue;
|
|
}
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\t\t%s\n",master_ptr->s->name);
|
|
}
|
|
if (master_ptr->unknown == NULL) {
|
|
#ifdef SKIP
|
|
input_error++;
|
|
#endif
|
|
continue;
|
|
}
|
|
if (master_ptr->in == FALSE ) {
|
|
sprintf(error_string, "Element, %s, in phase, %s, is not in model.", master_ptr->elt->name, phase_ptr->name);
|
|
error_msg(error_string, CONTINUE);
|
|
input_error++;
|
|
}
|
|
col = master_ptr->unknown->number;
|
|
coef = coef_elt * rxn_ptr->coef;
|
|
store_jacob (&(gas_comp_ptr->phase->moles_x), &(array[row + col]), coef);
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\t\t%-24s%10.3f\t%d\t%d\n",master_ptr->s->name, (double) coef, row/(count_unknowns+1), col);
|
|
}
|
|
}
|
|
if (use.gas_phase_ptr->type == PRESSURE) {
|
|
/* derivative wrt total moles of gas */
|
|
store_jacob (&(gas_comp_ptr->phase->fraction_x), &(array[row + gas_unknown->number]), coef_elt);
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\t\t%-24s%10.3f\t%d\t%d\n","gas moles", (double) elt_list[j].coef, row/(count_unknowns+1), gas_unknown->number);
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Build jacobian sums for sum of partial pressures equation
|
|
*/
|
|
if (use.gas_phase_ptr->type != PRESSURE) continue;
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\n\tPartial pressure eqn %s.\n\n",
|
|
phase_ptr->name);
|
|
}
|
|
unknown_ptr = gas_unknown;
|
|
row = unknown_ptr->number * (count_unknowns + 1);
|
|
for (rxn_ptr = phase_ptr->rxn_x->token+1;
|
|
rxn_ptr->s != NULL; rxn_ptr++) {
|
|
if (rxn_ptr->s->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(OUTPUT_MESSAGE,"\t\t%s\n",master_ptr->s->name);
|
|
}
|
|
if (master_ptr->unknown == NULL) {
|
|
#ifdef SKIP
|
|
input_error++;
|
|
#endif
|
|
continue;
|
|
}
|
|
if (master_ptr->in == FALSE ) {
|
|
sprintf(error_string, "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 = rxn_ptr->coef;
|
|
store_jacob (&(gas_comp_ptr->phase->p_soln_x), &(array[row + col]), coef);
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\t\t%-24s%10.3f\t%d\t%d\n",master_ptr->s->name, (double) coef,
|
|
row/(count_unknowns+1), col);
|
|
}
|
|
}
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int build_s_s_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
|
|
*/
|
|
int i, j, k, l, stop;
|
|
int row, col;
|
|
struct master *master_ptr;
|
|
struct rxn_token *rxn_ptr;
|
|
struct s_s *s_s_ptr, *s_s_ptr_old;
|
|
char token[MAX_LENGTH];
|
|
char *ptr;
|
|
|
|
if (s_s_unknown == NULL) return(OK);
|
|
s_s_ptr_old = NULL;
|
|
col = 0;
|
|
for (i = 0; i < count_unknowns; i++) {
|
|
if (x[i]->type != S_S_MOLES) continue;
|
|
s_s_ptr = x[i]->s_s;
|
|
if (s_s_ptr != s_s_ptr_old) {
|
|
col = x[i]->number;
|
|
s_s_ptr_old = s_s_ptr;
|
|
}
|
|
/*
|
|
* Calculate function value (inverse saturation index)
|
|
*/
|
|
if (x[i]->phase->rxn_x == NULL) continue;
|
|
store_mb(&(x[i]->phase->lk), &(x[i]->f), 1.0);
|
|
for (rxn_ptr = x[i]->phase->rxn_x->token + 1; rxn_ptr->s != NULL; rxn_ptr++) {
|
|
store_mb(&(rxn_ptr->s->la), &(x[i]->f), -rxn_ptr->coef);
|
|
}
|
|
/* include mole fraction */
|
|
store_mb(&(x[i]->phase->log10_fraction_x), &(x[i]->f), 1.0);
|
|
|
|
/* include activity coeficient */
|
|
store_mb(&(x[i]->phase->log10_lambda), &(x[i]->f), 1.0);
|
|
/*
|
|
* Put coefficients into mass action equations
|
|
*/
|
|
/* first IAP terms */
|
|
for (rxn_ptr=x[i]->phase->rxn_x->token + 1; rxn_ptr->s != NULL; rxn_ptr++) {
|
|
if (rxn_ptr->s->secondary != NULL &&
|
|
rxn_ptr->s->secondary->in == TRUE ) {
|
|
master_ptr=rxn_ptr->s->secondary;
|
|
} else {
|
|
master_ptr=rxn_ptr->s->primary;
|
|
}
|
|
if (master_ptr == NULL || master_ptr->unknown == NULL) continue;
|
|
store_jacob0(x[i]->number, master_ptr->unknown->number, rxn_ptr->coef);
|
|
}
|
|
|
|
if (s_s_ptr->a0 != 0.0 || s_s_ptr->a1 != 0.0) {
|
|
/*
|
|
* For binary solid solution
|
|
*/
|
|
/* next dnc terms */
|
|
row = x[i]->number * (count_unknowns + 1);
|
|
if (x[i]->s_s_comp_number == 0) {
|
|
col = x[i]->number;
|
|
} else {
|
|
col = x[i]->number -1;
|
|
}
|
|
store_jacob (&(x[i]->phase->dnc), &(array[row + col]), -1);
|
|
|
|
/* next dnb terms */
|
|
col++;
|
|
store_jacob (&(x[i]->phase->dnb), &(array[row + col]), -1);
|
|
} else {
|
|
/*
|
|
* For ideal solid solution
|
|
*/
|
|
row = x[i]->number * (count_unknowns + 1);
|
|
for (j = 0; j < s_s_ptr->count_comps; j++) {
|
|
if (j != x[i]->s_s_comp_number) {
|
|
/* store_jacob (&(s_s_ptr->dn), &(array[row + col + j]), -1.0); */
|
|
store_jacob (&(x[i]->phase->dn), &(array[row + col + j]), -1.0);
|
|
} else {
|
|
store_jacob (&(x[i]->phase->dnb), &(array[row + col + j]), -1.0);
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Put coefficients into mass balance equations
|
|
*/
|
|
count_elts = 0;
|
|
paren_count = 0;
|
|
strcpy(token, x[i]->phase->formula);
|
|
ptr = token;
|
|
get_elts_in_species (&ptr, 1.0);
|
|
/*
|
|
* Go through elements in phase
|
|
*/
|
|
#ifdef COMBINE
|
|
change_hydrogen_in_elt_list(0);
|
|
#endif
|
|
for (j = 0; j < count_elts; j++) {
|
|
|
|
if (strcmp(elt_list[j].elt->name, "H") == 0 && mass_hydrogen_unknown != NULL) {
|
|
store_jacob0(mass_hydrogen_unknown->number, x[i]->number, -elt_list[j].coef);
|
|
store_sum_deltas(&(delta[i]), &mass_hydrogen_unknown->delta, elt_list[j].coef);
|
|
|
|
} else if (strcmp(elt_list[j].elt->name, "O") == 0 && mass_oxygen_unknown != NULL) {
|
|
store_jacob0(mass_oxygen_unknown->number, x[i]->number, -elt_list[j].coef);
|
|
store_sum_deltas(&(delta[i]), &mass_oxygen_unknown->delta, elt_list[j].coef);
|
|
|
|
} else {
|
|
master_ptr = elt_list[j].elt->primary;
|
|
if (master_ptr->in == FALSE) {
|
|
master_ptr = master_ptr->s->secondary;
|
|
}
|
|
if (master_ptr == NULL || master_ptr->in == FALSE) {
|
|
if (state != ADVECTION && state != TRANSPORT && state != PHAST ) {
|
|
sprintf(error_string, "Element in phase, %s, is not in model.", x[i]->phase->name);
|
|
warning_msg(error_string);
|
|
}
|
|
if (master_ptr != NULL) {
|
|
master_ptr->s->la = -999.9;
|
|
}
|
|
/*
|
|
* Master species is in model
|
|
*/
|
|
} else if (master_ptr->in == TRUE ) {
|
|
store_jacob0(master_ptr->unknown->number, x[i]->number,
|
|
-elt_list[j].coef);
|
|
store_sum_deltas(&delta[i], &master_ptr->unknown->delta, elt_list[j].coef);
|
|
/*
|
|
* Master species in equation needs to be rewritten
|
|
*/
|
|
} else if (master_ptr->in == REWRITE ) {
|
|
stop = FALSE;
|
|
for (k=0; k < count_unknowns; k++) {
|
|
if (x[k]->type != MB) continue;
|
|
for (l = 0; x[k]->master[l] != NULL; l++) {
|
|
if (x[k]->master[l] == master_ptr) {
|
|
store_jacob0(x[k]->master[0]->unknown->number, x[i]->number,
|
|
-elt_list[j].coef);
|
|
store_sum_deltas(&delta[i], &x[k]->master[0]->unknown->delta,
|
|
elt_list[j].coef);
|
|
stop = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (stop == TRUE) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int 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(OUTPUT_MESSAGE,"\n\tJacobian summations.\n");
|
|
/*
|
|
* Calculate jacobian coefficients for each mass balance equation
|
|
*/
|
|
for (i = 0; i < count_mb_unknowns; i++) {
|
|
#ifdef SKIP
|
|
/* switch for mu term */
|
|
if (diffuse_layer_x == TRUE && ((mb_unknowns[i].unknown == charge_balance_unknown) ||
|
|
(mb_unknowns[i].unknown == ah2o_unknown) ||
|
|
(mb_unknowns[i].unknown == mu_unknown))) {
|
|
use_tot_g = 1;
|
|
} else if (diffuse_layer_x == TRUE && mb_unknowns[i].unknown->type == SURFACE_CB) {
|
|
use_tot_g = 2;
|
|
} else {
|
|
use_tot_g = 0;
|
|
}
|
|
#endif
|
|
/*
|
|
* 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(OUTPUT_MESSAGE,"\n\tMass balance eq: %s\t%f\n",
|
|
mb_unknowns[i].unknown->description, (double) coef);
|
|
store_dn(k, mb_unknowns[i].source, mb_unknowns[i].unknown->number, coef, mb_unknowns[i].gamma_source);
|
|
/*
|
|
* Add extra terms for change in dg/dx in diffuse layer model
|
|
*/
|
|
if (s[k]->type >= H2O || diffuse_layer_x == FALSE) {
|
|
continue;
|
|
} else if ( (mb_unknowns[i].unknown->type == MB ||
|
|
mb_unknowns[i].unknown->type == MH ||
|
|
mb_unknowns[i].unknown->type == MH2O) && state >= REACTION) {
|
|
if (mass_oxygen_unknown != NULL) {
|
|
/* term for water, sum of all surfaces */
|
|
source = &s[k]->tot_dh2o_moles;
|
|
target = &(array[mb_unknowns[i].unknown->number * (count_unknowns + 1) + mass_oxygen_unknown->number]);
|
|
store_jacob(source, target, coef);
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\t\t%-24s%10.3f\t%d\t%d\n", "sum[dn(i,s)/dlnwater]", (double) coef,
|
|
mb_unknowns[i].unknown->number, mass_oxygen_unknown->number);
|
|
}
|
|
}
|
|
|
|
/* terms for psi, one for each surface */
|
|
count_g = 0;
|
|
for (j = 0; j < count_unknowns; j++) {
|
|
if (x[j]->type != SURFACE_CB) continue;
|
|
source = &s[k]->diff_layer[count_g].dx_moles;
|
|
target = &(array[mb_unknowns[i].unknown->number *
|
|
(count_unknowns + 1) +
|
|
x[j]->number]);
|
|
store_jacob(source, target, coef);
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\t\t%-24s%10.3f\t%d\t%d\n",
|
|
"dg/dlny", (double) coef,
|
|
mb_unknowns[i].unknown->number,
|
|
x[j]->number);
|
|
}
|
|
count_g++;
|
|
if (count_g >= use.surface_ptr->count_charge) break;
|
|
}
|
|
|
|
/* terms for related phases */
|
|
count_g = 0;
|
|
for (j = 0; j < count_unknowns; j++) {
|
|
if (x[j]->type != SURFACE_CB) continue;
|
|
|
|
/* has related phase */
|
|
if (x[j-1]->surface_comp->phase_name == NULL) continue;
|
|
|
|
/* now find the related phase */
|
|
for (kk = count_unknowns - 1; kk >= 0; kk--) {
|
|
if (x[kk]->type != PP) continue;
|
|
if (x[kk]->phase->name == x[j-1]->surface_comp->phase_name) break;
|
|
}
|
|
|
|
if (kk >= 0) {
|
|
source = &s[k]->diff_layer[count_g].drelated_moles;
|
|
target = &(array[mb_unknowns[i].unknown->number *
|
|
(count_unknowns + 1) +
|
|
x[kk]->number]);
|
|
store_jacob(source, target, coef);
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\t\t%-24s%10.3f\t%d\t%d\n",
|
|
"dphase", (double) coef,
|
|
mb_unknowns[i].unknown->number,
|
|
x[kk]->number);
|
|
}
|
|
}
|
|
count_g++;
|
|
if (count_g >= use.surface_ptr->count_charge) 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;
|
|
if (mb_unknowns[i].unknown->number == x[j]->number) {
|
|
source = &s[k]->diff_layer[count_g].dx_moles;
|
|
target = &(array[mb_unknowns[i].unknown->number *
|
|
(count_unknowns + 1) +
|
|
x[j]->number]);
|
|
store_jacob(source, target, coef);
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\t\t%-24s%10.3f\t%d\t%d\n",
|
|
"dg/dlny", (double) coef,
|
|
mb_unknowns[i].unknown->number,
|
|
x[j]->number);
|
|
}
|
|
|
|
/* term for related phase */
|
|
/* has related phase */
|
|
if (x[j-1]->surface_comp->phase_name != NULL) {
|
|
|
|
/* now find the related phase */
|
|
for (kk = count_unknowns - 1; kk >= 0; kk--) {
|
|
if (x[kk]->type != PP) continue;
|
|
if (x[kk]->phase->name == x[j-1]->surface_comp->phase_name) break;
|
|
}
|
|
if (kk >= 0) {
|
|
source = &s[k]->diff_layer[count_g].drelated_moles;
|
|
target = &(array[mb_unknowns[i].unknown->number *
|
|
(count_unknowns + 1) +
|
|
x[kk]->number]);
|
|
store_jacob(source, target, coef);
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\t\t%-24s%10.3f\t%d\t%d\n",
|
|
"dphase", (double) coef,
|
|
mb_unknowns[i].unknown->number,
|
|
x[kk]->number);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mass_oxygen_unknown != NULL) {
|
|
/* term for water, for same surfaces */
|
|
source = &s[k]->diff_layer[count_g].dh2o_moles;
|
|
target = &(array[mb_unknowns[i].unknown->number *
|
|
(count_unknowns + 1) + mass_oxygen_unknown->number]);
|
|
store_jacob(source, target, coef);
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\t\t%-24s%10.3f\t%d\t%d\n",
|
|
"dn(i,s)/dlnwater", (double) coef,
|
|
mb_unknowns[i].unknown->number,
|
|
mass_oxygen_unknown->number);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
count_g++;
|
|
if (count_g >= use.surface_ptr->count_charge) break;
|
|
}
|
|
}
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int build_mb_sums(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Function builds lists sum_mb1 and sum_mb2 that describe how to sum molalities
|
|
* to calculate mass balance sums, including activity of water, ionic strength,
|
|
* charge balance, and alkalinity.
|
|
*/
|
|
int i;
|
|
LDBLE *target;
|
|
/*
|
|
* Make space for lists
|
|
*/
|
|
if (count_sum_mb1 + count_mb_unknowns >= max_sum_mb1) {
|
|
space ((void **) ((void *) &sum_mb1), count_sum_mb1 + count_mb_unknowns,
|
|
&max_sum_mb1, sizeof(struct list1));
|
|
}
|
|
if (count_sum_mb2 + count_mb_unknowns >= max_sum_mb2) {
|
|
space ((void **) ((void *) &sum_mb2), count_sum_mb2 + count_mb_unknowns,
|
|
&max_sum_mb2, sizeof(struct list2));
|
|
}
|
|
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\n\tMass balance summations.\n\n");
|
|
}
|
|
for (i = 0; i < count_mb_unknowns; i++) {
|
|
target = &(mb_unknowns[i].unknown->f);
|
|
store_mb(mb_unknowns[i].source, target, mb_unknowns[i].coef);
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\t\t%-24s%10.3f\n",
|
|
mb_unknowns[i].unknown->description,
|
|
(double) mb_unknowns[i].coef);
|
|
}
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int build_model(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Guts of prep. Determines species in model, rewrites equations,
|
|
* builds lists for mass balance and jacobian sums.
|
|
*/
|
|
int i, j, j0, k;
|
|
LDBLE coef_e;
|
|
|
|
if (s_hplus == NULL || s_eminus == NULL || s_h2o == NULL) {
|
|
error_msg("Data base is missing H+, H2O, or e- species.", CONTINUE);
|
|
input_error++;
|
|
}
|
|
/*
|
|
* Make space for lists of pointers to species in the model
|
|
*/
|
|
|
|
max_s_x = MAX_S;
|
|
space ((void **) ((void *) &s_x), INIT, &max_s_x, sizeof(struct species *));
|
|
|
|
max_sum_mb1=MAX_SUM_MB;
|
|
count_sum_mb1=0;
|
|
space ((void **) ((void *) &sum_mb1), INIT, &max_sum_mb1, sizeof(struct list1));
|
|
|
|
max_sum_mb2=MAX_SUM_MB;
|
|
count_sum_mb2=0;
|
|
space ((void **) ((void *) &sum_mb2), INIT, &max_sum_mb2, sizeof(struct list2));
|
|
|
|
max_sum_jacob0=MAX_SUM_JACOB0;
|
|
count_sum_jacob0=0;
|
|
space ((void **) ((void *) &sum_jacob0), INIT, &max_sum_jacob0,
|
|
sizeof(struct list0));
|
|
|
|
max_sum_jacob1=MAX_SUM_JACOB1;
|
|
count_sum_jacob1=0;
|
|
space ((void **) ((void *) &sum_jacob1), INIT, &max_sum_jacob1,
|
|
sizeof(struct list1));
|
|
|
|
max_sum_jacob2=MAX_SUM_JACOB2;
|
|
count_sum_jacob2=0;
|
|
space ((void **) ((void *) &sum_jacob2), INIT, &max_sum_jacob2,
|
|
sizeof(struct list2));
|
|
|
|
|
|
max_sum_delta=MAX_SUM_JACOB0;
|
|
count_sum_delta=0;
|
|
space ((void **) ((void *) &sum_delta), INIT, &max_sum_delta,
|
|
sizeof(struct list2));
|
|
|
|
max_species_list = 5 * MAX_S;
|
|
count_species_list = 0;
|
|
species_list = (struct species_list *) free_check_null(species_list);
|
|
space ((void **) ((void *) &species_list), INIT, &max_species_list,
|
|
sizeof (struct species_list));
|
|
|
|
/*
|
|
* Pick species in the model, determine reaction for model, build jacobian
|
|
*/
|
|
count_s_x=0;
|
|
compute_gfw("H2O", &gfw_water);
|
|
gfw_water *= 0.001;
|
|
for (i=0; i < count_s; i++) {
|
|
if (s[i]->type > H2O && s[i]->type != EX && s[i]->type != SURF) continue;
|
|
s[i]->in=FALSE;
|
|
count_trxn=0;
|
|
trxn_add (s[i]->rxn_s, 1.0, FALSE); /* rxn_s is set in tidy_model */
|
|
/*
|
|
* Check if species is in model
|
|
*/
|
|
s[i]->in = inout();
|
|
if (s[i]->in == TRUE) {
|
|
/* for isotopes, activity of water is for 1H and 16O */
|
|
if (s[i]->gflag == 9 ) {
|
|
gfw_water = 18.0/1000.0;
|
|
}
|
|
if (pitzer_model == FALSE) s[i]->lg = 0.0;
|
|
if (count_s_x + 1 >= max_s_x) {
|
|
space ((void **) ((void *) &s_x), count_s_x + 1,
|
|
&max_s_x,
|
|
sizeof (struct species *));
|
|
}
|
|
s_x[count_s_x++] = s[i];
|
|
/*
|
|
* Write mass action equation for current model
|
|
*/
|
|
write_mass_action_eqn_x(STOP);
|
|
if (s[i]->type == SURF) {
|
|
add_potential_factor();
|
|
}
|
|
rxn_free(s[i]->rxn_x);
|
|
s[i]->rxn_x = rxn_alloc(count_trxn+1);
|
|
trxn_copy (s[i]->rxn_x);
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\nSpecies: %s\n", s[i]->name);
|
|
trxn_print();
|
|
}
|
|
/*
|
|
* Determine mass balance equations, build sums for mass balance, build sums for jacobian
|
|
*/
|
|
count_trxn=0;
|
|
trxn_add (s[i]->rxn_s, 1.0, FALSE);
|
|
if (s[i]->next_secondary == NULL) {
|
|
write_mb_eqn_x();
|
|
} else {
|
|
count_elts = 0;
|
|
add_elt_list(s[i]->next_secondary, 1.0);
|
|
}
|
|
if (s[i]->type == SURF) {
|
|
add_potential_factor();
|
|
add_surface_charge_balance();
|
|
}
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"%s\n", trxn.token[0].s->name);
|
|
for (j = 0; j < count_elts; j++) {
|
|
output_msg(OUTPUT_MESSAGE,"\t%s\t%f\n", elt_list[j].elt->name, (double) elt_list[j].coef);
|
|
}
|
|
}
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\nSpecies: %s\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) build_jacobian_sums(i);
|
|
/*
|
|
* Build list of species for summing and printing
|
|
*/
|
|
if (s[i]->next_secondary == NULL) {
|
|
write_mb_for_species_list(i);
|
|
} else {
|
|
count_elts = 0;
|
|
add_elt_list(s[i]->next_secondary, 1.0);
|
|
}
|
|
build_species_list(i);
|
|
}
|
|
}
|
|
if (diffuse_layer_x == TRUE && pitzer_model == TRUE) {
|
|
error_msg("-diffuse_layer option not available for Pizer model", STOP);
|
|
}
|
|
/*
|
|
* Sum diffuse layer water into hydrogen and oxygen mass balances
|
|
*/
|
|
if (diffuse_layer_x == TRUE && 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_unknown->f),
|
|
2 / gfw_water);
|
|
#endif
|
|
if (mass_oxygen_unknown != NULL) {
|
|
store_mb(&(x[i]->mass_water),
|
|
&(mass_oxygen_unknown->f),
|
|
1 / gfw_water);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* For Pizer model add lg unknown for each aqueous species
|
|
*/
|
|
|
|
if (pitzer_model == TRUE) {
|
|
j0 = count_unknowns;
|
|
j = count_unknowns + count_s_x;
|
|
k = j0;
|
|
for (i = j0; i < j; i++) {
|
|
if (s_x[i - j0]->type == EX) continue;
|
|
if (s_x[i - j0]->type == SURF) continue;
|
|
x[k]->number = k;
|
|
x[k]->type = PITZER_GAMMA;
|
|
x[k]->s = s_x[i - j0];
|
|
x[k]->description = s_x[i - j0]->name;
|
|
k++;
|
|
count_unknowns++;
|
|
}
|
|
}
|
|
/*
|
|
* Rewrite phases to current master species
|
|
*/
|
|
for (i=0; i < count_phases; i++) {
|
|
count_trxn=0;
|
|
trxn_add(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].rxn, coef_e, TRUE);
|
|
}
|
|
/*
|
|
* Rewrite reaction to current master species
|
|
*/
|
|
write_mass_action_eqn_x(STOP);
|
|
trxn_reverse_k();
|
|
rxn_free(phases[i]->rxn_x);
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\nPhase: %s\n", phases[i]->name);
|
|
trxn_print();
|
|
}
|
|
phases[i]->rxn_x = rxn_alloc(count_trxn+1);
|
|
trxn_copy (phases[i]->rxn_x);
|
|
write_phase_sys_total(i);
|
|
}
|
|
}
|
|
build_solution_phase_boundaries();
|
|
build_pure_phases();
|
|
build_min_exch();
|
|
build_min_surface();
|
|
build_gas_phase();
|
|
build_s_s_assemblage();
|
|
/*
|
|
* Sort species list, by master only
|
|
*/
|
|
qsort (&species_list[0], (size_t) count_species_list,
|
|
(size_t) sizeof(struct species_list), species_list_compare_master);
|
|
/*
|
|
* Print size information to logfile
|
|
*/
|
|
/*
|
|
output_msg(OUTPUT_LOG, "\nMemory used:\n");
|
|
output_msg(OUTPUT_LOG, "count_s: %d\tmax_s: %d\t\tbytes: %d\n", count_s, max_s, (int) (max_s * sizeof(struct species)));
|
|
output_msg(OUTPUT_LOG, "count_s_x: %d\tmax_s_x: %d\t\tbytes: %d\n", count_s_x, max_s_x, (int) (max_s_x * sizeof(struct species *)));
|
|
output_msg(OUTPUT_LOG, "count_sum_mb1: %d\tmax_sum_mb1: %d\t\tbytes: %d\n", count_sum_mb1, max_sum_mb1, (int) (max_sum_mb1 * sizeof(struct list1)));
|
|
output_msg(OUTPUT_LOG, "count_sum_mb2: %d\tmax_sum_mb2: %d\t\tbytes: %d\n", count_sum_mb2, max_sum_mb2, (int) (max_sum_mb2 * sizeof(struct list2)));
|
|
output_msg(OUTPUT_LOG, "count_sum_jacob0: %d\tmax_sum_jacob0: %d\t\tbytes: %d\n", count_sum_jacob0, max_sum_jacob0, (int) (max_sum_jacob0 * sizeof(struct list0)));
|
|
output_msg(OUTPUT_LOG, "count_sum_jacob1: %d\tmax_sum_jacob1: %d\t\tbytes: %d\n", count_sum_jacob1, max_sum_jacob1, (int) (max_sum_jacob1 * sizeof(struct list1)));
|
|
output_msg(OUTPUT_LOG_LOG, "count_sum_jacob2: %d\tmax_sum_jacob2: %d\t\tbytes: %d\n", count_sum_jacob2, max_sum_jacob2, (int) (max_sum_jacob2 * sizeof(struct list2)));
|
|
output_msg(OUTPUT_LOG_LOG, "count_sum_delta: %d\tmax_sum_delta: %d\t\tbytes: %d\n", count_sum_delta, max_sum_delta, (int) (max_sum_delta * sizeof(struct list2)));
|
|
output_msg(OUTPUT_LOG, "count_unknowns: %d\n", count_unknowns);
|
|
output_msg(OUTPUT_LOG_LOG, "\n");
|
|
*/
|
|
/*
|
|
* Save model description
|
|
*/
|
|
/* if (state >= REACTION) save_model(); */
|
|
save_model();
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int build_pure_phases(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Includes calculation of inverse saturation index in sum_mb.
|
|
* Puts coefficients in iap and mass balance equations for each phase.
|
|
*/
|
|
int i;
|
|
int stop, j, k, l;
|
|
char token[MAX_LENGTH];
|
|
char *ptr;
|
|
struct master *master_ptr;
|
|
struct rxn_token *rxn_ptr;
|
|
/*
|
|
* Build into sums the logic to calculate inverse saturation indices for
|
|
* pure phases
|
|
*/
|
|
if (pure_phase_unknown == NULL) return(OK);
|
|
/*
|
|
* Calculate inverse saturation index
|
|
*/
|
|
for (i = 0; i < count_unknowns; i++) {
|
|
if (x[i]->type != PP || x[i]->phase->rxn_x == NULL) continue;
|
|
if (pure_phase_unknown == NULL) pure_phase_unknown = x[i];
|
|
|
|
store_mb(&(x[i]->phase->lk), &(x[i]->f), 1.0);
|
|
store_mb(&(x[i]->si), &(x[i]->f), 1.0);
|
|
|
|
for (rxn_ptr = x[i]->phase->rxn_x->token + 1; rxn_ptr->s != NULL; rxn_ptr++) {
|
|
store_mb(&(rxn_ptr->s->la), &(x[i]->f), -rxn_ptr->coef);
|
|
}
|
|
}
|
|
for (i = 0; i < count_unknowns; i++) {
|
|
/*
|
|
* rxn_x is null if an element in phase is not in solution
|
|
*/
|
|
if (x[i]->type != PP || x[i]->phase->rxn_x == NULL) continue;
|
|
/*
|
|
* Put coefficients into IAP equations
|
|
*/
|
|
for (rxn_ptr=x[i]->phase->rxn_x->token + 1; rxn_ptr->s != NULL; rxn_ptr++) {
|
|
if (rxn_ptr->s->secondary != NULL &&
|
|
rxn_ptr->s->secondary->in == TRUE ) {
|
|
master_ptr=rxn_ptr->s->secondary;
|
|
} else {
|
|
master_ptr=rxn_ptr->s->primary;
|
|
}
|
|
if (master_ptr == NULL || master_ptr->unknown == NULL) continue;
|
|
store_jacob0(x[i]->number, master_ptr->unknown->number, rxn_ptr->coef);
|
|
}
|
|
/*
|
|
* Put coefficients into mass balance equations
|
|
*/
|
|
count_elts = 0;
|
|
paren_count = 0;
|
|
if (x[i]->pure_phase->add_formula != NULL) {
|
|
strcpy(token, x[i]->pure_phase->add_formula);
|
|
ptr = token;
|
|
get_elts_in_species (&ptr, 1.0);
|
|
} else {
|
|
strcpy(token, x[i]->phase->formula);
|
|
ptr = token;
|
|
get_elts_in_species (&ptr, 1.0);
|
|
}
|
|
/*
|
|
* Go through elements in phase
|
|
*/
|
|
|
|
#ifdef COMBINE
|
|
change_hydrogen_in_elt_list(0);
|
|
#endif
|
|
for (j = 0; j < count_elts; j++) {
|
|
|
|
if (strcmp(elt_list[j].elt->name, "H") == 0 && mass_hydrogen_unknown != NULL) {
|
|
store_jacob0(mass_hydrogen_unknown->number, x[i]->number, -elt_list[j].coef);
|
|
store_sum_deltas(&(delta[i]), &mass_hydrogen_unknown->delta, elt_list[j].coef);
|
|
|
|
} else if (strcmp(elt_list[j].elt->name, "O") == 0 && mass_oxygen_unknown != NULL) {
|
|
store_jacob0(mass_oxygen_unknown->number, x[i]->number, -elt_list[j].coef);
|
|
store_sum_deltas(&(delta[i]), &mass_oxygen_unknown->delta, elt_list[j].coef);
|
|
|
|
} else {
|
|
master_ptr = elt_list[j].elt->primary;
|
|
if (master_ptr->in == FALSE) {
|
|
master_ptr = master_ptr->s->secondary;
|
|
}
|
|
if (master_ptr == NULL || master_ptr->in == FALSE) {
|
|
if (state != ADVECTION && state != TRANSPORT && state != PHAST ) {
|
|
sprintf(error_string, "Element in phase, %s, is not in model.", x[i]->phase->name);
|
|
warning_msg(error_string);
|
|
}
|
|
if (master_ptr != NULL) {
|
|
master_ptr->s->la = -999.9;
|
|
}
|
|
/*
|
|
* Master species is in model
|
|
*/
|
|
} else if (master_ptr->in == TRUE ) {
|
|
store_jacob0(master_ptr->unknown->number, x[i]->number,
|
|
-elt_list[j].coef);
|
|
store_sum_deltas(&delta[i], &master_ptr->unknown->delta, elt_list[j].coef);
|
|
/*
|
|
* Master species in equation needs to be rewritten
|
|
*/
|
|
} else if (master_ptr->in == REWRITE ) {
|
|
stop = FALSE;
|
|
for (k=0; k < count_unknowns; k++) {
|
|
if (x[k]->type != MB) continue;
|
|
for (l = 0; x[k]->master[l] != NULL; l++) {
|
|
if (x[k]->master[l] == master_ptr) {
|
|
store_jacob0(x[k]->master[0]->unknown->number, x[i]->number,
|
|
-elt_list[j].coef);
|
|
store_sum_deltas(&delta[i], &x[k]->master[0]->unknown->delta,
|
|
elt_list[j].coef);
|
|
stop = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (stop == TRUE) break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int build_solution_phase_boundaries(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
int i;
|
|
struct master *master_ptr;
|
|
struct rxn_token *rxn_ptr;
|
|
/*
|
|
* Build into sums the logic to calculate inverse saturation indices for
|
|
* solution phase boundaries
|
|
*/
|
|
if (solution_phase_boundary_unknown == NULL) return(OK);
|
|
/*
|
|
* Calculate inverse saturation index
|
|
*/
|
|
for (i = 0; i < count_unknowns; i++) {
|
|
if (x[i]->type != SOLUTION_PHASE_BOUNDARY) continue;
|
|
|
|
store_mb(&(x[i]->phase->lk), &(x[i]->f), 1.0);
|
|
store_mb(&(x[i]->si), &(x[i]->f), 1.0);
|
|
if (x[i]->phase->in != TRUE) {
|
|
sprintf(error_string, "Solution does not contain all elements for phase-boundary mineral, %s.", x[i]->phase->name);
|
|
error_msg(error_string, CONTINUE);
|
|
input_error++;
|
|
break;
|
|
}
|
|
for (rxn_ptr = x[i]->phase->rxn_x->token + 1; rxn_ptr->s != NULL; rxn_ptr++) {
|
|
store_mb(&(rxn_ptr->s->la), &(x[i]->f), -rxn_ptr->coef);
|
|
}
|
|
}
|
|
if (input_error > 0) return(ERROR);
|
|
/*
|
|
* Put coefficients into array
|
|
*/
|
|
for (i = 0; i < count_unknowns; i++) {
|
|
if (x[i]->type != SOLUTION_PHASE_BOUNDARY) continue;
|
|
for (rxn_ptr=x[i]->phase->rxn_x->token + 1; rxn_ptr->s != NULL; rxn_ptr++) {
|
|
if (rxn_ptr->s->secondary != NULL &&
|
|
rxn_ptr->s->secondary->in == TRUE ) {
|
|
master_ptr=rxn_ptr->s->secondary;
|
|
} else {
|
|
master_ptr=rxn_ptr->s->primary;
|
|
}
|
|
if (master_ptr->unknown == NULL) continue;
|
|
store_jacob0(x[i]->number, master_ptr->unknown->number, rxn_ptr->coef);
|
|
}
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int build_species_list(int n)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Builds a list that includes an entry for each master species in each
|
|
* secondary reaction. Used for summing species of each element and
|
|
* printing results.
|
|
*/
|
|
int j;
|
|
struct master *master_ptr;
|
|
/*
|
|
* Check space and store reaction token name and pointer to species
|
|
*/
|
|
if (count_species_list + count_elts >= max_species_list) {
|
|
space ((void **) ((void *) &species_list), count_species_list + count_elts,
|
|
&max_species_list, sizeof (struct species_list));
|
|
}
|
|
/*
|
|
* Treat species made only with H+, e-, and H2O specially
|
|
*/
|
|
if ( is_special(s[n]) == TRUE ) {
|
|
species_list[count_species_list].master_s = s_hplus;
|
|
species_list[count_species_list].s = s[n];
|
|
species_list[count_species_list].coef = 0.0;
|
|
count_species_list++;
|
|
return(OK);
|
|
}
|
|
/*
|
|
* Treat exchange species specially
|
|
*/
|
|
if (s[n]->type == EX) {
|
|
if (s[n]->primary != NULL) return(OK); /* master species has zero molality */
|
|
for (j = 0; j < count_elts; j++) {
|
|
if ( elt_list[j].elt->master->s->type != EX ) continue;
|
|
master_ptr = elt_list[j].elt->master;
|
|
species_list[count_species_list].master_s = elt_list[j].elt->master->s;
|
|
species_list[count_species_list].s = s[n];
|
|
species_list[count_species_list].coef = master_ptr->coef *
|
|
elt_list[j].coef;
|
|
count_species_list++;
|
|
}
|
|
return(OK);
|
|
}
|
|
/*
|
|
* Treat surface species specially
|
|
*/
|
|
if (s[n]->type == SURF_PSI) return(OK);
|
|
if (s[n]->type == SURF) {
|
|
for (j=0; j < count_elts; j++) {
|
|
if ( elt_list[j].elt->master->s->type != SURF ) continue;
|
|
master_ptr = elt_list[j].elt->master;
|
|
species_list[count_species_list].master_s = elt_list[j].elt->master->s;
|
|
species_list[count_species_list].s = s[n];
|
|
species_list[count_species_list].coef = master_ptr->coef *
|
|
elt_list[j].coef;
|
|
count_species_list++;
|
|
}
|
|
return(OK);
|
|
}
|
|
/*
|
|
* Other aqueous species
|
|
*/
|
|
for (j=0; j < count_elts; j++) {
|
|
if ( is_special(elt_list[j].elt->master->s) == TRUE ) continue;
|
|
if (elt_list[j].elt->master->s->secondary != NULL) {
|
|
master_ptr = elt_list[j].elt->master->s->secondary;
|
|
} else {
|
|
master_ptr = elt_list[j].elt->master->s->primary;
|
|
}
|
|
species_list[count_species_list].master_s = master_ptr->s;
|
|
species_list[count_species_list].s = s[n];
|
|
/*
|
|
* Find coefficient for element represented by master species
|
|
*/
|
|
species_list[count_species_list].coef = master_ptr->coef *
|
|
elt_list[j].coef;
|
|
count_species_list++;
|
|
}
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int clear (void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
int i;
|
|
/*
|
|
* Resets information for setting up a new model
|
|
*/
|
|
struct solution *solution_ptr;
|
|
/*
|
|
* Clear species solution-dependent data
|
|
*/
|
|
solution_ptr = use.solution_ptr;
|
|
|
|
for (i=0; i < count_s; i++) {
|
|
s[i]->in=FALSE;
|
|
}
|
|
/*
|
|
* Set pe structure
|
|
*/
|
|
pe_data_free(pe_x);
|
|
pe_x = pe_data_dup(solution_ptr->pe);
|
|
default_pe_x = solution_ptr->default_pe;
|
|
/*
|
|
* Clear master species solution-dependent data
|
|
*/
|
|
for (i=0; i < count_master; i++) {
|
|
master[i]->in=FALSE;
|
|
master[i]->unknown=NULL;
|
|
master[i]->pe_rxn = &(pe_x[solution_ptr->default_pe].rxn);
|
|
/*
|
|
* copy primary reaction to secondary reaction
|
|
*/
|
|
rxn_free (master[i]->rxn_secondary);
|
|
master[i]->rxn_secondary = rxn_dup (master[i]->rxn_primary);
|
|
}
|
|
|
|
if (state == INITIAL_SOLUTION) {
|
|
s_h2o->secondary->in=TRUE;
|
|
s_hplus->secondary->in=TRUE;
|
|
} else {
|
|
s_h2o->primary->in=TRUE;
|
|
s_hplus->primary->in=TRUE;
|
|
}
|
|
s_eminus->primary->in=TRUE;
|
|
/*
|
|
* Set all unknown pointers to NULL
|
|
*/
|
|
mb_unknown = NULL;
|
|
ah2o_unknown = NULL;
|
|
mass_hydrogen_unknown = NULL;
|
|
mass_oxygen_unknown = NULL;
|
|
mu_unknown = NULL;
|
|
alkalinity_unknown = NULL;
|
|
carbon_unknown = NULL;
|
|
ph_unknown = NULL;
|
|
pe_unknown = NULL;
|
|
charge_balance_unknown = NULL;
|
|
solution_phase_boundary_unknown = NULL;
|
|
pure_phase_unknown = NULL;
|
|
exchange_unknown = NULL;
|
|
surface_unknown = NULL;
|
|
gas_unknown = NULL;
|
|
s_s_unknown = NULL;
|
|
/*
|
|
* Free arrays used in model
|
|
*/
|
|
free_model_allocs();
|
|
|
|
return (OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int convert_units(struct solution *solution_ptr)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Converts solution concentrations to moles/kg water
|
|
* Uses totals.input conc to calculate totals.moles.
|
|
*/
|
|
int i, l;
|
|
LDBLE sum_solutes;
|
|
char c;
|
|
struct master *master_ptr;
|
|
struct conc *tot_ptr;
|
|
char token[MAX_LENGTH];
|
|
char *ptr;
|
|
/*
|
|
* Convert units
|
|
*/
|
|
sum_solutes= exp(-solution_ptr->ph * LOG_10);
|
|
for ( i=0; solution_ptr->totals[i].description != NULL; i++) {
|
|
master_ptr = master_bsearch (solution_ptr->totals[i].description);
|
|
if (master_ptr != NULL) {
|
|
if (master_ptr->minor_isotope == TRUE) continue;
|
|
}
|
|
tot_ptr=&(solution_ptr->totals[i]);
|
|
tot_ptr->moles = 0.0;
|
|
if (strcmp(tot_ptr->description,"H(1)") == 0 ||
|
|
strcmp(tot_ptr->description,"E" ) == 0 ) {
|
|
continue;
|
|
}
|
|
if (tot_ptr->input_conc <= 0) continue;
|
|
/*
|
|
* Get gfw
|
|
*/
|
|
/* use given gfw if gfw > 0.0 */
|
|
/* use formula give with "as" */
|
|
if (tot_ptr->gfw <= 0.0) {
|
|
if ( tot_ptr->as != NULL ) {
|
|
/* use given chemical formula to calculate gfw */
|
|
if (compute_gfw(tot_ptr->as, &(tot_ptr->gfw) ) == ERROR ) {
|
|
sprintf(error_string, "Could not compute gfw, %s.", tot_ptr->as);
|
|
error_msg(error_string, CONTINUE);
|
|
input_error++;
|
|
}
|
|
if (strcmp (tot_ptr->description,"Alkalinity") == 0 &&
|
|
strcmp (tot_ptr->as, "CaCO3") == 0) {
|
|
tot_ptr->gfw /= 2.;
|
|
sprintf(error_string, "Equivalent wt for alkalinity should be Ca.5(CO3).5. Using %g g/eq.", (double) tot_ptr->gfw);
|
|
warning_msg(error_string);
|
|
}
|
|
/* use gfw of master species */
|
|
} else {
|
|
ptr = tot_ptr->description;
|
|
copy_token(token, &ptr, &l);
|
|
master_ptr = master_bsearch (token);
|
|
if (master_ptr != NULL) {
|
|
/* use gfw for element redox state*/
|
|
tot_ptr->gfw = master_ptr->gfw;
|
|
} else {
|
|
sprintf(error_string, "Could not find gfw, %s.", tot_ptr->description);
|
|
error_msg(error_string, CONTINUE);
|
|
input_error++;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Convert liters to kg solution
|
|
*/
|
|
tot_ptr->moles=tot_ptr->input_conc;
|
|
if (strstr (solution_ptr->units, "/l") != NULL ) {
|
|
tot_ptr->moles *= 1.0/(solution_ptr->density);
|
|
}
|
|
/*
|
|
* Convert milli or micro
|
|
*/
|
|
c=tot_ptr->units[0];
|
|
if (c == 'm') {
|
|
tot_ptr->moles *= 1e-3;
|
|
} else if ( c == 'u' ) {
|
|
tot_ptr->moles *= 1e-6;
|
|
}
|
|
/*
|
|
* Sum grams of solute, convert from moles necessary
|
|
*/
|
|
if (strstr (tot_ptr->units, "g/kgs") != NULL ||
|
|
strstr (tot_ptr->units,"g/l") != NULL ) {
|
|
sum_solutes += tot_ptr->moles;
|
|
} else if (strstr (tot_ptr->units, "Mol/kgs") != NULL ||
|
|
strstr (tot_ptr->units, "Mol/l") != NULL ||
|
|
strstr (tot_ptr->units, "eq/l") != NULL ) {
|
|
sum_solutes += (tot_ptr->moles)*(tot_ptr->gfw);
|
|
}
|
|
/*
|
|
* Convert grams to moles, if necessary
|
|
*/
|
|
if ( strstr (tot_ptr->units, "g/") != NULL && tot_ptr->gfw != 0.0) {
|
|
tot_ptr->moles /= tot_ptr->gfw;
|
|
}
|
|
}
|
|
/*
|
|
* Convert /kgs to /kgw
|
|
*/
|
|
if (strstr( solution_ptr->units, "kgs") != NULL ||
|
|
strstr( solution_ptr->units, "/l") != NULL ) {
|
|
mass_water_aq_x = 1.0 - 1e-3*sum_solutes;
|
|
for ( i=0; solution_ptr->totals[i].description != NULL; i++) {
|
|
solution_ptr->totals[i].moles /= mass_water_aq_x;
|
|
}
|
|
}
|
|
/*
|
|
* Scale by mass of water in solution
|
|
*/
|
|
mass_water_aq_x = solution_ptr->mass_water;
|
|
for ( i=0; solution_ptr->totals[i].description != NULL; i++) {
|
|
solution_ptr->totals[i].moles *= mass_water_aq_x;
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
struct master **get_list_master_ptrs(char *ptr, struct master *master_ptr)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Input: ptr contains a list of one or more master species names
|
|
* Output: space is allocated and a list of master species pointers is
|
|
* returned.
|
|
*/
|
|
int j, l, count_list;
|
|
char token[MAX_LENGTH];
|
|
struct master **master_ptr_list;
|
|
struct master *master_ptr0;
|
|
/*
|
|
* Make list of master species pointers
|
|
*/
|
|
count_list = 0;
|
|
master_ptr_list = unknown_alloc_master();
|
|
master_ptr0=master_ptr;
|
|
if (master_ptr0 == master_ptr->s->primary) {
|
|
/*
|
|
* First in list is primary species
|
|
*/
|
|
for (j = 0; j < count_master; j++) {
|
|
if (master[j] == master_ptr0) break;
|
|
}
|
|
j++;
|
|
/*
|
|
* Element has only one valence
|
|
*/
|
|
if ( j >= count_master || master[j]->elt->primary != master_ptr0) {
|
|
master_ptr_list[count_list++] = master_ptr0;
|
|
/*
|
|
* Element has multiple valences
|
|
*/
|
|
} else {
|
|
if (master_ptr0->s->secondary == NULL) {
|
|
sprintf(error_string, "Master species for valence states of element %s are not correct.\n\tPossibly related to master species for %s.", master_ptr0->elt->name, master[j]->elt->name);
|
|
error_msg(error_string, CONTINUE);
|
|
input_error++;
|
|
}
|
|
master_ptr_list[count_list++] = master_ptr0->s->secondary;
|
|
while (j < count_master && master[j]->elt->primary == master_ptr0) {
|
|
if (master[j]->s->primary == NULL) {
|
|
master_ptr_list = (struct master **) PHRQ_realloc((void *) master_ptr_list, (size_t) (count_list + 2) * sizeof(struct master *));
|
|
if (master_ptr_list == NULL) malloc_error();
|
|
master_ptr_list[count_list++] = master[j];
|
|
}
|
|
j++;
|
|
}
|
|
}
|
|
} else {
|
|
/*
|
|
* First in list is secondary species, Include all valences from input
|
|
*/
|
|
master_ptr_list[count_list++]=master_ptr0;
|
|
while (copy_token(token,&ptr, &l) != EMPTY) {
|
|
master_ptr = master_bsearch(token);
|
|
if (master_ptr != NULL) {
|
|
master_ptr_list = (struct master **) PHRQ_realloc((void *) master_ptr_list, (size_t) (count_list + 2) * sizeof(struct master *));
|
|
if (master_ptr_list == NULL) malloc_error();
|
|
master_ptr_list[count_list++] = master_ptr;
|
|
}
|
|
}
|
|
}
|
|
master_ptr_list[count_list] = NULL;
|
|
return (master_ptr_list);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int inout(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
int i;
|
|
struct rxn_token_temp *token_ptr;
|
|
/*
|
|
* Routine goes through trxn to determine if each master species is
|
|
* in this model.
|
|
* Assumes equation is written in terms of primary and secondary species
|
|
* Checks to see if in is TRUE or REWRITE for each species
|
|
* Returns TRUE if in model
|
|
* FALSE if not
|
|
*/
|
|
for (i=1; i < count_trxn; i++) {
|
|
token_ptr = &(trxn.token[i]);
|
|
/* Check primary master species in */
|
|
if (token_ptr->s->primary != NULL && (token_ptr->s->primary->in == TRUE) ) continue;
|
|
/* Check secondary master species */
|
|
if ( (token_ptr->s->secondary != NULL) && (token_ptr->s->secondary->in != FALSE) ) {
|
|
continue;
|
|
}
|
|
/* Must be primary master species that is out */
|
|
return(FALSE);
|
|
}
|
|
return(TRUE);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int is_special(struct species *spec)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Checks to see if a species is composed of only H, O, and e-
|
|
* Returns TRUE if true
|
|
* FALSE if not
|
|
*/
|
|
int special;
|
|
struct rxn_token *token_ptr;
|
|
|
|
special = TRUE;
|
|
for ( token_ptr = spec->rxn_s->token + 1; token_ptr->s != NULL; token_ptr++) {
|
|
if (token_ptr->s != s_hplus &&
|
|
token_ptr->s != s_h2o &&
|
|
token_ptr->s != s_eminus ) {
|
|
special = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
return(special);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int store_mb_unknowns(struct unknown *unknown_ptr, LDBLE *LDBLE_ptr, LDBLE coef, LDBLE *gamma_ptr)
|
|
/* ---------------------------------------------------------------------- */
|
|
/*
|
|
* Takes an unknown pointer and a coefficient and puts in
|
|
* list of mb_unknowns
|
|
*/
|
|
{
|
|
if (equal(coef, 0.0, TOL) == TRUE) return(OK);
|
|
if ( (count_mb_unknowns + 1) >= max_mb_unknowns) {
|
|
space ((void **) ((void *) &mb_unknowns), count_mb_unknowns + 1, &max_mb_unknowns,
|
|
sizeof(struct unknown_list));
|
|
}
|
|
mb_unknowns[count_mb_unknowns].unknown = unknown_ptr;
|
|
mb_unknowns[count_mb_unknowns].source = LDBLE_ptr;
|
|
mb_unknowns[count_mb_unknowns].gamma_source = gamma_ptr;
|
|
mb_unknowns[count_mb_unknowns].coef = coef;
|
|
count_mb_unknowns++;
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int mb_for_species_aq(int n)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Make list of mass balance and charge balance equations in which
|
|
* to insert species n.
|
|
*
|
|
* count_mb_unknowns - number of equations and summation relations
|
|
* mb_unknowns.unknown - pointer to unknown which contains row number
|
|
* mb_unknowns.source - pointer to the LDBLE number to be multiplied
|
|
* by coef, usually moles.
|
|
* mb_unknowns.coef - coefficient of s[n] in equation or relation
|
|
*/
|
|
int i, j;
|
|
struct master *master_ptr;
|
|
|
|
count_mb_unknowns = 0;
|
|
/*
|
|
* e- does not appear in any mass balances
|
|
*/
|
|
if (s[n]->type == EMINUS) return(OK);
|
|
/*
|
|
* Do not include diffuse layer in cb, alk, ah2o, mu
|
|
*/
|
|
if (charge_balance_unknown != NULL && s[n]->type < H2O) {
|
|
store_mb_unknowns(charge_balance_unknown, &s[n]->moles, s[n]->z, &s[n]->dg);
|
|
}
|
|
if (alkalinity_unknown != NULL && s[n]->type < H2O ) {
|
|
store_mb_unknowns(alkalinity_unknown, &s[n]->moles, s[n]->alk, &s[n]->dg);
|
|
}
|
|
if (ah2o_unknown != NULL && s[n]->type < H2O ) {
|
|
store_mb_unknowns(ah2o_unknown, &s[n]->moles, 1.0, &s[n]->dg);
|
|
}
|
|
if (mu_unknown != NULL && s[n]->type < H2O ) {
|
|
store_mb_unknowns(mu_unknown, &s[n]->moles, s[n]->z * s[n]->z, &s[n]->dg);
|
|
}
|
|
/*
|
|
* Include diffuse layer in hydrogen and oxygen mass balance
|
|
*/
|
|
if (mass_hydrogen_unknown != NULL ) {
|
|
if (diffuse_layer_x == TRUE && state >= REACTION) {
|
|
#ifdef COMBINE
|
|
#ifndef COMBINE_CHARGE
|
|
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 - 2 *s[n]->o - s[n]->z, &s[n]->dg_total_g);
|
|
#endif
|
|
#else
|
|
store_mb_unknowns(mass_hydrogen_unknown, &s[n]->tot_g_moles, s[n]->h, &s[n]->dg_total_g );
|
|
#endif
|
|
} else {
|
|
#ifdef COMBINE
|
|
#ifndef COMBINE_CHARGE
|
|
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 - 2* s[n]->o - s[n]->z, &s[n]->dg);
|
|
#endif
|
|
#else
|
|
store_mb_unknowns(mass_hydrogen_unknown, &s[n]->moles, s[n]->h, &s[n]->dg);
|
|
#endif
|
|
}
|
|
}
|
|
if (mass_oxygen_unknown != NULL ) {
|
|
if (diffuse_layer_x == TRUE && 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.surface_ptr != NULL && s[n]->type < H2O && diffuse_layer_x == TRUE) {
|
|
j = 0;
|
|
for(i = 0; i < count_unknowns; i++) {
|
|
if (x[i]->type == SURFACE_CB) {
|
|
store_mb_unknowns(x[i], &s[n]->diff_layer[j].g_moles, s[n]->z, &s[n]->diff_layer[j].dg_g_moles);
|
|
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->type == SOLUTION_PHASE_BOUNDARY ) {
|
|
continue;
|
|
}
|
|
if (diffuse_layer_x == TRUE && 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 mb_for_species_ex(int n)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Make list of mass balance and charge balance equations in which
|
|
* to insert exchange species n.
|
|
*
|
|
* count_mb_unknowns - number of equations and summation relations
|
|
* mb_unknowns.source - pointer to the LDBLE number to be multiplied
|
|
* by coef, usually moles.
|
|
* mb_unknowns.unknown - pointer to unknown which contains row number
|
|
* mb_unknowns.coef - coefficient of s[n] in equation or relation
|
|
*/
|
|
int i;
|
|
struct master *master_ptr;
|
|
count_mb_unknowns = 0;
|
|
/*
|
|
* Master species for exchange do not appear in any mass balances
|
|
*/
|
|
if (s[n]->type == EX && s[n]->primary != NULL) return(OK);
|
|
/*
|
|
* Include diffuse layer in hydrogen and oxygen mass balance
|
|
*/
|
|
if (charge_balance_unknown != NULL) {
|
|
store_mb_unknowns(charge_balance_unknown, &s[n]->moles, s[n]->z, &s[n]->dg);
|
|
}
|
|
if (mass_hydrogen_unknown != NULL ) {
|
|
#ifdef COMBINE
|
|
#ifndef COMBINE_CHARGE
|
|
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 - 2 * s[n]->o - s[n]->z, &s[n]->dg);
|
|
#endif
|
|
#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 mb_for_species_surf(int n)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Make list of mass balance and charge balance equations in which
|
|
* to insert species n.
|
|
*
|
|
* count_mb_unknowns - number of equations and summation relations
|
|
* mb_unknowns.source - pointer to the LDBLE number to be multiplied
|
|
* by coef, usually moles.
|
|
* mb_unknowns.unknown - pointer to unknown which contains row number
|
|
* mb_unknowns.coef - coefficient of s[n] in equation or relation
|
|
*/
|
|
int i;
|
|
struct master *master_ptr;
|
|
|
|
count_mb_unknowns = 0;
|
|
/*
|
|
* Include in charge balance, if diffuse_layer_x == FALSE
|
|
*/
|
|
if (charge_balance_unknown != NULL && diffuse_layer_x == FALSE) {
|
|
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
|
|
#ifndef COMBINE_CHARGE
|
|
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 - 2*s[n]->o -s[n]->z, &s[n]->dg);
|
|
|
|
#endif
|
|
#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) {
|
|
store_mb_unknowns(master_ptr->unknown, &s[n]->moles, s[n]->z, &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 reprep (void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* If a basis species has been switched, makes new model.
|
|
* Unknowns are not changed, but mass-action equations are
|
|
* rewritten and lists for mass balance and jacobian are regenerated
|
|
*/
|
|
int i;
|
|
/*
|
|
* Initialize s, master, and unknown pointers
|
|
*/
|
|
for (i=0; i < count_master; i++) {
|
|
if (master[i]->in == FALSE) continue;
|
|
rxn_free (master[i]->rxn_secondary);
|
|
master[i]->rxn_secondary = rxn_dup (master[i]->rxn_primary);
|
|
}
|
|
resetup_master();
|
|
/*
|
|
* Set unknown pointers, unknown types, validity checks
|
|
*/
|
|
tidy_redox();
|
|
if (input_error > 0) {
|
|
error_msg("Program terminating due to input errors.", STOP);
|
|
}
|
|
/*
|
|
* Free arrays built in build_model
|
|
*/
|
|
s_x = (struct species **) free_check_null(s_x);
|
|
sum_mb1 = (struct list1 *) free_check_null(sum_mb1);
|
|
sum_mb2 = (struct list2 *) free_check_null(sum_mb2);
|
|
sum_jacob0 = (struct list0 *) free_check_null(sum_jacob0);
|
|
sum_jacob1 = (struct list1 *) free_check_null(sum_jacob1);
|
|
sum_jacob2 = (struct list2 *) free_check_null(sum_jacob2);
|
|
sum_delta = (struct list2 *) free_check_null(sum_delta);
|
|
/*
|
|
* Build model again
|
|
*/
|
|
build_model();
|
|
same_model = FALSE;
|
|
k_temp(tc_x);
|
|
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int resetup_master (void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* For basis switch, rewrite equations for master species
|
|
* Set master_ptr->rxn_secondary,
|
|
* master_ptr->pe_rxn,
|
|
* and special cases for alkalinity, carbon, and pH.
|
|
*/
|
|
int i, j;
|
|
struct master *master_ptr, *master_ptr0;
|
|
|
|
for (i = 0; i < count_unknowns; i++ ) {
|
|
if (x[i]->type != MB) continue;
|
|
master_ptr0=x[i]->master[0];
|
|
for (j = 0; (master_ptr = x[i]->master[j]) != NULL; j++) {
|
|
/*
|
|
* Set flags
|
|
*/
|
|
if (j == 0) {
|
|
if (master_ptr->s->primary == NULL) {
|
|
rxn_free(master_ptr->rxn_secondary);
|
|
master_ptr->rxn_secondary = rxn_dup(master_ptr->s->rxn_s);
|
|
}
|
|
} else {
|
|
if (master_ptr0->s->primary == NULL) {
|
|
rewrite_master_to_secondary(master_ptr, master_ptr0);
|
|
rxn_free(master_ptr->rxn_secondary);
|
|
master_ptr->rxn_secondary = rxn_alloc(count_trxn+1);
|
|
trxn_copy (master_ptr->rxn_secondary);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int write_mass_action_eqn_x (int stop)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Reduce mass-action equation to the master species that are in the model
|
|
*/
|
|
LDBLE coef_e;
|
|
int count, repeat;
|
|
int i, count_rxn_orig;
|
|
/*
|
|
* Rewrite any secondary master species flagged REWRITE
|
|
* Replace pe if necessary
|
|
*/
|
|
count = 0;
|
|
repeat = TRUE;
|
|
while (repeat == TRUE) {
|
|
count++;
|
|
if (count > MAX_ADD_EQUATIONS) {
|
|
sprintf(error_string, "Could not reduce equation "
|
|
"to primary and secondary species that are "
|
|
"in the model\n\t Species: %s.",
|
|
trxn.token[0].s->name);
|
|
if (stop == STOP) {
|
|
input_error++;
|
|
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 ) {
|
|
trxn_add( *(trxn.token[i].s->secondary->pe_rxn),
|
|
trxn.token[i].coef*coef_e, FALSE);
|
|
}
|
|
}
|
|
}
|
|
trxn_combine();
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int 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;
|
|
char token[MAX_LENGTH];
|
|
LDBLE sum_z;
|
|
struct master *master_ptr;
|
|
struct unknown *unknown_ptr;
|
|
|
|
if (use.surface_ptr->edl == FALSE) 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) {
|
|
sprintf(error_string,"Did not find a surface species in equation defining %s", trxn.token[0].name);
|
|
error_msg(error_string, CONTINUE);
|
|
sprintf(error_string,"One of the following must be defined with SURFACE_SPECIES:");
|
|
error_msg(error_string, CONTINUE);
|
|
for (i=1; i < count_trxn; i++) {
|
|
sprintf(error_string," %s", trxn.token[i].name);
|
|
error_msg(error_string, CONTINUE);
|
|
}
|
|
input_error++;
|
|
return(ERROR);
|
|
}
|
|
strcpy(token, master_ptr->elt->name);
|
|
unknown_ptr = find_surface_charge_unknown(token);
|
|
if (unknown_ptr == NULL) {
|
|
sprintf(error_string, "No potential unknown found for surface species %s.", token);
|
|
error_msg(error_string, STOP);
|
|
}
|
|
master_ptr = unknown_ptr->master[0]; /* potential for surface component */
|
|
/*
|
|
* Make sure there is space
|
|
*/
|
|
if (count_trxn + 1 >= max_trxn) {
|
|
space ((void **) &(trxn.token), count_trxn+1, &max_trxn,
|
|
sizeof(struct rxn_token_temp));
|
|
}
|
|
/*
|
|
* Include psi in mass action equation
|
|
*/
|
|
if (master_ptr != NULL) {
|
|
trxn.token[count_trxn].name = master_ptr->s->name;
|
|
trxn.token[count_trxn].s = master_ptr->s;
|
|
trxn.token[count_trxn].coef = -2.0 * sum_z;
|
|
count_trxn++;
|
|
} else {
|
|
output_msg(OUTPUT_MESSAGE, "How did this happen in add potential factor?\n");
|
|
}
|
|
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int add_surface_charge_balance(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;
|
|
char *ptr;
|
|
char token[MAX_LENGTH];
|
|
|
|
struct master *master_ptr;
|
|
struct unknown *unknown_ptr;
|
|
|
|
if (use.surface_ptr->edl == FALSE) 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) {
|
|
sprintf(error_string, "No surface master species found for surface species.");
|
|
error_msg(error_string, STOP);
|
|
}
|
|
/*
|
|
* Find potential unknown for surface species
|
|
*/
|
|
strcpy(token, master_ptr->elt->name);
|
|
unknown_ptr = find_surface_charge_unknown(token);
|
|
if (unknown_ptr == NULL) {
|
|
sprintf(error_string, "No potential unknown found for surface species %s.", token);
|
|
error_msg(error_string, STOP);
|
|
}
|
|
master_ptr = unknown_ptr->master[0]; /* potential for surface component */
|
|
/*
|
|
* Include charge balance in list for mass-balance equations
|
|
*/
|
|
ptr = master_ptr->elt->name;
|
|
get_secondary_in_species(&ptr, 1.0);
|
|
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int rewrite_master_to_secondary(struct master *master_ptr1, struct master *master_ptr2)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Write equation for secondary master species in terms of another secondary master species
|
|
* Store result in rxn_secondary of master_ptr.
|
|
*/
|
|
LDBLE coef1, coef2;
|
|
struct master *master_ptr_p1, *master_ptr_p2;
|
|
/*
|
|
* Check that the two master species have the same primary master species
|
|
*/
|
|
master_ptr_p1 = master_ptr1->elt->primary;
|
|
master_ptr_p2 = master_ptr2->elt->primary;
|
|
if (master_ptr_p1 != master_ptr_p2 ||
|
|
master_ptr_p1 == NULL ) {
|
|
sprintf(error_string, "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 ) {
|
|
sprintf(error_string, "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 setup_exchange (void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Fill in data for exchanger in unknowns structures
|
|
*/
|
|
int i, j;
|
|
struct master *master_ptr;
|
|
struct master **master_ptr_list;
|
|
|
|
if (use.exchange_ptr == NULL) return(OK);
|
|
|
|
for (j = 0; j < use.exchange_ptr->count_comps; j++) {
|
|
for (i = 0; use.exchange_ptr->comps[j].totals[i].elt != NULL; i++ ) {
|
|
/*
|
|
* Find master species
|
|
*/
|
|
master_ptr = use.exchange_ptr->comps[j].totals[i].elt->master;
|
|
if (master_ptr == NULL) {
|
|
sprintf(error_string, "Master species not in data "
|
|
"base for %s, skipping element.",
|
|
use.exchange_ptr->comps[j].totals[i].elt->name);
|
|
input_error++;
|
|
error_msg(error_string, CONTINUE);
|
|
continue;
|
|
}
|
|
if (master_ptr->type != EX) continue;
|
|
/*
|
|
* Check for data already given
|
|
*/
|
|
if (master_ptr->in != FALSE ) {
|
|
x[master_ptr->unknown->number]->moles += use.exchange_ptr->comps[j].totals[i].coef;
|
|
|
|
} else {
|
|
/*
|
|
* Set flags
|
|
*/
|
|
master_ptr_list = unknown_alloc_master();
|
|
master_ptr_list[0] = master_ptr;
|
|
master_ptr->in = TRUE;
|
|
/*
|
|
* Set unknown data
|
|
*/
|
|
x[count_unknowns]->type = EXCH;
|
|
x[count_unknowns]->exch_comp = &(use.exchange_ptr->comps[j]);
|
|
x[count_unknowns]->description = use.exchange_ptr->comps[j].totals[i].elt->name;
|
|
x[count_unknowns]->moles = use.exchange_ptr->comps[j].totals[i].coef;
|
|
x[count_unknowns]->master = master_ptr_list;
|
|
x[count_unknowns]->master[0]->unknown = x[count_unknowns];
|
|
count_unknowns++;
|
|
}
|
|
}
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int setup_gas_phase(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Fill in data for gas phase unknown (sum of partial pressures)
|
|
* in unknown structure
|
|
*/
|
|
int i;
|
|
if (use.gas_phase_ptr == NULL) return(OK);
|
|
/*
|
|
* 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 (i=0; i < use.gas_phase_ptr->count_comps; i++) {
|
|
x[count_unknowns]->moles += use.gas_phase_ptr->comps[i].moles;
|
|
}
|
|
if (x[count_unknowns]->moles <= 0) x[count_unknowns]->moles = MIN_TOTAL;
|
|
x[count_unknowns]->ln_moles = log(x[count_unknowns]->moles);
|
|
x[count_unknowns]->gas_phase = use.gas_phase_ptr;
|
|
gas_unknown = x[count_unknowns];
|
|
count_unknowns++;
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int setup_s_s_assemblage(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Fill in data for solid solution unknowns (sum of partial pressures)
|
|
* in unknown structure
|
|
*/
|
|
int i, j;
|
|
if (use.s_s_assemblage_ptr == NULL) return(OK);
|
|
/*
|
|
* One for each component in each solid solution
|
|
*/
|
|
s_s_unknown = NULL;
|
|
for (j = 0; j < use.s_s_assemblage_ptr->count_s_s; j++) {
|
|
for (i=0; i < use.s_s_assemblage_ptr->s_s[j].count_comps; i++) {
|
|
x[count_unknowns]->type = S_S_MOLES;
|
|
x[count_unknowns]->description = string_hsave(use.s_s_assemblage_ptr->s_s[j].comps[i].name);
|
|
x[count_unknowns]->moles = 0.0;
|
|
if (use.s_s_assemblage_ptr->s_s[j].comps[i].moles <= 0) {
|
|
use.s_s_assemblage_ptr->s_s[j].comps[i].moles = MIN_TOTAL;
|
|
}
|
|
x[count_unknowns]->moles = use.s_s_assemblage_ptr->s_s[j].comps[i].moles;
|
|
use.s_s_assemblage_ptr->s_s[j].comps[i].initial_moles = x[count_unknowns]->moles;
|
|
x[count_unknowns]->ln_moles = log(x[count_unknowns]->moles);
|
|
x[count_unknowns]->s_s = &(use.s_s_assemblage_ptr->s_s[j]);
|
|
x[count_unknowns]->s_s_comp = &(use.s_s_assemblage_ptr->s_s[j].comps[i]);
|
|
x[count_unknowns]->s_s_comp_number = i;
|
|
x[count_unknowns]->phase = use.s_s_assemblage_ptr->s_s[j].comps[i].phase;
|
|
x[count_unknowns]->number = count_unknowns;
|
|
x[count_unknowns]->phase->dn = use.s_s_assemblage_ptr->s_s[j].comps[i].dn;
|
|
x[count_unknowns]->phase->dnb = use.s_s_assemblage_ptr->s_s[j].comps[i].dnb;
|
|
x[count_unknowns]->phase->dnc = use.s_s_assemblage_ptr->s_s[j].comps[i].dnc;
|
|
x[count_unknowns]->phase->log10_fraction_x = use.s_s_assemblage_ptr->s_s[j].comps[i].log10_fraction_x;
|
|
x[count_unknowns]->phase->log10_lambda = use.s_s_assemblage_ptr->s_s[j].comps[i].log10_lambda;
|
|
if (s_s_unknown == NULL) s_s_unknown = x[count_unknowns];
|
|
count_unknowns++;
|
|
}
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int setup_surface (void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Fill in data for surface assemblage in unknown structure
|
|
*/
|
|
int i, j, k;
|
|
struct master *master_ptr;
|
|
struct master **master_ptr_list;
|
|
struct unknown *unknown_ptr;
|
|
char token[MAX_LENGTH];
|
|
char *name1, *name2;
|
|
if (use.surface_ptr == NULL) return(OK);
|
|
|
|
for (i = 0; i < use.surface_ptr->count_comps; i++ ) {
|
|
/*
|
|
* Find master species for each surface, setup unknown structure
|
|
*/
|
|
for (j = 0; use.surface_ptr->comps[i].totals[j].elt != NULL; j++) {
|
|
master_ptr = use.surface_ptr->comps[i].totals[j].elt->master;
|
|
if (master_ptr == NULL) {
|
|
sprintf(error_string, "Master species not in data base for %s, skipping element.", use.surface_ptr->comps[i].totals[j].elt->name);
|
|
warning_msg(error_string);
|
|
continue;
|
|
}
|
|
if (master_ptr->type != SURF) continue;
|
|
/*
|
|
* Check that data not already given
|
|
*/ if (master_ptr->in != FALSE ) {
|
|
sprintf(error_string, "Analytical data entered twice for %s.", master_ptr->s->name);
|
|
error_msg(error_string, CONTINUE);
|
|
input_error++;
|
|
continue;
|
|
}
|
|
/*
|
|
* Set flags
|
|
*/
|
|
use.surface_ptr->comps[i].master = master_ptr;
|
|
master_ptr_list = unknown_alloc_master();
|
|
master_ptr_list[0] = master_ptr;
|
|
master_ptr->in = TRUE;
|
|
/*
|
|
* Setup mass balance unknown
|
|
*/
|
|
x[count_unknowns]->type = SURFACE;
|
|
x[count_unknowns]->description = use.surface_ptr->comps[i].totals[j].elt->name;
|
|
x[count_unknowns]->number = count_unknowns;
|
|
x[count_unknowns]->surface_comp = &(use.surface_ptr->comps[i]);
|
|
x[count_unknowns]->master = master_ptr_list;
|
|
x[count_unknowns]->master[0]->unknown = x[count_unknowns];
|
|
x[count_unknowns]->moles = use.surface_ptr->comps[i].totals[j].coef;
|
|
if (surface_unknown == NULL) surface_unknown = x[count_unknowns];
|
|
x[count_unknowns]->potential_unknown = NULL;
|
|
count_unknowns++;
|
|
if (use.surface_ptr->edl == FALSE) continue;
|
|
/*
|
|
* Setup surface-potential unknown
|
|
*/
|
|
strcpy(token, master_ptr->elt->name);
|
|
unknown_ptr = find_surface_charge_unknown(token);
|
|
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);
|
|
master_ptr_list = unknown_alloc_master();
|
|
master_ptr_list[0] = master_ptr;
|
|
master_ptr->in = TRUE;
|
|
/*
|
|
* Find surface charge structure
|
|
*/
|
|
x[count_unknowns]->type = SURFACE_CB;
|
|
k = use.surface_ptr->comps[i].charge;
|
|
x[count_unknowns]->surface_charge = &use.surface_ptr->charge[k];
|
|
x[count_unknowns]->related_moles = x[count_unknowns]->surface_charge->grams;
|
|
x[count_unknowns]->mass_water = use.surface_ptr->charge[k].mass_water;
|
|
replace("_psi","_CB",token);
|
|
x[count_unknowns]->description = string_hsave(token);
|
|
x[count_unknowns]->master = master_ptr_list;
|
|
use.surface_ptr->charge[k].psi_master = x[count_unknowns]->master[0];
|
|
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++;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* check related phases
|
|
*/
|
|
if (use.surface_ptr->related_phases == TRUE) {
|
|
for (i = 0; i < count_unknowns; i++) {
|
|
if (x[i]->type != SURFACE_CB) continue;
|
|
for (j = 0; j < count_unknowns; j++) {
|
|
if (x[j]->type != SURFACE) continue;
|
|
if (x[j]->potential_unknown != x[i]) continue;
|
|
if (x[j]->surface_comp->phase_name != x[i]->surface_comp->phase_name) {
|
|
if (x[i]->surface_comp->phase_name == NULL) {
|
|
name1 = string_hsave("None");
|
|
} else {
|
|
name1 = x[i]->surface_comp->phase_name;
|
|
}
|
|
if (x[j]->surface_comp->phase_name == NULL) {
|
|
name2 = string_hsave("None");
|
|
} else {
|
|
name2 = x[j]->surface_comp->phase_name;
|
|
}
|
|
input_error++;
|
|
sprintf(error_string,"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",
|
|
x[i]->surface_comp->master->s->name, name1,
|
|
x[j]->surface_comp->master->s->name, name2);
|
|
|
|
error_msg(error_string, CONTINUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* check related kinetics
|
|
*/
|
|
if (use.surface_ptr->related_rate == TRUE) {
|
|
for (i = 0; i < count_unknowns; i++) {
|
|
if (x[i]->type != SURFACE_CB) continue;
|
|
for (j = 0; j < count_unknowns; j++) {
|
|
if (x[j]->type != SURFACE) continue;
|
|
if (x[j]->potential_unknown != x[i]) continue;
|
|
if (x[j]->surface_comp->rate_name != x[i]->surface_comp->rate_name) {
|
|
if (x[i]->surface_comp->rate_name == NULL) {
|
|
name1 = string_hsave("None");
|
|
} else {
|
|
name1 = x[i]->surface_comp->rate_name;
|
|
}
|
|
if (x[j]->surface_comp->rate_name == NULL) {
|
|
name2 = string_hsave("None");
|
|
} else {
|
|
name2 = x[j]->surface_comp->rate_name;
|
|
}
|
|
input_error++;
|
|
sprintf(error_string,"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",
|
|
x[i]->surface_comp->master->s->name, name1,
|
|
x[j]->surface_comp->master->s->name, name2);
|
|
|
|
error_msg(error_string, CONTINUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
struct unknown *find_surface_charge_unknown(char *str_ptr)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* 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
|
|
*/
|
|
int i;
|
|
char *ptr;
|
|
char token[MAX_LENGTH];
|
|
|
|
replace ("_", " ", str_ptr);
|
|
ptr = str_ptr;
|
|
copy_token(token, &ptr, &i);
|
|
strcat(token, "_CB");
|
|
strcpy(str_ptr, token);
|
|
for (i = 0; i < count_unknowns; i++) {
|
|
if ( strcmp(token, x[i]->description) == 0 ) {
|
|
return(x[i]);
|
|
}
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
int setup_master_rxn(struct master **master_ptr_list, struct reaction **pe_rxn)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Rewrites rxn_secondary for all redox states in list
|
|
* First, in = TRUE; others, in = REWRITE
|
|
*/
|
|
int j;
|
|
struct master *master_ptr, *master_ptr0;
|
|
/*
|
|
* Set master_ptr->in, master_ptr->rxn
|
|
*/
|
|
master_ptr0 = master_ptr_list[0];
|
|
for (j = 0; (master_ptr = master_ptr_list[j]) != NULL; j++) {
|
|
/*
|
|
* Check that data not already given
|
|
*/
|
|
if (master_ptr->s == s_h2o) {
|
|
sprintf(error_string, "Can not 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) {
|
|
sprintf(error_string, "Analytical data entered twice for %s.", master_ptr->s->name);
|
|
error_msg(error_string, CONTINUE);
|
|
input_error++;
|
|
continue;
|
|
}
|
|
}
|
|
/*
|
|
* Set flags
|
|
*/
|
|
if (j == 0) {
|
|
master_ptr->in = TRUE;
|
|
if (master_ptr->s->primary == NULL) {
|
|
rxn_free (master_ptr->rxn_secondary);
|
|
master_ptr->rxn_secondary = rxn_dup (master_ptr->s->rxn_s);
|
|
/* debug
|
|
trxn_print ();
|
|
*/
|
|
}
|
|
} else {
|
|
master_ptr->in = REWRITE;
|
|
if (master_ptr0->s->primary == NULL) {
|
|
rewrite_master_to_secondary(master_ptr, master_ptr0);
|
|
rxn_free (master_ptr->rxn_secondary);
|
|
master_ptr->rxn_secondary = rxn_alloc(count_trxn+1);
|
|
trxn_copy (master_ptr->rxn_secondary);
|
|
/* debug
|
|
trxn_print ();
|
|
*/
|
|
}
|
|
}
|
|
master_ptr->pe_rxn = pe_rxn;
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int setup_pure_phases(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
int i;
|
|
/*
|
|
* Fills in data for pure_phase assemglage in unknown structure
|
|
*/
|
|
|
|
if (use.pp_assemblage_ptr == NULL) return(OK);
|
|
/*
|
|
* Setup unknowns
|
|
*/
|
|
for (i=0; i < use.pp_assemblage_ptr->count_comps; i++) {
|
|
x[count_unknowns]->type = PP;
|
|
x[count_unknowns]->description = use.pp_assemblage_ptr->pure_phases[i].name;
|
|
x[count_unknowns]->moles = use.pp_assemblage_ptr->pure_phases[i].moles;
|
|
x[count_unknowns]->phase = use.pp_assemblage_ptr->pure_phases[i].phase;
|
|
x[count_unknowns]->si = use.pp_assemblage_ptr->pure_phases[i].si;
|
|
x[count_unknowns]->delta = use.pp_assemblage_ptr->pure_phases[i].delta;
|
|
x[count_unknowns]->pure_phase = &(use.pp_assemblage_ptr->pure_phases[i]);
|
|
x[count_unknowns]->dissolve_only = use.pp_assemblage_ptr->pure_phases[i].dissolve_only;
|
|
if (pure_phase_unknown == NULL) pure_phase_unknown = x[count_unknowns];
|
|
count_unknowns++;
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int setup_solution (void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Fills in data in unknown structure for the solution
|
|
*/
|
|
int l, i, j;
|
|
struct master *master_ptr;
|
|
struct solution *solution_ptr;
|
|
char *ptr;
|
|
char token[MAX_LENGTH];
|
|
struct master_isotope *master_isotope_ptr;
|
|
|
|
solution_ptr = use.solution_ptr;
|
|
count_unknowns = 0;
|
|
for (i = 0; solution_ptr->totals[i].description != NULL; i++ ) {
|
|
/*solution_ptr->totals[i].skip = FALSE;*/
|
|
ptr=solution_ptr->totals[i].description;
|
|
copy_token(token, &ptr, &l);
|
|
master_ptr = master_bsearch(token);
|
|
/*
|
|
* Treat minor isotopes as special in initial solution calculation
|
|
*/
|
|
if ((state == INITIAL_SOLUTION) && (master_ptr != NULL) && (master_ptr->minor_isotope == TRUE) && (initial_solution_isotopes == FALSE)) {
|
|
master_isotope_ptr = master_isotope_search(token);
|
|
if (master_isotope_ptr != NULL) {
|
|
master_isotope_ptr->ratio = solution_ptr->totals[i].input_conc;
|
|
}
|
|
/*solution_ptr->totals[i].skip = TRUE;*/
|
|
continue;
|
|
}
|
|
/*
|
|
* Check that total not <= zero
|
|
*/
|
|
if (solution_ptr->totals[i].input_conc <= 0.0) {
|
|
if (strcmp(token,"H(1)") != 0 &&
|
|
strcmp(token,"E") != 0 ) {
|
|
/*solution_ptr->totals[i].skip = TRUE;*/
|
|
continue;
|
|
}
|
|
}
|
|
/*
|
|
* Find master species
|
|
*/
|
|
master_ptr = master_bsearch(token);
|
|
if (master_ptr == NULL) {
|
|
/*solution_ptr->totals[i].skip = TRUE;*/
|
|
sprintf(error_string,"Master species not in data base for %s, skipping element.", solution_ptr->totals[i].description);
|
|
warning_msg(error_string);
|
|
continue;
|
|
}
|
|
if (master_ptr->type != AQ) {
|
|
/*solution_ptr->totals[i].skip = TRUE;*/
|
|
sprintf(error_string, "Only aqueous concentrations are allowed in solution data, ignoring %s.", solution_ptr->totals[i].description);
|
|
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(ptr, master_ptr);
|
|
setup_master_rxn(x[count_unknowns]->master, &(pe_x[solution_ptr->totals[i].n_pe].rxn));
|
|
/*
|
|
* Set default unknown data
|
|
*/
|
|
x[count_unknowns]->type = MB;
|
|
x[count_unknowns]->description = solution_ptr->totals[i].description;
|
|
x[count_unknowns]->total = &(solution_ptr->totals[i]);
|
|
for (j=0; x[count_unknowns]->master[j] != NULL; j++) {
|
|
x[count_unknowns]->master[j]->unknown = x[count_unknowns];
|
|
}
|
|
x[count_unknowns]->moles = solution_ptr->totals[i].moles;
|
|
/*
|
|
* Set pointers
|
|
*/
|
|
ptr=solution_ptr->totals[i].description;
|
|
copy_token(token, &ptr, &l);
|
|
str_tolower(token);
|
|
if (strstr( token,"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") == 0 || strcmp(token,"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,"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,"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 (solution_ptr->totals[i].equation_name != NULL ) {
|
|
ptr = solution_ptr->totals[i].equation_name;
|
|
copy_token(token, &ptr, &l);
|
|
str_tolower(token);
|
|
if (strstr(token,"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->cb;
|
|
}
|
|
} else {
|
|
error_msg("Charge balance specified for more"
|
|
" than one species.", CONTINUE);
|
|
input_error++;
|
|
}
|
|
} else {
|
|
/*
|
|
* Solution phase boundaries
|
|
*/
|
|
solution_ptr->totals[i].phase=
|
|
phase_bsearch(solution_ptr->totals[i].equation_name, &l, FALSE);
|
|
if (solution_ptr->totals[i].phase == NULL) {
|
|
sprintf(error_string, "Expected a mineral name, %s.", solution_ptr->totals[i].equation_name);
|
|
error_msg(error_string, CONTINUE);
|
|
input_error++;
|
|
}
|
|
x[count_unknowns]->type = SOLUTION_PHASE_BOUNDARY;
|
|
x[count_unknowns]->phase=solution_ptr->totals[i].phase;
|
|
x[count_unknowns]->si=solution_ptr->totals[i].phase_si;
|
|
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(OUTPUT_MESSAGE,"\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) {
|
|
/*
|
|
* 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;
|
|
mu_unknown = x[count_unknowns];
|
|
count_unknowns++;
|
|
}
|
|
/*
|
|
* Activity of water
|
|
*/
|
|
ah2o_unknown=x[count_unknowns];
|
|
ah2o_unknown->description= string_hsave( "A(H2O)" );
|
|
ah2o_unknown->type=AH2O;
|
|
ah2o_unknown->number=count_unknowns;
|
|
ah2o_unknown->master = unknown_alloc_master();
|
|
ah2o_unknown->master[0] = master_bsearch ("O");
|
|
ah2o_unknown->master[0]->unknown = ah2o_unknown;
|
|
ah2o_unknown->moles = 0.0;
|
|
count_unknowns++;
|
|
|
|
if (state >= REACTION) {
|
|
/*
|
|
|
|
* Reaction: pH for charge balance
|
|
*/
|
|
ph_unknown = x[count_unknowns];
|
|
ph_unknown->description = string_hsave( "pH" );
|
|
ph_unknown->type = CB;
|
|
ph_unknown->moles = solution_ptr->cb;
|
|
ph_unknown->number = count_unknowns;
|
|
ph_unknown->master = unknown_alloc_master();
|
|
ph_unknown->master[0] = s_hplus->primary;
|
|
ph_unknown->master[0]->unknown = ph_unknown;
|
|
charge_balance_unknown = ph_unknown;
|
|
count_unknowns++;
|
|
/*
|
|
* Reaction: pe for total hydrogen
|
|
*/
|
|
pe_unknown = x[count_unknowns];
|
|
mass_hydrogen_unknown = x[count_unknowns];
|
|
mass_hydrogen_unknown->description = string_hsave( "Hydrogen" );
|
|
mass_hydrogen_unknown->type = MH;
|
|
#ifdef COMBINE
|
|
#ifndef COMBINE_CHARGE
|
|
mass_hydrogen_unknown->moles = solution_ptr->total_h - 2*solution_ptr->total_o ;
|
|
#else
|
|
mass_hydrogen_unknown->moles = solution_ptr->total_h - 2*solution_ptr->total_o - solution_ptr->cb ;
|
|
#endif
|
|
#else
|
|
mass_hydrogen_unknown->moles = solution_ptr->total_h;
|
|
#endif
|
|
mass_hydrogen_unknown->number = count_unknowns;
|
|
mass_hydrogen_unknown->master = unknown_alloc_master();
|
|
mass_hydrogen_unknown->master[0] = s_eminus->primary;
|
|
mass_hydrogen_unknown->master[0]->unknown = mass_hydrogen_unknown;
|
|
count_unknowns++;
|
|
/*
|
|
* Reaction H2O for total oxygen
|
|
*/
|
|
mass_oxygen_unknown = x[count_unknowns];
|
|
mass_oxygen_unknown->description = string_hsave( "Oxygen" );
|
|
mass_oxygen_unknown->type = MH2O;
|
|
mass_oxygen_unknown->moles = solution_ptr->total_o;
|
|
mass_oxygen_unknown->number = count_unknowns;
|
|
mass_oxygen_unknown->master = unknown_alloc_master();
|
|
mass_oxygen_unknown->master[0] = s_h2o->primary;
|
|
count_unknowns++;
|
|
}
|
|
/*
|
|
* Validity tests
|
|
*/
|
|
if ( (ph_unknown != NULL) &&
|
|
(ph_unknown == charge_balance_unknown) &&
|
|
(alkalinity_unknown != NULL) ) {
|
|
error_msg("pH adustment can not 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 can not be used with charge balance"
|
|
" or solution phase boundary constraints.", CONTINUE);
|
|
input_error++;
|
|
}
|
|
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
struct master **unknown_alloc_master(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Allocates space for a list of 2 master pointers
|
|
*/
|
|
struct master **master_ptr;
|
|
|
|
master_ptr = (struct master **) PHRQ_malloc( 2 * sizeof(struct master *) );
|
|
if (master_ptr == NULL) malloc_error();
|
|
master_ptr[0] = NULL;
|
|
master_ptr[1] = NULL;
|
|
return(master_ptr);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int setup_unknowns (void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Counts unknowns and allocates space for unknown structures
|
|
*/
|
|
int i, j;
|
|
struct solution *solution_ptr;
|
|
|
|
solution_ptr = use.solution_ptr;
|
|
/*
|
|
* Calculate maximum number of unknowns
|
|
*/
|
|
max_unknowns = 0;
|
|
/*
|
|
* Count mass balance in solution
|
|
*/
|
|
for (i = 0; solution_ptr->totals[i].description != NULL; i++ ) max_unknowns++;
|
|
/*
|
|
* Add 5 for ionic strength, activity of water, charge balance, total H, total O
|
|
*/
|
|
max_unknowns += 5;
|
|
/*
|
|
* Count pure phases
|
|
*/
|
|
if (use.pp_assemblage_ptr != NULL) {
|
|
max_unknowns += use.pp_assemblage_ptr->count_comps;
|
|
/*
|
|
for (i = 0; use.pp_assemblage_ptr->pure_phases[i].name != NULL; i++ ) {
|
|
max_unknowns++;
|
|
}
|
|
*/
|
|
}
|
|
/*
|
|
* Count exchange
|
|
*/
|
|
if (use.exchange_ptr != NULL) {
|
|
for (j = 0; j < use.exchange_ptr->count_comps; j++) {
|
|
for (i = 0; use.exchange_ptr->comps[j].totals[i].elt != NULL; i++) {
|
|
if (use.exchange_ptr->comps[j].totals[i].elt->master == NULL) {
|
|
sprintf(error_string, "Master species missing for element %s", use.exchange_ptr->comps[j].totals[i].elt->name);
|
|
error_msg(error_string, STOP);
|
|
}
|
|
if (use.exchange_ptr->comps[j].totals[i].elt->master->type == EX) {
|
|
max_unknowns++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Count surfaces
|
|
*/
|
|
if (use.surface_ptr != NULL) {
|
|
max_unknowns += use.surface_ptr->count_comps + use.surface_ptr->count_charge;
|
|
}
|
|
/*
|
|
* Count gas components
|
|
*/
|
|
if (use.gas_phase_ptr != NULL) {
|
|
max_unknowns++;
|
|
}
|
|
/*
|
|
* Count solid solutions
|
|
*/
|
|
if (use.s_s_assemblage_ptr != NULL) {
|
|
/* max_unknowns += 2 * use.s_s_assemblage_ptr->count_s_s;*/
|
|
for (i = 0; i < use.s_s_assemblage_ptr->count_s_s; i++) {
|
|
max_unknowns += use.s_s_assemblage_ptr->s_s[i].count_comps;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* One for luck
|
|
*/
|
|
max_unknowns++;
|
|
if (pitzer_model == TRUE) {
|
|
max_unknowns += count_s;
|
|
}
|
|
/*
|
|
* Allocate space for pointer array and structures
|
|
*/
|
|
|
|
space ((void **) ((void *) &x), INIT, &max_unknowns, sizeof(struct unknown *));
|
|
for (i = 0; i < max_unknowns; i++) {
|
|
x[i] = (struct unknown *) unknown_alloc ();
|
|
x[i]->number = i;
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int store_dn (int k, LDBLE *source, int row, LDBLE coef_in, LDBLE *gamma_source)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Stores the terms for d moles of species k in solution into row, multiplied
|
|
* by coef_in
|
|
*/
|
|
int col;
|
|
LDBLE coef;
|
|
struct rxn_token *rxn_ptr;
|
|
struct master *master_ptr;
|
|
|
|
if (equal(coef_in, 0.0, TOL) == TRUE) {
|
|
return(OK);
|
|
}
|
|
/* Gamma term for d molality of species */
|
|
/* Note dg includes molality as a factor */
|
|
|
|
row = row * (count_unknowns + 1);
|
|
if (s[k]->type != SURF && s[k] != s_h2o ) {
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\t\t%-24s%10.3f\t%d\t%d\n","Activity coefficient", (double) (-1.0 * coef_in), row/(count_unknowns + 1), mu_unknown->number);
|
|
}
|
|
/* mu term */
|
|
if (gamma_source != NULL) {
|
|
store_jacob(gamma_source, &array[row + mu_unknown->number], -1.0 * coef_in);
|
|
#ifdef SKIP
|
|
if (use_tot_g == 0) {
|
|
store_jacob(&(s[k]->dg_total_g), &array[row + mu_unknown->number], -1.0 * coef_in);
|
|
} else if (use_tot_g == 1) {
|
|
store_jacob(&(s[k]->dg), &array[row + mu_unknown->number], -1.0 * coef_in);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
/*
|
|
* Mass of water factor
|
|
*/
|
|
if (mass_oxygen_unknown != NULL && s[k]->type != EX && s[k]->type != SURF) {
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\t\t%-24s%10.3f\t%d\t%d\n",
|
|
mass_oxygen_unknown->master[0]->s->name, (double) coef_in,
|
|
row/(count_unknowns+1),
|
|
mass_oxygen_unknown->number);
|
|
}
|
|
store_jacob(source, &(array[row + mass_oxygen_unknown->number]), coef_in);
|
|
}
|
|
if ( s[k] == s_h2o ) return(OK);
|
|
for ( rxn_ptr = s[k]->rxn_x->token + 1; rxn_ptr->s != NULL; rxn_ptr++) {
|
|
if (rxn_ptr->s->secondary != NULL && rxn_ptr->s->secondary->in == TRUE ) {
|
|
master_ptr=rxn_ptr->s->secondary;
|
|
} else {
|
|
master_ptr=rxn_ptr->s->primary;
|
|
}
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\t\t%s\n",master_ptr->s->name);
|
|
}
|
|
if (master_ptr->unknown == NULL) continue;
|
|
col = master_ptr->unknown->number;
|
|
coef = coef_in * rxn_ptr->coef;
|
|
store_jacob (source, &(array[row + col]), coef);
|
|
if ( debug_prep == TRUE ) {
|
|
output_msg(OUTPUT_MESSAGE,"\t\t%-24s%10.3f\t%d\t%d\n",master_ptr->s->name, (double) coef,
|
|
row/(count_unknowns+1), col);
|
|
}
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int store_jacob(LDBLE *source, LDBLE *target, LDBLE coef)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Adds a new item to either sum_jacob1 or sum_jacob2
|
|
* If coef is 1.0, adds to sum_jacob1, which does not require a multiply
|
|
* Otherwise, adds to sum_jacob2, which allows multiply by coef
|
|
*/
|
|
if (equal(coef, 1.0, TOL) == TRUE) {
|
|
if (debug_prep == TRUE) {
|
|
output_msg(OUTPUT_MESSAGE,"\t\tjacob1 %d\n", count_sum_jacob1);
|
|
}
|
|
sum_jacob1[count_sum_jacob1].source = source;
|
|
sum_jacob1[count_sum_jacob1++].target = target;
|
|
/* Check space */
|
|
if (count_sum_jacob1 >= max_sum_jacob1) {
|
|
space ((void **) ((void *) &sum_jacob1), count_sum_jacob1,
|
|
&max_sum_jacob1,
|
|
sizeof(struct list1));
|
|
}
|
|
} else {
|
|
if (debug_prep == TRUE) {
|
|
output_msg(OUTPUT_MESSAGE,"\t\tjacob2 %d\n", count_sum_jacob2);
|
|
}
|
|
sum_jacob2[count_sum_jacob2].source = source;
|
|
sum_jacob2[count_sum_jacob2].target = target;
|
|
sum_jacob2[count_sum_jacob2++].coef=coef;
|
|
/* Check space */
|
|
if (count_sum_jacob2 >= max_sum_jacob2) {
|
|
space ((void **) ((void *) &sum_jacob2), count_sum_jacob2, &max_sum_jacob2,
|
|
sizeof(struct list2));
|
|
}
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int store_jacob0(int row, int column, LDBLE coef)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Stores in list a constant coef which will be added into jacobian array
|
|
*/
|
|
sum_jacob0[count_sum_jacob0].target = &(array[row*(count_unknowns+1) + column]);
|
|
sum_jacob0[count_sum_jacob0++].coef=coef;
|
|
/* Check space */
|
|
if (count_sum_jacob0 >= max_sum_jacob0) {
|
|
space ((void **) ((void *) &sum_jacob0), count_sum_jacob0,
|
|
&max_sum_jacob0,
|
|
sizeof(struct list0));
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int store_mb(LDBLE *source, LDBLE *target, LDBLE coef)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Adds item to list sum_mb1 or sum_mb2
|
|
* If coef is 1.0, adds to sum_mb1, which does not require a multiply
|
|
* else, adds to sum_mb2, which will multiply by coef
|
|
*/
|
|
if (equal(coef, 1.0, TOL) == TRUE ) {
|
|
sum_mb1[count_sum_mb1].source = source;
|
|
sum_mb1[count_sum_mb1++].target = target;
|
|
if (count_sum_mb1 >= max_sum_mb1) {
|
|
space ((void **) ((void *) &sum_mb1), count_sum_mb1 + count_trxn + 4,
|
|
&max_sum_mb1, sizeof(struct list1));
|
|
}
|
|
} else {
|
|
sum_mb2[count_sum_mb2].source = source;
|
|
sum_mb2[count_sum_mb2].coef = coef;
|
|
sum_mb2[count_sum_mb2++].target = target;
|
|
if (count_sum_mb2 >= max_sum_mb2) {
|
|
space ((void **) ((void *) &sum_mb2), count_sum_mb2,
|
|
&max_sum_mb2, sizeof(struct list2));
|
|
}
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int store_sum_deltas(LDBLE *source, LDBLE *target, LDBLE coef)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* List sum_delta is summed to determine the change in the mass of
|
|
* each element due to mass transfers of minerals, changes show up
|
|
* in x[i]->delta. These may be multiplied by a factor under some
|
|
* situations where the entire calculated step is not taken
|
|
*/
|
|
sum_delta[count_sum_delta].source = source;
|
|
sum_delta[count_sum_delta].target = target;
|
|
sum_delta[count_sum_delta++].coef=coef;
|
|
/* Check space */
|
|
if (count_sum_delta >= max_sum_delta) {
|
|
space ((void **) ((void *) &sum_delta), count_sum_delta, &max_sum_delta,
|
|
sizeof(struct list2));
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int switch_bases(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Check if activity of first master species is predominant among activities of
|
|
* secondary master species included in mass balance.
|
|
*/
|
|
int i, j;
|
|
int first;
|
|
int return_value;
|
|
LDBLE la, la1;
|
|
struct master *master_ptr;
|
|
|
|
return_value = FALSE;
|
|
for (i=0; i < count_unknowns; i++) {
|
|
if (x[i]->type != MB) continue;
|
|
first = 0;
|
|
la = x[i]->master[0]->s->la;
|
|
for (j=1; x[i]->master[j] != NULL; j++) {
|
|
la1 = x[i]->master[j]->s->lm + x[i]->master[j]->s->lg;
|
|
if (first == 0 && la1 > la + 10.) {
|
|
la = la1;
|
|
first = j;
|
|
} else if (first != 0 && la1 > la ) {
|
|
la = la1;
|
|
first = j;
|
|
}
|
|
}
|
|
if (first != 0) {
|
|
master_ptr = x[i]->master[0];
|
|
x[i]->master[0] = x[i]->master[first];
|
|
x[i]->master[0]->in = TRUE;
|
|
x[i]->master[first] = master_ptr;
|
|
x[i]->master[first]->in = REWRITE;
|
|
output_msg(OUTPUT_LOG,"Switching bases to %s.\tIteration %d\n",
|
|
x[i]->master[0]->s->name, iterations);
|
|
return_value = TRUE;
|
|
}
|
|
}
|
|
return(return_value);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int tidy_redox (void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Write pe redox reactions (rxn in struct pe_data) in terms of master species
|
|
* defined in analytical data
|
|
*
|
|
*/
|
|
int i, j, l;
|
|
char *ptr;
|
|
char token[MAX_LENGTH], tok1[MAX_LENGTH], tok2[MAX_LENGTH];
|
|
struct pe_data *pe_data_ptr;
|
|
struct master *master_ptr1, *master_ptr2;
|
|
/*
|
|
* Keep valences of oxygen and hydrogen in model, if not already in
|
|
*/
|
|
for (i=0; i < count_master; i++) {
|
|
if (master[i]->primary == TRUE &&
|
|
(master[i]->s == s_hplus ||
|
|
master[i]->s == s_h2o ) ) {
|
|
j = i+1;
|
|
while (j < count_master && master[j]->elt->primary == master[i]) {
|
|
if (master[j]->in == FALSE &&
|
|
master[j]->s != master[i]->s) {
|
|
master[j]->in = REWRITE;
|
|
master[j]->pe_rxn = master[i]->pe_rxn;
|
|
}
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Writes equations for e- for each redox couple used in solution n
|
|
*/
|
|
for (pe_data_ptr = pe_x; pe_data_ptr->name != NULL; pe_data_ptr++) {
|
|
if (strcmp_nocase_arg1(pe_data_ptr->name,"pe") == 0 ) {
|
|
rxn_free(pe_data_ptr->rxn);
|
|
pe_data_ptr->rxn = rxn_dup(s_eminus->rxn);
|
|
} else {
|
|
strcpy(token, pe_data_ptr->name);
|
|
replace ("/"," ",token);
|
|
ptr=token;
|
|
/*
|
|
* Get redox states and elements from redox couple
|
|
*/
|
|
copy_token (tok1, &ptr, &l);
|
|
copy_token (tok2, &ptr, &l);
|
|
/*
|
|
* Find master species
|
|
*/
|
|
master_ptr1 = master_bsearch(tok1);
|
|
master_ptr2 = master_bsearch(tok2);
|
|
if (master_ptr1 != NULL && master_ptr2 != NULL ) {
|
|
rewrite_master_to_secondary (master_ptr1, master_ptr2);
|
|
/*
|
|
* Rewrite equation to e-
|
|
*/
|
|
trxn_swap ("e-");
|
|
} else {
|
|
sprintf(error_string, "Can not find master species for redox couple, %s.", pe_data_ptr->name);
|
|
error_msg(error_string, STOP);
|
|
}
|
|
if (inout() == FALSE) {
|
|
sprintf(error_string, "Analytical data missing for redox couple, %s\n\t Using pe instead.", pe_data_ptr->name );
|
|
warning_msg(error_string);
|
|
rxn_free(pe_data_ptr->rxn);
|
|
pe_data_ptr->rxn = rxn_dup(s_eminus->rxn);
|
|
pe_data_ptr->name = pe_x[0].name;
|
|
} else {
|
|
rxn_free(pe_data_ptr->rxn);
|
|
pe_data_ptr->rxn = rxn_alloc(count_trxn+1);
|
|
trxn_copy (pe_data_ptr->rxn);
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* Rewrite equations to master species that are "in" the model
|
|
*/
|
|
for (pe_data_ptr = pe_x; pe_data_ptr->name != NULL; pe_data_ptr++) {
|
|
count_trxn=0;
|
|
trxn_add(pe_data_ptr->rxn, 1.0, FALSE);
|
|
if (write_mass_action_eqn_x(CONTINUE) == FALSE) {
|
|
sprintf(error_string, "Could not rewrite redox "
|
|
"couple equation for %s\n\t Possibly missing data for one "
|
|
"of the redox states.", pe_data_ptr->name);
|
|
warning_msg(error_string);
|
|
sprintf(error_string, "Using pe instead of %s.", pe_data_ptr->name);
|
|
warning_msg(error_string);
|
|
rxn_free(pe_data_ptr->rxn);
|
|
pe_data_ptr->rxn = rxn_dup (pe_x[0].rxn);
|
|
pe_data_ptr->name = pe_x[0].name;
|
|
} else {
|
|
rxn_free(pe_data_ptr->rxn);
|
|
pe_data_ptr->rxn = rxn_alloc(count_trxn+1);
|
|
trxn_copy (pe_data_ptr->rxn);
|
|
}
|
|
}
|
|
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int write_mb_eqn_x (void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
int count, repeat;
|
|
int i, count_rxn_orig;
|
|
int j, k;
|
|
char *ptr;
|
|
struct master *master_ptr;
|
|
/*
|
|
* Rewrite any secondary master species flagged REWRITE
|
|
* Don't add in any pe reactions
|
|
*/
|
|
count = 0;
|
|
repeat = TRUE;
|
|
while (repeat == TRUE) {
|
|
count++;
|
|
if (count > MAX_ADD_EQUATIONS) {
|
|
sprintf(error_string, "Could not reduce equation "
|
|
"to primary and secondary species that are "
|
|
"in the model, %s.", trxn.token[0].s->name);
|
|
error_msg(error_string, CONTINUE);
|
|
return(ERROR);
|
|
}
|
|
repeat = FALSE;
|
|
count_rxn_orig=count_trxn;
|
|
for (i=1; i < count_rxn_orig; i++) {
|
|
if (trxn.token[i].s->secondary == NULL) continue;
|
|
if (trxn.token[i].s->secondary->in == REWRITE ) {
|
|
repeat=TRUE;
|
|
trxn_add(trxn.token[i].s->secondary->rxn_secondary,
|
|
trxn.token[i].coef, FALSE);
|
|
}
|
|
}
|
|
trxn_combine();
|
|
}
|
|
/*
|
|
*
|
|
*/
|
|
count_elts = 0;
|
|
paren_count = 0;
|
|
for (i = 1; i < count_trxn; i++) {
|
|
j = count_elts;
|
|
ptr = trxn.token[i].s->name;
|
|
get_elts_in_species(&ptr, trxn.token[i].coef);
|
|
for (k = j; k < count_elts; k++) {
|
|
if (trxn.token[i].s->secondary != NULL) {
|
|
master_ptr = trxn.token[i].s->secondary->elt->primary;
|
|
} else {
|
|
master_ptr = trxn.token[i].s->primary;
|
|
}
|
|
if (elt_list[k].elt == master_ptr->elt) {
|
|
elt_list[k].coef = 0.0;
|
|
break;
|
|
}
|
|
}
|
|
if (trxn.token[i].s->secondary == NULL) {
|
|
ptr = trxn.token[i].s->primary->elt->name;
|
|
get_secondary_in_species(&ptr, trxn.token[i].coef);
|
|
} else {
|
|
ptr = trxn.token[i].s->secondary->elt->name;
|
|
get_secondary_in_species(&ptr, trxn.token[i].coef);
|
|
}
|
|
}
|
|
if (count_elts > 0 ) {
|
|
qsort (elt_list, (size_t) count_elts,
|
|
(size_t) sizeof(struct elt_list), elt_list_compare);
|
|
elt_list_combine ();
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int write_mb_for_species_list (int n)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Sets up data to add to species_list
|
|
* Original secondary redox states are retained
|
|
*/
|
|
int i;
|
|
char *ptr;
|
|
/*
|
|
* 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) {
|
|
ptr = trxn.token[i].s->primary->elt->name;
|
|
get_secondary_in_species(&ptr, trxn.token[i].coef);
|
|
} else {
|
|
ptr = trxn.token[i].s->secondary->elt->name;
|
|
get_secondary_in_species(&ptr, trxn.token[i].coef);
|
|
}
|
|
}
|
|
for (i = 0; i < count_elts; i++) {
|
|
if (strcmp(elt_list[i].elt->name, "O(-2)") == 0) {
|
|
if (count_elts >= max_elts) {
|
|
space ((void **) ((void *) &elt_list), count_elts, &max_elts,
|
|
sizeof(struct elt_list));
|
|
}
|
|
elt_list[count_elts].elt = element_h_one;
|
|
elt_list[count_elts].coef = elt_list[i].coef*2;
|
|
count_elts++;
|
|
}
|
|
}
|
|
if (count_elts > 0 ) {
|
|
qsort (elt_list, (size_t) count_elts,
|
|
(size_t) sizeof(struct elt_list), elt_list_compare);
|
|
elt_list_combine ();
|
|
}
|
|
s[n]->next_sys_total = (struct elt_list *) free_check_null(s[n]->next_sys_total);
|
|
s[n]->next_sys_total = elt_list_save();
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int write_phase_sys_total (int n)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Sets up data to add to species_list
|
|
* Original secondary redox states are retained
|
|
*/
|
|
int i;
|
|
char *ptr;
|
|
/*
|
|
* Start with secondary reaction
|
|
*/
|
|
count_trxn=0;
|
|
trxn_add (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) {
|
|
ptr = trxn.token[i].s->primary->elt->name;
|
|
get_secondary_in_species(&ptr, trxn.token[i].coef);
|
|
} else {
|
|
ptr = trxn.token[i].s->secondary->elt->name;
|
|
get_secondary_in_species(&ptr, trxn.token[i].coef);
|
|
}
|
|
}
|
|
for (i = 0; i < count_elts; i++) {
|
|
if (strcmp(elt_list[i].elt->name, "O(-2)") == 0) {
|
|
if (count_elts >= max_elts) {
|
|
space ((void **) ((void *) &elt_list), count_elts, &max_elts,
|
|
sizeof(struct elt_list));
|
|
}
|
|
elt_list[count_elts].elt = element_h_one;
|
|
elt_list[count_elts].coef = elt_list[i].coef*2;
|
|
count_elts++;
|
|
}
|
|
}
|
|
if (count_elts > 0 ) {
|
|
qsort (elt_list, (size_t) count_elts,
|
|
(size_t) sizeof(struct elt_list), elt_list_compare);
|
|
elt_list_combine ();
|
|
}
|
|
phases[n]->next_sys_total = (struct elt_list *) free_check_null(phases[n]->next_sys_total);
|
|
phases[n]->next_sys_total = elt_list_save();
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int k_temp(LDBLE tempc)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Calculates log k's for all species and pure_phases
|
|
*/
|
|
int i;
|
|
LDBLE tempk;
|
|
|
|
if (same_model == TRUE && same_temperature == TRUE) return(OK);
|
|
|
|
tempk=tempc+273.15;
|
|
/*
|
|
* Calculate log k for all aqueous species
|
|
*/
|
|
for (i=0; i < count_s_x; i++) {
|
|
s_x[i]->lk = k_calc(s_x[i]->rxn_x->logk, tempk);
|
|
}
|
|
/*
|
|
* Calculate log k for all pure phases
|
|
*/
|
|
for (i=0; i < count_phases; i++) {
|
|
if (phases[i]->in == TRUE) {
|
|
phases[i]->lk = k_calc(phases[i]->rxn_x->logk, tempk);
|
|
}
|
|
}
|
|
/*
|
|
* Calculate miscibility gaps for solid solutions
|
|
*/
|
|
if (use.s_s_assemblage_ptr != NULL ) {
|
|
for (i = 0; i < use.s_s_assemblage_ptr->count_s_s; i++) {
|
|
if (fabs(tempk - use.s_s_assemblage_ptr->s_s[i].tk) > 0.01) {
|
|
s_s_prep(tempk, &(use.s_s_assemblage_ptr->s_s[i]), FALSE);
|
|
}
|
|
}
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
LDBLE k_calc(LDBLE *logk, LDBLE tempk)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Calculates log k at specified temperature
|
|
*
|
|
* Input:
|
|
* *logk is pointer to logkt0, deltah, and analytical expression data
|
|
* tempk is temperature in degrees K.
|
|
*
|
|
* Returns calculated log k.
|
|
*/
|
|
return(logk[0]
|
|
- logk[1]*(298.15-tempk)/(298.15*tempk*LOG_10*R_KJ_DEG_MOL)
|
|
+ logk[2]
|
|
+ logk[3]*tempk
|
|
+ logk[4]/tempk
|
|
+ logk[5]*log10(tempk)
|
|
+ logk[6]/(tempk*tempk) );
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
static int save_model(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
int i;
|
|
/*
|
|
* save temperature
|
|
*/
|
|
last_model.temperature = tc_x;
|
|
/*
|
|
* mark master species
|
|
*/
|
|
for (i = 0; i < count_master; i++) {
|
|
master[i]->last_model = FALSE;
|
|
if (master[i]->total > 0) {
|
|
if (master[i]->primary == TRUE) {
|
|
master[i]->last_model = TRUE;
|
|
} else {
|
|
/* mark primary master */
|
|
master[i]->s->secondary->elt->primary->last_model = TRUE;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* save list of phase pointers for gas phase
|
|
*/
|
|
last_model.gas_phase = (struct phase **) free_check_null(last_model.gas_phase);
|
|
if (use.gas_phase_ptr != NULL) {
|
|
last_model.count_gas_phase = use.gas_phase_ptr->count_comps;
|
|
last_model.gas_phase = (struct phase **) PHRQ_malloc((size_t) use.gas_phase_ptr->count_comps * sizeof(struct phase *));
|
|
if (last_model.gas_phase == NULL) malloc_error();
|
|
for (i = 0; i < use.gas_phase_ptr->count_comps; i++) {
|
|
last_model.gas_phase[i] = use.gas_phase_ptr->comps[i].phase;
|
|
}
|
|
} else {
|
|
last_model.count_gas_phase = 0;
|
|
last_model.gas_phase = NULL;
|
|
}
|
|
/*
|
|
* save list of names of solid solutions
|
|
*/
|
|
last_model.s_s_assemblage = (char **) free_check_null(last_model.s_s_assemblage);
|
|
if (use.s_s_assemblage_ptr != NULL) {
|
|
last_model.count_s_s_assemblage = use.s_s_assemblage_ptr->count_s_s;
|
|
last_model.s_s_assemblage = (char **) PHRQ_malloc((size_t) use.s_s_assemblage_ptr->count_s_s * sizeof(char *));
|
|
if (last_model.s_s_assemblage == NULL) malloc_error();
|
|
for (i = 0; i < use.s_s_assemblage_ptr->count_s_s; i++) {
|
|
last_model.s_s_assemblage[i] = use.s_s_assemblage_ptr->s_s[i].name;
|
|
}
|
|
} else {
|
|
last_model.count_s_s_assemblage = 0;
|
|
last_model.s_s_assemblage = NULL;
|
|
}
|
|
/*
|
|
* save list of phase pointers for pp_assemblage
|
|
*/
|
|
last_model.pp_assemblage = (struct phase **) free_check_null(last_model.pp_assemblage);
|
|
last_model.add_formula = (char **) free_check_null(last_model.add_formula);
|
|
last_model.si = (double *) free_check_null(last_model.si);
|
|
if (use.pp_assemblage_ptr != NULL) {
|
|
last_model.count_pp_assemblage = use.pp_assemblage_ptr->count_comps;
|
|
last_model.pp_assemblage = (struct phase **) PHRQ_malloc((size_t) use.pp_assemblage_ptr->count_comps * sizeof(struct phase *));
|
|
if (last_model.pp_assemblage == NULL) malloc_error();
|
|
last_model.add_formula = (char **) PHRQ_malloc((size_t) use.pp_assemblage_ptr->count_comps * sizeof(char *));
|
|
if (last_model.add_formula == NULL) malloc_error();
|
|
last_model.si = (double *) PHRQ_malloc((size_t) use.pp_assemblage_ptr->count_comps * sizeof(LDBLE));
|
|
if (last_model.si == NULL) malloc_error();
|
|
for (i = 0; i < use.pp_assemblage_ptr->count_comps; i++) {
|
|
last_model.pp_assemblage[i] = use.pp_assemblage_ptr->pure_phases[i].phase;
|
|
last_model.add_formula[i] = use.pp_assemblage_ptr->pure_phases[i].add_formula;
|
|
last_model.si[i] = use.pp_assemblage_ptr->pure_phases[i].si;
|
|
}
|
|
} else {
|
|
last_model.count_pp_assemblage = 0;
|
|
last_model.pp_assemblage = NULL;
|
|
last_model.add_formula = NULL;
|
|
last_model.si = NULL;
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int check_same_model(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
int i;
|
|
/*
|
|
* Force new model to be built in prep
|
|
*/
|
|
if (last_model.force_prep == TRUE) {
|
|
last_model.force_prep = FALSE;
|
|
same_temperature = FALSE;
|
|
return(FALSE);
|
|
}
|
|
/*
|
|
* Check temperature
|
|
*/
|
|
if (fabs(tc_x - last_model.temperature) < 0.05) {
|
|
same_temperature = TRUE;
|
|
} else {
|
|
same_temperature = FALSE;
|
|
}
|
|
/*
|
|
* Check master species
|
|
*/
|
|
for (i = 0; i < count_master; i++) {
|
|
/*
|
|
output_msg(OUTPUT_MESSAGE,"%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) continue;
|
|
if (master[i]->total <= MIN_TOTAL && master[i]->last_model == FALSE) continue;
|
|
return(FALSE);
|
|
}
|
|
/*
|
|
* Check gas_phase
|
|
*/
|
|
if (use.gas_phase_ptr != NULL) {
|
|
if (last_model.count_gas_phase != use.gas_phase_ptr->count_comps) return(FALSE);
|
|
for (i = 0; i < use.gas_phase_ptr->count_comps; i++) {
|
|
if(last_model.gas_phase[i] != use.gas_phase_ptr->comps[i].phase) {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
} else {
|
|
if(last_model.gas_phase != NULL) return(FALSE);
|
|
}
|
|
/*
|
|
* Check solid solutions
|
|
*/
|
|
if (use.s_s_assemblage_ptr != NULL) {
|
|
if (last_model.count_s_s_assemblage != use.s_s_assemblage_ptr->count_s_s) return(FALSE);
|
|
for (i = 0; i < use.s_s_assemblage_ptr->count_s_s; i++) {
|
|
if(last_model.s_s_assemblage[i] != use.s_s_assemblage_ptr->s_s[i].name) {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
} else {
|
|
if(last_model.s_s_assemblage != NULL) return(FALSE);
|
|
}
|
|
/*
|
|
* Check pure_phases
|
|
*/
|
|
if (use.pp_assemblage_ptr != NULL) {
|
|
if (last_model.count_pp_assemblage != use.pp_assemblage_ptr->count_comps) return(FALSE);
|
|
for (i = 0; i < use.pp_assemblage_ptr->count_comps; i++) {
|
|
if(last_model.pp_assemblage[i] != use.pp_assemblage_ptr->pure_phases[i].phase) {
|
|
return(FALSE);
|
|
}
|
|
if(last_model.add_formula[i] != use.pp_assemblage_ptr->pure_phases[i].add_formula) {
|
|
return(FALSE);
|
|
}
|
|
if(last_model.si[i] != use.pp_assemblage_ptr->pure_phases[i].si) {
|
|
return(FALSE);
|
|
}
|
|
}
|
|
} else {
|
|
if(last_model.pp_assemblage != NULL) return(FALSE);
|
|
}
|
|
/*
|
|
* Model is the same
|
|
*/
|
|
return(TRUE);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int build_min_exch(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Defines proportionality factor between mineral and exchanger to
|
|
* jacob0
|
|
*/
|
|
int i, j, k, n, jj;
|
|
int row;
|
|
struct exch_comp *comp_ptr;
|
|
struct master *master_ptr;
|
|
struct unknown *unknown_ptr;
|
|
LDBLE coef;
|
|
|
|
if (use.exchange_ptr == NULL) return(OK);
|
|
if (exchange_bsearch(use.exchange_ptr->n_user, &n) == NULL) {
|
|
input_error++;
|
|
sprintf(error_string,"Exchange %d not found.", use.exchange_ptr->n_user);
|
|
error_msg(error_string, CONTINUE);
|
|
}
|
|
if (exchange[n].related_phases == FALSE) return(OK);
|
|
for (i=0; i<exchange[n].count_comps; i++) {
|
|
if (exchange[n].comps[i].phase_name == NULL) continue;
|
|
|
|
/* find unknown number */
|
|
for (j=count_unknowns-1; j>=0; j--) {
|
|
if (x[j]->type != EXCH) continue;
|
|
if (x[j]->master[0] == exchange[n].comps[i].master) break;
|
|
}
|
|
for (k=count_unknowns-1; k>=0; k--) {
|
|
if (x[k]->type != PP) continue;
|
|
if (x[k]->phase->name == exchange[n].comps[i].phase_name) break;
|
|
}
|
|
if (j == -1 ) {
|
|
input_error++;
|
|
sprintf(error_string,"Did not find unknown for master exchange species %s", exchange[n].comps[i].master->s->name);
|
|
error_msg(error_string, CONTINUE);
|
|
}
|
|
if (j == -1 || k == -1) continue;
|
|
/*
|
|
* Build jacobian
|
|
*/
|
|
comp_ptr = &exchange[n].comps[i];
|
|
|
|
/* charge balance */
|
|
store_jacob0(charge_balance_unknown->number, x[k]->number, comp_ptr->formula_z * comp_ptr->phase_proportion);
|
|
store_sum_deltas(&delta[k], &charge_balance_unknown->delta, -comp_ptr->formula_z * comp_ptr->phase_proportion);
|
|
|
|
|
|
/* mole balance balance */
|
|
for (jj = 0; comp_ptr->formula_totals[jj].elt != NULL; jj++) {
|
|
master_ptr = comp_ptr->formula_totals[jj].elt->primary;
|
|
if (master_ptr->in == FALSE) {
|
|
master_ptr = master_ptr->s->secondary;
|
|
}
|
|
if (master_ptr == NULL) {
|
|
input_error++;
|
|
sprintf(error_string,"Did not find unknown for exchange related to mineral %s", exchange[n].comps[i].phase_name);
|
|
error_msg(error_string, STOP);
|
|
}
|
|
if (master_ptr->s->type == EX) {
|
|
if (equal(x[j]->moles, x[k]->moles * comp_ptr->formula_totals[jj].coef * comp_ptr->phase_proportion, 1.e-8) == FALSE) {
|
|
input_error++;
|
|
sprintf(error_string,"Number of sites in exchanger %s (=%e) not consistent with moles calculated from phase %s (%e).", master_ptr->s->name, (double) x[j]->moles, comp_ptr->phase_name, (double) (x[k]->moles * comp_ptr->formula_totals[jj].coef * comp_ptr->phase_proportion));
|
|
error_msg(error_string, CONTINUE);
|
|
}
|
|
}
|
|
coef = comp_ptr->formula_totals[jj].coef;
|
|
if (master_ptr->s == s_hplus) {
|
|
row = mass_hydrogen_unknown->number;
|
|
unknown_ptr = mass_hydrogen_unknown;
|
|
#ifdef COMBINE
|
|
#ifndef COMBINE_CHARGE
|
|
coef = x[j]->master[0]->s->h - 2 * x[j]->master[0]->s->o;
|
|
#else
|
|
coef = x[j]->master[0]->s->h - 2 * x[j]->master[0]->s->o - x[j]->master[0]->s->z ;
|
|
#endif
|
|
#endif
|
|
} else if (master_ptr->s == s_h2o) {
|
|
row = mass_oxygen_unknown->number;
|
|
unknown_ptr = mass_oxygen_unknown;
|
|
} else {
|
|
row = master_ptr->unknown->number;
|
|
unknown_ptr = master_ptr->unknown;
|
|
}
|
|
store_jacob0(row, x[k]->number, coef * comp_ptr->phase_proportion);
|
|
store_sum_deltas(&delta[k], &unknown_ptr->delta, -coef * comp_ptr->phase_proportion);
|
|
}
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int build_min_surface(void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Defines proportionality factor between mineral and surface to
|
|
* jacob0
|
|
*/
|
|
int i, j, k, n, jj, row;
|
|
struct elt_list *next_elt;
|
|
struct surface_comp *comp_ptr;
|
|
struct unknown *unknown_ptr;
|
|
struct master *master_ptr;
|
|
LDBLE coef;
|
|
|
|
if (use.surface_ptr == NULL) return(OK);
|
|
if (surface_bsearch(use.surface_ptr->n_user, &n) == NULL) {
|
|
input_error++;
|
|
sprintf(error_string,"Surface %d not found.", use.surface_ptr->n_user);
|
|
error_msg(error_string, CONTINUE);
|
|
}
|
|
if (surface[n].related_phases == FALSE) return(OK);
|
|
for (i=0; i<surface[n].count_comps; i++) {
|
|
if (surface[n].comps[i].phase_name == NULL) continue;
|
|
|
|
/* find unknown number */
|
|
for (j=count_unknowns-1; j>=0; j--) {
|
|
if (x[j]->type != SURFACE) continue;
|
|
if (x[j]->master[0] == surface[n].comps[i].master) break;
|
|
}
|
|
for (k=count_unknowns-1; k>=0; k--) {
|
|
if (x[k]->type != PP) continue;
|
|
if (x[k]->phase->name == surface[n].comps[i].phase_name) break;
|
|
}
|
|
if (j == -1 ) {
|
|
input_error++;
|
|
sprintf(error_string,"Did not find unknown for master surface species %s", surface[n].comps[i].master->s->name);
|
|
error_msg(error_string, CONTINUE);
|
|
}
|
|
if (j == -1 || k == -1) continue;
|
|
|
|
next_elt = x[j]->master[0]->s->next_elt;
|
|
comp_ptr = x[j]->surface_comp;
|
|
|
|
/* update grams == moles in this case */
|
|
if(j < count_unknowns - 1 && x[j+1]->type == SURFACE_CB) {
|
|
#ifdef SKIP
|
|
/* Major error, can't use "use." because it changes every time
|
|
have to use only things allocated in prep.c Now put
|
|
grams into unknown structure. */
|
|
/*
|
|
store_sum_deltas(&delta[k], &(use.surface_ptr->charge[comp_ptr->charge].grams), -1.0);
|
|
*/
|
|
#endif
|
|
store_sum_deltas(&delta[k], &(x[j+1]->related_moles), -1.0);
|
|
}
|
|
/* mole balance balance */
|
|
for (jj = 0; next_elt[jj].elt != NULL; jj++) {
|
|
master_ptr = next_elt[jj].elt->primary;
|
|
if (master_ptr->in == FALSE) {
|
|
master_ptr = master_ptr->s->secondary;
|
|
}
|
|
if (master_ptr == NULL) {
|
|
input_error++;
|
|
sprintf(error_string,"Did not find unknown for surface related to mineral %s", surface[n].comps[i].phase_name);
|
|
error_msg(error_string, STOP);
|
|
}
|
|
if (master_ptr->s->type == SURF) {
|
|
if (equal(x[j]->moles, x[k]->moles * next_elt[jj].coef * comp_ptr->phase_proportion, 1.e-8) == FALSE) {
|
|
input_error++;
|
|
sprintf(error_string,"Number of sites in surface %s (=%e) not consistent with moles of phase %s (=%e).\n%s", master_ptr->s->name, (double) x[j]->moles, comp_ptr->phase_name, (double) (x[k]->moles * next_elt[jj].coef * comp_ptr->phase_proportion), "\tHas equilibrium_phase assemblage been redefined?\n");
|
|
error_msg(error_string, CONTINUE);
|
|
}
|
|
}
|
|
coef = next_elt[jj].coef;
|
|
if (master_ptr->s == s_hplus) {
|
|
row = mass_hydrogen_unknown->number;
|
|
unknown_ptr = mass_hydrogen_unknown;
|
|
#ifdef COMBINE
|
|
#ifndef COMBINE_CHARGE
|
|
coef = x[j]->master[0]->s->h - 2 * x[j]->master[0]->s->o;
|
|
#else
|
|
coef = x[j]->master[0]->s->h - 2 * x[j]->master[0]->s->o - x[j]->master[0]->s->z;
|
|
#endif
|
|
#endif
|
|
} else if (master_ptr->s == s_h2o) {
|
|
row = mass_oxygen_unknown->number;
|
|
unknown_ptr = mass_oxygen_unknown;
|
|
} else {
|
|
row = master_ptr->unknown->number;
|
|
unknown_ptr = master_ptr->unknown;
|
|
}
|
|
store_jacob0(row, x[k]->number, coef * comp_ptr->phase_proportion);
|
|
store_sum_deltas(&delta[k], &unknown_ptr->delta, -coef * comp_ptr->phase_proportion);
|
|
}
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int setup_related_surface (void)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
/*
|
|
* Fill in data for surface assemblage in unknown structure
|
|
*/
|
|
int i, k;
|
|
struct surface_comp *comp_ptr;
|
|
|
|
if (use.surface_ptr == NULL) return(OK);
|
|
if (use.surface_ptr->related_phases == FALSE) return(OK);
|
|
|
|
for (i = 0; i < count_unknowns; i++) {
|
|
if (x[i]->type == SURFACE && x[i]->surface_comp->phase_name != NULL) {
|
|
for (k=count_unknowns-1; k>=0; k--) {
|
|
if (x[k]->type != PP) continue;
|
|
if (x[k]->phase->name == x[i]->surface_comp->phase_name) break;
|
|
}
|
|
if (k == -1) continue;
|
|
|
|
comp_ptr = x[i]->surface_comp;
|
|
x[i]->phase_unknown = x[k];
|
|
/* !!!!! */
|
|
x[i]->moles = x[k]->moles * comp_ptr->phase_proportion;
|
|
|
|
} else if (x[i]->type == SURFACE_CB && x[i-1]->surface_comp->phase_name != NULL) {
|
|
for (k=count_unknowns-1; k>=0; k--) {
|
|
if (x[k]->type != PP) continue;
|
|
if (x[k]->phase->name == x[i]->surface_comp->phase_name) break;
|
|
}
|
|
if (k == -1) continue;
|
|
|
|
comp_ptr = x[i]->surface_comp;
|
|
x[i]->phase_unknown = x[k];
|
|
/* !!!! Added for security, not checked... */
|
|
x[i]->related_moles = x[k]->moles * comp_ptr->phase_proportion;
|
|
}
|
|
}
|
|
return(OK);
|
|
}
|
|
/* ---------------------------------------------------------------------- */
|
|
int change_hydrogen_in_elt_list(LDBLE charge)
|
|
/* ---------------------------------------------------------------------- */
|
|
{
|
|
int j;
|
|
int found_h, found_o;
|
|
LDBLE coef_h, coef_o, coef;
|
|
found_h = -1;
|
|
found_o = -1;
|
|
coef_h = 0.0;
|
|
coef_o = 0.0;
|
|
qsort (elt_list, (size_t) count_elts,
|
|
(size_t) sizeof(struct elt_list), elt_list_compare);
|
|
elt_list_combine();
|
|
for (j = 0; j < count_elts; j++) {
|
|
if (strcmp(elt_list[j].elt->name,"H") == 0) {
|
|
found_h = j;
|
|
coef_h = elt_list[j].coef;
|
|
} else if (strcmp(elt_list[j].elt->name,"O") == 0) {
|
|
found_o = j;
|
|
coef_o = elt_list[j].coef;
|
|
}
|
|
}
|
|
coef = coef_h-2*coef_o - charge;
|
|
if (found_h < 0 && found_o < 0) return(OK);
|
|
if (found_h >= 0 && found_o < 0) return(OK);
|
|
if (found_h < 0 && found_o >= 0) {
|
|
elt_list[count_elts].elt = s_hplus->primary->elt;
|
|
elt_list[count_elts].coef = coef;
|
|
count_elts++;
|
|
qsort (elt_list, (size_t) count_elts,
|
|
(size_t) sizeof(struct elt_list), elt_list_compare);
|
|
elt_list_combine();
|
|
return(OK);
|
|
}
|
|
elt_list[found_h].coef = coef;
|
|
return(OK);
|
|
}
|