#include "Utils.h" #include "Phreeqc.h" #include "phqalloc.h" #include "Exchange.h" #include "GasPhase.h" #include "PPassemblage.h" #include "SSassemblage.h" #include "cxxKinetics.h" #include "Solution.h" #define ZERO_TOL 1.0e-30 #if defined(PHREEQCI_GUI) #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #endif /* ---------------------------------------------------------------------- */ int Phreeqc:: tidy_model(void) /* ---------------------------------------------------------------------- */ { int n_user, last; int new_named_logk; /* * Determine if any new elements, species, phases have been read */ overall_iterations = 0; state = INITIALIZE; new_model = FALSE; new_pp_assemblage = FALSE; new_surface = FALSE; new_exchange = FALSE; new_reaction = FALSE; new_temperature = FALSE; new_mix = FALSE; new_solution = FALSE; new_gas_phase = FALSE; new_inverse = FALSE; new_punch = FALSE; new_surface = FALSE; new_ss_assemblage = FALSE; new_kinetics = FALSE; new_pitzer = FALSE; new_named_logk = FALSE; if (keycount[Keywords::KEY_SOLUTION_SPECIES] > 0 || /*"species" */ keycount[Keywords::KEY_SOLUTION_MASTER_SPECIES] > 0 || /*"master" */ keycount[Keywords::KEY_PHASES] > 0 || /*"phases" */ keycount[Keywords::KEY_EXCHANGE_SPECIES] > 0 || /*"exchange_species" */ keycount[Keywords::KEY_EXCHANGE_MASTER_SPECIES] > 0 || /*"master_exchange_species" */ keycount[Keywords::KEY_SURFACE_SPECIES] > 0 || /*"surface_species" */ keycount[Keywords::KEY_SURFACE_MASTER_SPECIES] > 0 || /*"master_surface_species" */ keycount[Keywords::KEY_RATES] > 0 || /*"rates" */ keycount[Keywords::KEY_LLNL_AQUEOUS_MODEL_PARAMETERS] > 0 || /*"llnl_aqueous_model_parameters" */ (keycount[Keywords::KEY_DATABASE] > 0 && simulation == 0) || /*"database" */ keycount[Keywords::KEY_NAMED_EXPRESSIONS] > 0 || /*"named_analytical_expressions" */ keycount[Keywords::KEY_ISOTOPES] > 0 || /*"isotopes" */ keycount[Keywords::KEY_CALCULATE_VALUES] > 0 || /*"calculate_values" */ keycount[Keywords::KEY_ISOTOPE_RATIOS] > 0 || /*"isotopes_ratios", */ keycount[Keywords::KEY_ISOTOPE_ALPHAS] > 0 || /*"isotopes_alphas" */ keycount[Keywords::KEY_PITZER] > 0 || /*"pitzer" */ keycount[Keywords::KEY_SIT] > 0 /*"sit" */ ) { new_model = TRUE; } if (keycount[Keywords::KEY_EQUILIBRIUM_PHASES] > 0 || keycount[Keywords::KEY_EQUILIBRIUM_PHASES_RAW] > 0 || keycount[Keywords::KEY_EQUILIBRIUM_PHASES_MODIFY]) { new_pp_assemblage = TRUE; /*"pure_phases" */ } if (keycount[Keywords::KEY_SURFACE] > 0 || keycount[Keywords::KEY_SURFACE_RAW] > 0 || keycount[Keywords::KEY_SURFACE_MODIFY]) { new_surface = TRUE; /*"surface" */ } if (keycount[Keywords::KEY_EXCHANGE] > 0 || keycount[Keywords::KEY_EXCHANGE_RAW] > 0 || keycount[Keywords::KEY_EXCHANGE_MODIFY]) { new_exchange = TRUE; /*"exchange" */ } if (keycount[Keywords::KEY_REACTION] > 0 /*|| keycount[Keywords::KEY_REACTION_RAW] > 0 || keycount[Keywords::KEY_REACTION_MODIFY]*/) { new_reaction = TRUE; /*"reaction" */ } if (keycount[Keywords::KEY_REACTION_TEMPERATURE] > 0 /*|| keycount[Keywords::KEY_REACTION_TEMPERATURE_RAW] > 0 || keycount[Keywords::KEY_REACTION_TEMPERATURE_MODIFY]*/) { new_temperature = TRUE; /*"reacton_temperature" */ } if (keycount[Keywords::KEY_MIX] > 0 || keycount[Keywords::KEY_MIX_RAW] > 0) { new_mix = TRUE; /*"mix" */ } if (keycount[Keywords::KEY_SOLUTION] > 0 || keycount[Keywords::KEY_SOLUTION_SPREAD] > 0 || keycount[Keywords::KEY_SOLUTION_RAW] > 0 || keycount[Keywords::KEY_SOLUTION_MODIFY]) { /*"solution" */ new_solution = TRUE; } if (keycount[Keywords::KEY_GAS_PHASE] > 0 || keycount[Keywords::KEY_GAS_PHASE_RAW] > 0 || keycount[Keywords::KEY_GAS_PHASE_MODIFY]) { new_gas_phase = TRUE; /*"gas_phase" */ } if (keycount[Keywords::KEY_SOLID_SOLUTIONS] > 0 || keycount[Keywords::KEY_SOLID_SOLUTIONS_RAW] > 0 || keycount[Keywords::KEY_SOLID_SOLUTIONS_MODIFY]) { new_ss_assemblage = TRUE; /*"solid_solutions" */ } if (keycount[Keywords::KEY_KINETICS] > 0 /*|| keycount[Keywords::KEY_KINETICS_RAW] > 0 || keycount[Keywords::KEY_KINETICS_MODIFY]*/) { new_kinetics = TRUE; /*"kinetics" */ } if (keycount[Keywords::KEY_INVERSE_MODELING] > 0) { new_inverse = TRUE; /*"inverse_modeling" */ } if (keycount[Keywords::KEY_SELECTED_OUTPUT] > 0 || /*"selected_output" */ keycount[Keywords::KEY_USER_PUNCH] > 0) /*"user_punch" */ { new_punch = TRUE; } if (keycount[Keywords::KEY_COPY] > 0) { new_copy = TRUE; /*"copy" */ } if (keycount[Keywords::KEY_PITZER] > 0) { new_pitzer = TRUE; /*"pitzer" */ } if (keycount[Keywords::KEY_NAMED_EXPRESSIONS] > 0) { new_named_logk = TRUE; /*"named_log_k" */ } /* * Sort arrays */ if (new_model == TRUE) { /* species */ if (s.size() > 1) //qsort(&s[0], s.size(), sizeof(class species*), s_compare); { s.clear(); std::map::iterator it; for (it = species_map.begin(); it != species_map.end(); it++) { s.push_back(it->second); } } /* master species */ if (master.size() > 1) qsort(&master[0], master.size(), sizeof(class master*), master_compare); /* elements */ if (elements.size() > 1) //qsort(&elements[0], elements.size(), sizeof(class element*), element_compare); { elements.clear(); std::map::iterator it; for (it = elements_map.begin(); it != elements_map.end(); it++) { elements.push_back(it->second); } } /* phases */ if (phases.size() > 1) //qsort(&phases[0], phases.size(), sizeof(class phase *), phase_compare); { phases.clear(); std::map::iterator it; for (it = phases_map.begin(); it != phases_map.end(); it++) { phases.push_back(it->second); } } } /* named_log_k */ if (new_named_logk) { tidy_logk(); } /* * Check pointers, write reactions for species */ if (new_model) { sum_species_map.clear(); tidy_species(); tidy_phases(); tidy_master_isotope(); /* * calculate gfw of water, kg/mole */ compute_gfw("H2O", &gfw_water); gfw_water *= 0.001; } /* * tidy surface data */ if (new_model || new_surface) { tidy_surface(); } /* * tidy inverse data */ if (new_inverse) { tidy_inverse(); } /* * tidy gas phase data */ if (new_gas_phase) { tidy_gas_phase(); } /* * tidy pp_assemblage data */ if (new_model || new_pp_assemblage) { tidy_pp_assemblage(); } /* * tidy ss_assemblage data */ if (new_model || new_ss_assemblage) { tidy_ss_assemblage(); } /* * tidy exchange data, after pp_assemblages */ if (new_exchange) { tidy_exchange(); tidy_min_exchange(); tidy_kin_exchange(); } /* * tidy surface data */ if (new_surface) { tidy_min_surface(); tidy_kin_surface(); } /* * tidy solution isotope data */ if (new_solution) { tidy_isotopes(); } if (new_model) { tidy_isotope_ratios(); tidy_isotope_alphas(); } /* * Duplicate kinetics */ if (new_kinetics) { std::map::iterator it; for (it = Rxn_kinetics_map.begin(); it != Rxn_kinetics_map.end(); it++) { n_user = it->second.Get_n_user(); last = it->second.Get_n_user_end(); it->second.Set_n_user_end(n_user); Utilities::Rxn_copies(Rxn_kinetics_map, n_user, last); } } /* * Tidy pitzer information */ if (pitzer_model && new_model) { pitzer_tidy(); } /* * Tidy SIT information */ if (sit_model && new_model) { sit_tidy(); } /* * Tidy punch information */ if (get_input_errors() == 0 && (new_punch || new_model)) { tidy_punch(); } /* * Tidy solution information */ if (new_solution) { tidy_solutions(); } /* * need to update exchange and surface related in case anything has changed */ if (keycount[Keywords::KEY_KINETICS] > 0 || keycount[Keywords::KEY_KINETICS_RAW] > 0 || keycount[Keywords::KEY_KINETICS_MODIFY] || keycount[Keywords::KEY_EXCHANGE] > 0 || keycount[Keywords::KEY_EXCHANGE_RAW] > 0 || keycount[Keywords::KEY_EXCHANGE_MODIFY]) { update_kin_exchange(); } if (keycount[Keywords::KEY_EQUILIBRIUM_PHASES] > 0 || keycount[Keywords::KEY_EQUILIBRIUM_PHASES_RAW] > 0 || keycount[Keywords::KEY_EQUILIBRIUM_PHASES_MODIFY] || keycount[Keywords::KEY_EXCHANGE] > 0 || keycount[Keywords::KEY_EXCHANGE_RAW] > 0 || keycount[Keywords::KEY_EXCHANGE_MODIFY]) { update_min_exchange(); } if (keycount[Keywords::KEY_EQUILIBRIUM_PHASES] > 0 || keycount[Keywords::KEY_EQUILIBRIUM_PHASES_RAW] > 0 || keycount[Keywords::KEY_EQUILIBRIUM_PHASES_MODIFY] || keycount[Keywords::KEY_SURFACE] > 0 || keycount[Keywords::KEY_SURFACE_RAW] > 0 || keycount[Keywords::KEY_SURFACE_MODIFY] > 0) { update_min_surface(); } if (keycount[Keywords::KEY_KINETICS] > 0 || keycount[Keywords::KEY_KINETICS_RAW] > 0 || keycount[Keywords::KEY_KINETICS_MODIFY] > 0 || keycount[Keywords::KEY_SURFACE] > 0 || keycount[Keywords::KEY_SURFACE_RAW] > 0 || keycount[Keywords::KEY_SURFACE_MODIFY] > 0) { update_kin_surface(); } /* if (new_model || new_exchange || new_pp_assemblage || new_surface || new_gas_phase || new_kinetics) reset_last_model(); */ if (new_model) { reset_last_model(); } /* * make sure essential species are defined */ //if (new_model) { if (s_h2o == NULL) { input_error++; //error_msg("H2O not defined.", STOP); error_msg("H2O not defined.", CONTINUE); } else { if (s_h2o->primary == NULL) { input_error++; error_msg("H2O, primary master species for O, not defined.", CONTINUE); } if (s_h2o->secondary == NULL) { input_error++; error_msg("H2O, secondary master species for O(-2), not defined.", CONTINUE); } if (s_h2o->type != H2O) { input_error++; error_msg("H2O can only be defined in SOLUTION_SPECIES.", CONTINUE); } } if (s_hplus == NULL && s_h3oplus == NULL) { input_error++; error_msg("Neither H+ nor H3O+ are defined in solution_species.", STOP); } else if (s_hplus == NULL && s_h3oplus != NULL) { s_hplus = s_h3oplus; s_h3oplus = NULL; } else if (s_hplus != NULL && s_h3oplus == NULL) { } else if (s_hplus != NULL && s_h3oplus != NULL) { input_error++; error_msg("Cannot define both H+ and H3O+ in solution_species.", STOP); } if (s_hplus->primary == NULL) { input_error++; error_msg("H3O+, primary master species for H, not defined.", CONTINUE); } if (s_hplus->secondary == NULL) { input_error++; error_msg("H3O+, secondary master species for H(1), not defined.", CONTINUE); } if (s_eminus == NULL) { input_error++; error_msg("e- not defined in solution_species.", CONTINUE); } if (s_eminus->primary == NULL) { input_error++; error_msg("e-, primary master species for E-, not defined.", CONTINUE); } if (pitzer_model == FALSE || pitzer_pe == TRUE) { if (s_h2 == NULL) { input_error++; error_msg("H2(aq) not defined in solution_species.", CONTINUE); } if (s_o2 == NULL) { input_error++; error_msg("O2(aq) not defined in solution_species.", CONTINUE); } } element_h_one = element_store("H(1)"); if (element_h_one == NULL) { input_error++; error_msg("H(1) not defined in solution_master_species.", CONTINUE); } } /* * Error check, program termination */ if (get_input_errors() > 0 || parse_error > 0) { error_msg("Calculations terminating due to input errors.", STOP); } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: check_species_input(void) /* ---------------------------------------------------------------------- */ { /* * Check species data for completeness */ int i; int return_value; return_value = OK; for (i = 0; i < (int)s.size(); i++) { if (s[i]->next_elt.size() == 0) { input_error++; return_value = ERROR; error_string = sformatf( "Elements in species have not been tabulated, %s.", s[i]->name); error_msg(error_string, CONTINUE); } if (s[i]->rxn.token.size() == 0) { input_error++; return_value = ERROR; error_string = sformatf( "Reaction for species has not been defined, %s.", s[i]->name); error_msg(error_string, CONTINUE); } else { select_log_k_expression(s[i]->logk, s[i]->rxn.logk); add_other_logk(s[i]->rxn.logk, s[i]->add_logk); } } return (return_value); } /* ---------------------------------------------------------------------- */ int Phreeqc:: select_log_k_expression(LDBLE * source_k, LDBLE * target_k) /* ---------------------------------------------------------------------- */ { int j; bool analytic; analytic = false; for (j = T_A1; j <= T_A6; j++) { if (source_k[j] != 0.0) { analytic = true; break; } } if (analytic) { target_k[logK_T0] = 0.0; target_k[delta_h] = 0.0; for (j = T_A1; j <= T_A6; j++) { target_k[j] = source_k[j]; } } else { target_k[logK_T0] = source_k[logK_T0]; target_k[delta_h] = source_k[delta_h]; for (j = T_A1; j <= T_A6; j++) { target_k[j] = 0.0; } } for (j = delta_v; j < MAX_LOG_K_INDICES; j++) { target_k[j] = source_k[j]; } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: tidy_logk(void) /* ---------------------------------------------------------------------- */ /* * Picks log k expression */ { int i; for (i = 0; i < (int)logk.size(); i++) { select_log_k_expression(logk[i]->log_k_original, logk[i]->log_k); logk[i]->done = FALSE; } for (i = 0; i < (int)logk.size(); i++) { if (logk[i]->done == FALSE) { add_logks(logk[i], 0); } } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: add_other_logk(LDBLE * source_k, std::vector &add_logk) /* ---------------------------------------------------------------------- */ { int j; bool analytic; class logk *logk_ptr; LDBLE coef; for (size_t i = 0; i < add_logk.size(); i++) { coef = add_logk[i].coef; std::string token = add_logk[i].name; str_tolower(token); std::map::iterator l_it = logk_map.find(token); if (l_it == logk_map.end()) { input_error++; error_string = sformatf( "Could not find named temperature expression, %s\n", token.c_str()); error_msg(error_string, CONTINUE); return (ERROR); } logk_ptr = l_it->second; analytic = false; for (j = T_A1; j <= T_A6; j++) { if (logk_ptr->log_k[j] != 0.0) { analytic = true; break; } } if (analytic) { for (j = T_A1; j <= T_A6; j++) { source_k[j] += logk_ptr->log_k[j] * coef; } } else { source_k[logK_T0] += logk_ptr->log_k[logK_T0] * coef; source_k[delta_h] += logk_ptr->log_k[delta_h] * coef; } for (j = delta_v; j < MAX_LOG_K_INDICES; j++) { source_k[j] += logk_ptr->log_k[j] * coef; } } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: add_logks(class logk *logk_ptr, int repeats) /* ---------------------------------------------------------------------- */ { int i, j; class logk *next_logk_ptr; LDBLE coef; /* * Adds in other named_expressions to get complete log K * Evaluates others recursively if necessary */ if (repeats > 15) { input_error++; error_string = sformatf( "Circular definition of named_logk? %s\n", logk_ptr->name); error_msg(error_string, CONTINUE); return (ERROR); } for (i = 0; i < (int)logk_ptr->add_logk.size(); i++) { coef = logk_ptr->add_logk[i].coef; std::string token = logk_ptr->add_logk[i].name; str_tolower(token); std::map::iterator l_it = logk_map.find(token); if (l_it == logk_map.end()) { input_error++; error_string = sformatf( "Could not find named temperature expression, %s\n", token.c_str()); error_msg(error_string, CONTINUE); return (ERROR); } next_logk_ptr = l_it->second; if (next_logk_ptr->done == FALSE) { /*output_msg(sformatf( "Done == FALSE\n", token)); */ if (add_logks(next_logk_ptr, repeats + 1) == ERROR) { return (ERROR); } } for (j = 0; j < MAX_LOG_K_INDICES; j++) { logk_ptr->log_k[j] += next_logk_ptr->log_k[j] * coef; } } logk_ptr->done = TRUE; return (OK); } /* ---------------------------------------------------------------------- */ LDBLE Phreeqc:: coef_in_master(class master * master_ptr) /* ---------------------------------------------------------------------- */ { int l; LDBLE coef; const char* cptr; std::string elt_name; const class elt_list *next_elt; coef = 0.0; cptr = master_ptr->elt->name; get_elt(&cptr, elt_name, &l); for (next_elt = &master_ptr->s->next_elt[0]; next_elt->elt != NULL; next_elt++) { if (strcmp(elt_name.c_str(), next_elt->elt->name) == 0) { coef = next_elt->coef; break; } } return (coef); } /* ---------------------------------------------------------------------- */ int Phreeqc:: rewrite_eqn_to_secondary(void) /* ---------------------------------------------------------------------- */ { /* * Write equation for species in terms of secondary species * Result is in trxn. */ LDBLE coef; int repeat, i, add_count; class rxn_token_temp *token_ptr; /* * */ add_count = 0; repeat = TRUE; /* * Reduce reaction equation to primary and secondary species */ while (repeat == TRUE) { repeat = FALSE; /* Check for too many iterations */ if (++add_count > MAX_ADD_EQUATIONS) { parse_error++; error_string = sformatf( "Could not reduce equation to secondary master species, %s.", trxn.token[0].name); error_msg(error_string, CONTINUE); break; } for (i = 1; i < count_trxn; i++) { token_ptr = &(trxn.token[i]); if (token_ptr->s == NULL) { error_string = sformatf( "NULL species pointer for species, %s.", token_ptr->name); error_msg(error_string, CONTINUE); input_error++; break; } if (token_ptr->s->secondary == NULL && token_ptr->s->primary == NULL) { coef = token_ptr->coef; trxn_add(token_ptr->s->rxn, coef, true); repeat = TRUE; break; } } } trxn_combine(); return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: replace_solids_gases(void) /* ---------------------------------------------------------------------- */ { /* * Write equation for species in terms of secondary species * Result is in trxn. */ LDBLE coef; int n; int repeat, i, add_count; class rxn_token_temp *token_ptr; class phase *phase_ptr; int replaced; char token[MAX_LENGTH]; /* * */ add_count = 0; repeat = TRUE; replaced = FALSE; /* * Reduce reaction equation to primary and secondary species */ while (repeat == TRUE) { repeat = FALSE; /* Check for too many iterations */ if (++add_count > MAX_ADD_EQUATIONS) { parse_error++; error_string = sformatf( "Could not remove all solids and gases from equation, %s.", trxn.token[0].name); error_msg(error_string, CONTINUE); break; } for (i = 1; i < count_trxn; i++) { token_ptr = &(trxn.token[i]); if (token_ptr->s == NULL) { phase_ptr = phase_bsearch(token_ptr->name, &n, FALSE); /* try phase name without (g) or (s) */ if (phase_ptr == NULL) { strcpy(token, token_ptr->name); replace("(g)", "", token); replace("(s)", "", token); replace("(G)", "", token); replace("(S)", "", token); phase_ptr = phase_bsearch(token, &n, FALSE); } if (phase_ptr == NULL) { input_error++; error_string = sformatf( "Phase not found, %s.", token_ptr->name); error_msg(error_string, CONTINUE); break; } coef = token_ptr->coef; /* add reaction for solid/gas */ /* debug output_msg(sformatf( "Reaction to add.\n")); rxn_print(phase_ptr->rxn); */ trxn_add_phase(phase_ptr->rxn, coef, false); /* remove solid/gas from trxn list */ trxn.token[i].name = phase_ptr->rxn.token[0].name; trxn.token[i].s = phase_ptr->rxn.token[0].s; trxn.token[i].coef = -coef * phase_ptr->rxn.token[0].coef; repeat = TRUE; replaced = TRUE; /* debug output_msg(sformatf( "Before combined.\n")); trxn_print(); */ /* combine */ trxn_combine(); /* debug output_msg(sformatf( "Combined.\n")); trxn_print(); */ break; } } } trxn_combine(); return (replaced); } /* ---------------------------------------------------------------------- */ int Phreeqc:: rewrite_eqn_to_primary(void) /* ---------------------------------------------------------------------- */ { /* * Write equation for secondary master species in terms of primary master species * Store result in reaction structure for master species * rewrite if necessary. * */ int repeat, j, add_count; /* * Check secondary master species */ repeat = TRUE; add_count = 0; /* * Check if reaction contains only primary master species */ while (repeat == TRUE) { repeat = FALSE; /* * Check for too many iterations */ if (++add_count > MAX_ADD_EQUATIONS) { parse_error++; error_string = sformatf( "Could not reduce equation to primary master species, %s.", trxn.token[0].s->name); error_msg(error_string, CONTINUE); break; } /* * Go through species in reaction for secondary master species, look for non-primary * species as reactants, rewrite */ for (j = 1; j < count_trxn; j++) { if (trxn.token[j].s->primary == NULL) { trxn_add(trxn.token[j].s->rxn, trxn.token[j].coef, true); repeat = TRUE; break; } } } trxn_combine(); return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: tidy_gas_phase(void) /* ---------------------------------------------------------------------- */ { int n_user, last; LDBLE P, V_m; bool PR; /* * Find all gases for each gas_phase in phase list */ for (std::set::const_iterator nit = Rxn_new_gas_phase.begin(); nit != Rxn_new_gas_phase.end(); nit++) { std::map::iterator it = Rxn_gas_phase_map.find(*nit); if (it == Rxn_gas_phase_map.end()) { assert(false); } cxxGasPhase *gas_phase_ptr = &(it->second); PR = false; P = 0.0; for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++) { int k; class phase *phase_ptr = phase_bsearch(gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str(), &k, FALSE); if (phase_ptr == NULL) { input_error++; error_string = sformatf( "Gas not found in PHASES database, %s.", gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str()); error_msg(error_string, CONTINUE); continue; } else { if (phase_ptr->t_c > 0 && phase_ptr->p_c > 0) PR = true; } } gas_phase_ptr->Set_pr_in(PR); if (gas_phase_ptr->Get_new_def()) { for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++) { /* * Fixed pressure */ if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE) { if (gas_phase_ptr->Get_solution_equilibria()) { input_error++; error_string = sformatf( "Gas phase %d: cannot use '-equilibrium' option with fixed pressure gas phase.", gas_phase_ptr->Get_n_user()); error_msg(error_string, CONTINUE); } /* calculate moles */ #ifdef NPP if (!isnan(gas_phase_ptr->Get_gas_comps()[j].Get_p_read())) #else if (gas_phase_ptr->Get_gas_comps()[j].Get_p_read() != NAN) #endif { P += gas_phase_ptr->Get_gas_comps()[j].Get_p_read(); if (!PR) { double moles = gas_phase_ptr->Get_gas_comps()[j].Get_p_read() * gas_phase_ptr->Get_volume() / R_LITER_ATM / gas_phase_ptr->Get_temperature(); gas_phase_ptr->Get_gas_comps()[j].Set_moles(moles); gas_phase_ptr->Get_gas_comps()[j].Set_p(gas_phase_ptr->Get_gas_comps()[j].Get_p_read()); gas_phase_ptr->Get_gas_comps()[j].Set_phi(1.0); gas_phase_ptr->Get_gas_comps()[j].Set_f(gas_phase_ptr->Get_gas_comps()[j].Get_p_read()); } } else { input_error++; error_string = sformatf( "Gas phase %d: partial pressure of gas component %s not defined.", gas_phase_ptr->Get_n_user(), gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str()); error_msg(error_string, CONTINUE); } } else { /* * Fixed volume */ if (!gas_phase_ptr->Get_solution_equilibria()) { #ifdef NPP if (!isnan(gas_phase_ptr->Get_gas_comps()[j].Get_p_read())) #else if (gas_phase_ptr->Get_gas_comps()[j].Get_p_read() != NAN) #endif { P += gas_phase_ptr->Get_gas_comps()[j].Get_p_read(); if (!PR) { double moles = gas_phase_ptr->Get_gas_comps()[j].Get_p_read() * gas_phase_ptr->Get_volume() / R_LITER_ATM / gas_phase_ptr->Get_temperature(); gas_phase_ptr->Get_gas_comps()[j].Set_moles(moles); gas_phase_ptr->Get_gas_comps()[j].Set_p(gas_phase_ptr->Get_gas_comps()[j].Get_p_read()); gas_phase_ptr->Get_gas_comps()[j].Set_phi(1.0); gas_phase_ptr->Get_gas_comps()[j].Set_f(gas_phase_ptr->Get_gas_comps()[j].Get_p_read()); } } else { input_error++; error_string = sformatf( "Gas phase %d: moles of gas component %s not defined.", gas_phase_ptr->Get_n_user(), gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str()); error_msg(error_string, CONTINUE); } } } } if (PR && P > 0) { std::vector phase_ptrs; size_t j_PR; std::vector &gc = gas_phase_ptr->Get_gas_comps(); for (j_PR = 0; j_PR < gas_phase_ptr->Get_gas_comps().size(); j_PR++) { int k; class phase *phase_ptr = phase_bsearch(gas_phase_ptr->Get_gas_comps()[j_PR].Get_phase_name().c_str(), &k, FALSE); if (gc[j_PR].Get_p_read() == 0) { gc[j_PR].Set_moles(0.0); gc[j_PR].Set_p(0.0); gc[j_PR].Set_phi(1.0); gc[j_PR].Set_f(0.0); continue; } if (phase_ptr) { phase_ptr->moles_x = gc[j_PR].Get_p_read() / P; phase_ptrs.push_back(phase_ptr); } } V_m = calc_PR(phase_ptrs, P, gas_phase_ptr->Get_temperature(), 0); gas_phase_ptr->Set_v_m(V_m); if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_VOLUME) { gas_phase_ptr->Set_total_p(P); } for (j_PR = 0; j_PR < gas_phase_ptr->Get_gas_comps().size(); j_PR++) { int k; class phase *phase_ptr = phase_bsearch(gc[j_PR].Get_phase_name().c_str(), &k, FALSE); if (gc[j_PR].Get_p_read() == 0) { gc[j_PR].Set_moles(0.0); gc[j_PR].Set_p(0.0); gc[j_PR].Set_phi(1.0); gc[j_PR].Set_f(0.0); } else { if (phase_ptr) { gc[j_PR].Set_moles(phase_ptr->moles_x * gas_phase_ptr->Get_volume() / V_m); gc[j_PR].Set_p(gc[j_PR].Get_p_read()); gc[j_PR].Set_phi(phase_ptr->pr_phi); gc[j_PR].Set_f(gc[j_PR].Get_p_read()* phase_ptr->pr_phi); gas_phase_ptr->Set_total_moles(gas_phase_ptr->Get_total_moles() + gc[j_PR].Get_moles()); } } } } /* * Duplicate gas phase, only if not solution equilibria */ if (!gas_phase_ptr->Get_solution_equilibria()) { gas_phase_ptr->Set_new_def(false); n_user = gas_phase_ptr->Get_n_user(); last = gas_phase_ptr->Get_n_user_end(); gas_phase_ptr->Set_n_user_end(n_user); for (int j1 = n_user + 1; j1 <= last; j1++) { Utilities::Rxn_copy(Rxn_gas_phase_map, n_user, j1); } } else { gas_phase_ptr->Set_new_def(true); } } } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: tidy_inverse(void) /* ---------------------------------------------------------------------- */ { /* * After all of data are read, fill in data for an inverse structure, * including master species pointers, phase pointers, and uncertainties * and a list of all elements from phases or -balance input. */ int i, j, k, l; int count_in; LDBLE value; class master *master_ptr; class master *master_alk_ptr; const class elt_list *elt_list_ptr; master_alk_ptr = master_bsearch("Alkalinity"); for (i = 0; i < count_inverse; i++) { if (inverse[i].new_def != TRUE) continue; /* * Set default uncertainties for all solutions, if necessary */ if (inverse[i].uncertainties.size() < inverse[i].count_solns) { size_t count = inverse[i].uncertainties.size(); double value = (count > 0) ? inverse[i].uncertainties.back() : 0.05; inverse[i].uncertainties.resize(inverse[i].count_solns); for (size_t j = count; j < inverse[i].count_solns; j++) { inverse[i].uncertainties[j] = value; } } /* * Set default ph uncertainties for all solutions, if necessary */ if (inverse[i].ph_uncertainties.size() < inverse[i].count_solns) { size_t count = inverse[i].ph_uncertainties.size(); double value = (count > 0) ? inverse[i].ph_uncertainties.back() : 0.05; inverse[i].ph_uncertainties.resize(inverse[i].count_solns); for (size_t j = count; j < inverse[i].count_solns; j++) { inverse[i].ph_uncertainties[j] = value; } } /* * Set default force for all solutions */ if (inverse[i].force_solns.size() < inverse[i].count_solns) { size_t count = inverse[i].force_solns.size(); inverse[i].force_solns.resize(inverse[i].count_solns); for (size_t j = count; j < inverse[i].count_solns; j++) { inverse[i].force_solns[j] = false; } } /* * Find master species for element, set uncertainties */ for (j = 0; j < inverse[i].elts.size(); j++) { inverse[i].elts[j].master = master_bsearch_primary(inverse[i].elts[j].name); if (inverse[i].elts[j].master == NULL) { input_error++; error_string = sformatf( "No master species for element, %s.", inverse[i].elts[j].name); error_msg(error_string, CONTINUE); continue; } size_t count_uncertainties = inverse[i].elts[j].uncertainties.size(); inverse[i].elts[j].uncertainties.resize((size_t)inverse[i].count_solns); if (count_uncertainties == 0) { /* use default uncertainties for element */ for (k = 0; k < inverse[i].count_solns; k++) { inverse[i].elts[j].uncertainties[k] = inverse[i].uncertainties[k]; } } else if (count_uncertainties < inverse[i].count_solns) { /* use input uncertainties, fill in any missing at end */ value = inverse[i].elts[j].uncertainties[count_uncertainties - 1]; for (size_t k = count_uncertainties; k < inverse[i].count_solns; k++) { inverse[i].elts[j].uncertainties[k] = value; } } } /* * Find phase */ count_elts = 0; paren_count = 0; for (j = 0; j < inverse[i].phases.size(); j++) { inverse[i].phases[j].phase = phase_bsearch(inverse[i].phases[j].name, &k, FALSE); if (inverse[i].phases[j].phase == NULL) { input_error++; error_string = sformatf( "Could not find phase, %s.", inverse[i].phases[j].name); error_msg(error_string, CONTINUE); continue; } /* * Find isotope elements */ if (inverse[i].phases[j].isotopes.size() > 0) { for (k = 0; k < inverse[i].phases[j].isotopes.size(); k++) { inverse[i].phases[j].isotopes[k].primary = NULL; inverse[i].phases[j].isotopes[k].master = NULL; master_ptr = master_bsearch(inverse[i].phases[j].isotopes[k]. elt_name); if (master_ptr == NULL) { input_error++; error_string = sformatf( "Element not found for isotope calculation: %s.", inverse[i].phases[j].isotopes[k].elt_name); error_msg(error_string, CONTINUE); continue; } if (master_ptr->primary != TRUE) { input_error++; error_string = sformatf( "Isotope ratio may only be used" " for total element in phase.\n" "Secondary species not allowed: %s.", master_ptr->elt->name); error_msg(error_string, CONTINUE); continue; } inverse[i].phases[j].isotopes[k].primary = master_ptr; inverse[i].phases[j].isotopes[k].master = master_ptr; /* find coefficient for element */ for (elt_list_ptr = &inverse[i].phases[j].phase->next_elt[0]; elt_list_ptr->elt != NULL; elt_list_ptr++) { if (elt_list_ptr->elt == master_ptr->elt) { inverse[i].phases[j].isotopes[k].coef = elt_list_ptr->coef; break; } } if (elt_list_ptr == NULL) { input_error++; error_string = sformatf( "Element, %s,for which isotope ratio was defined is not found in phase, %s", master_ptr->elt->name, inverse[i].phases[j].phase->name); error_msg(error_string, CONTINUE); continue; } } qsort(&inverse[i].phases[j].isotopes[0], inverse[i].phases[j].isotopes.size(), sizeof(class isotope), isotope_compare); } add_elt_list(inverse[i].phases[j].phase->next_elt, 1.0); } if (get_input_errors() > 0) return (ERROR); /* * Sort elements in reaction and combine */ elt_list_combine(); /* * Mark master species list */ for (j = 0; j < (int)master.size(); j++) master[j]->in = FALSE; for (j = 0; j < count_elts; j++) { elt_list[j].elt->master->in = TRUE; } /* Include all input elements */ for (j = 0; j < inverse[i].elts.size(); j++) { inverse[i].elts[j].master->in = TRUE; } s_eminus->primary->in = TRUE; /* Include electrons */ if (master_alk_ptr) { master_alk_ptr->in = TRUE; /* Include alkalinity */ } else { input_error++; error_string = sformatf( "Alkalinity must be defined in SOLUTION_MASTER_SPECIES to be able to use INVERSE_MODELING."); error_msg(error_string, CONTINUE); } /* * Unmark primary and mark secondary master species for redox elements */ count_in = 0; inverse[i].count_redox_rxns = 0; for (j = 0; j < (int)master.size(); j++) { /* skip all secondary master species in this loop */ if (master[j]->primary == FALSE || master[j]->in == FALSE) continue; count_in++; if (j + 1 == (int)master.size()) continue; /* if next master species is secondary, mark all secondary master species until a primary is found */ if (master[(size_t)j + 1]->primary == FALSE) { master[j]->in = FALSE; count_in--; for (k = j + 1; k < (int)master.size(); k++) { if (master[k]->primary == FALSE) { count_in++; master[k]->in = TRUE; if (master[k]->s->primary == NULL) { inverse[i].count_redox_rxns++; } } else { break; } } } } /* * Save list of master species in inv_elts structure */ std::vector inv_elts; inv_elts.resize(count_in); count_in = 0; for (j = 0; j < (int)master.size(); j++) { /* skip H(1) and O(-2) */ if (master[j]->s == s_hplus || master[j]->s == s_h2o) continue; if (master[j]->in == TRUE) { /* set master */ inv_elts[count_in].master = master[j]; /* alloc uncertainties and set default */ inv_elts[count_in].uncertainties.resize((size_t)inverse[i].count_solns); for (k = 0; k < inverse[i].count_solns; k++) { inv_elts[count_in].uncertainties[k] = inverse[i].uncertainties[k]; } count_in++; } } if (s_co3->secondary->in == TRUE) { inverse[i].carbon = TRUE; } else { inverse[i].carbon = FALSE; } /* * copy in input uncertainties */ /* copy primary redox to all secondary redox */ for (j = 0; j < inverse[i].elts.size(); j++) { master_ptr = master_bsearch(inverse[i].elts[j].name); if (master_ptr == NULL) { input_error++; error_string = sformatf( "Element not found, %s.", inverse[i].elts[j].name); error_msg(error_string, CONTINUE); continue; } if (master_ptr->primary == FALSE || master_ptr->s->secondary == NULL) continue; for (k = 0; k < count_in; k++) { if (master_ptr == inv_elts[k].master->elt->primary) { for (l = 0; l < inverse[i].count_solns; l++) { inv_elts[k].uncertainties[l] = inverse[i].elts[j].uncertainties[l]; } } } inverse[i].elts[j].uncertainties.clear(); } /* copy masters that are not primary redox */ for (j = 0; j < inverse[i].elts.size(); j++) { master_ptr = master_bsearch(inverse[i].elts[j].name); if (master_ptr == NULL) { input_error++; error_string = sformatf( "Element not found, %s.", inverse[i].elts[j].name); error_msg(error_string, CONTINUE); continue; } if (master_ptr->primary == TRUE && master_ptr->s->secondary != NULL) continue; for (k = 0; k < count_in; k++) { if (master_ptr == inv_elts[k].master) { for (l = 0; l < inverse[i].count_solns; l++) { inv_elts[k].uncertainties[l] = inverse[i].elts[j].uncertainties[l]; } break; } } inverse[i].elts[j].uncertainties.clear(); } /* * replace elts in inverse struct */ inverse[i].elts.clear(); inverse[i].elts = inv_elts; inverse[i].elts.resize(count_in); for (j = 0; j < inverse[i].elts.size(); j++) { /* debug output_msg(sformatf( "\t%d\t%s", j, inverse[i].elts[j].master->elt->name)); for (k = 0; k < inverse[i].count_solns; k++) { output_msg(sformatf( "\t%f", inverse[i].elts[j].uncertainties[k])); } output_msg(sformatf("\n")); */ } } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: tidy_phases(void) /* ---------------------------------------------------------------------- */ { int i; int replaced; /* * Fix log Ks first, so they can possibly be added to other phase equations */ for (i = 0; i < (int)phases.size(); i++) { select_log_k_expression(phases[i]->logk, phases[i]->rxn.logk); add_other_logk(phases[i]->rxn.logk, phases[i]->add_logk); phases[i]->rxn.token[0].name = phases[i]->name; phases[i]->rxn.token[0].s = NULL; } /* * Rewrite all phases to secondary species */ for (i = 0; i < (int)phases.size(); i++) { /* * Rewrite equation */ count_trxn = 0; trxn_add_phase(phases[i]->rxn, 1.0, false); trxn.token[0].name = phases[i]->name; /* debug output_msg(sformatf( "%s PHASE.\n", phases[i]->name)); trxn_print(); */ replaced = replace_solids_gases(); phases[i]->replaced = replaced; /* save rxn_s */ trxn_reverse_k(); rewrite_eqn_to_secondary(); trxn_reverse_k(); trxn_copy(phases[i]->rxn_s); /* * Check equation */ if (phases[i]->check_equation == TRUE) { if (replaced == FALSE) { phase_rxn_to_trxn(phases[i], phases[i]->rxn); } else { phase_rxn_to_trxn(phases[i], phases[i]->rxn_s); } if (check_eqn(FALSE) == ERROR) { input_error++; error_string = sformatf( "Equation for phase %s does not balance.", phases[i]->name); error_msg(error_string, CONTINUE); } } } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: tidy_pp_assemblage(void) /* ---------------------------------------------------------------------- */ { LDBLE coef; const char* cptr; /* * Find pointers for pure phases */ //std::map::iterator it; //it = Rxn_pp_assemblage_map.begin(); //for ( ; it != Rxn_pp_assemblage_map.end(); it++) //{ //for (size_t nn = 0; nn < Rxn_new_pp_assemblage.size(); nn++) //{ //std::map::iterator kit = Rxn_pp_assemblage_map.find(Rxn_new_pp_assemblage[nn]); for (std::set::const_iterator nit = Rxn_new_pp_assemblage.begin(); nit != Rxn_new_pp_assemblage.end(); nit++) { std::map::iterator kit = Rxn_pp_assemblage_map.find(*nit); if (kit == Rxn_pp_assemblage_map.end()) { assert(false); } //if (!kit->second.Get_new_def()) continue; cxxPPassemblage *pp_assemblage_ptr = &(kit->second); count_elts = 0; paren_count = 0; coef = 1.0; pp_assemblage_ptr->Set_new_def(false); std::map::iterator it; it = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++) { int k; class phase *phase_ptr = phase_bsearch(it->first.c_str(), &k, FALSE); if (phase_ptr == NULL) { input_error++; error_string = sformatf( "Phase not found in database, %s.", it->first.c_str()); error_msg(error_string, CONTINUE); continue; } else { add_elt_list(phase_ptr->next_elt, coef); } if (it->second.Get_add_formula().size() > 0) { size_t first = count_elts; phase_ptr = phase_bsearch(it->second.Get_add_formula().c_str(), &k, FALSE); if (phase_ptr != NULL) { it->second.Set_add_formula(phase_ptr->formula); } { cptr = it->second.Get_add_formula().c_str(); get_elts_in_species(&cptr, coef); } /* check that all elements are in the database */ for (size_t l = first; l < count_elts; l++) { if (elt_list[l].elt->master == NULL) { input_error++; error_string = sformatf( "Element \"%s\" in alternative phase for \"%s\" in EQUILIBRIUM_PHASES not found in database.", elt_list[l].elt->name, it->first.c_str()); error_msg(error_string, CONTINUE); } } } } /* * Store list with all elements in phases and add formulae */ cxxNameDouble nd = elt_list_NameDouble(); pp_assemblage_ptr->Set_eltList(nd); /* * Duplicate pure phases if necessary */ int n_user = pp_assemblage_ptr->Get_n_user(); int n_user_end = pp_assemblage_ptr->Get_n_user_end(); pp_assemblage_ptr->Set_n_user_end(n_user); Utilities::Rxn_copies(Rxn_pp_assemblage_map, n_user, n_user_end); } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: tidy_ss_assemblage(void) /* ---------------------------------------------------------------------- */ { class phase *phase_ptr; LDBLE nb, nc, n_tot, xb, xc, dnb, dnc, l_a0, l_a1; LDBLE xb2, xb3, xb4, xc2, xc3; LDBLE moles; /* * Find pointers for pure phases */ //std::map::iterator it; //for (it = Rxn_ss_assemblage_map.begin(); it != Rxn_ss_assemblage_map.end(); it++) //{ //for (size_t nn = 0; nn < Rxn_new_ss_assemblage.size(); nn++) //{ //std::map::iterator it = Rxn_ss_assemblage_map.find(Rxn_new_ss_assemblage[nn]); for (std::set::const_iterator nit = Rxn_new_ss_assemblage.begin(); nit != Rxn_new_ss_assemblage.end(); nit++) { std::map::iterator it = Rxn_ss_assemblage_map.find(*nit); if (it == Rxn_ss_assemblage_map.end()) { assert(false); } //if (!it->second.Get_new_def()) continue; count_elts = 0; paren_count = 0; cxxSSassemblage *ss_assemblage_ptr = &(it->second); std::vector ss_ptrs = ss_assemblage_ptr->Vectorize(); for (size_t j = 0; j < ss_ptrs.size(); j++) { cxxSS *ss_ptr = ss_ptrs[j]; for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++) { cxxSScomp * comp_ptr = &(ss_ptr->Get_ss_comps()[k]); int k1; phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &k1, FALSE); if (phase_ptr == NULL) { input_error++; error_string = sformatf( "Phase not found in database, %s, assemblage %d.", comp_ptr->Get_name().c_str(), ss_assemblage_ptr->Get_n_user()); error_msg(error_string, CONTINUE); continue; } else { phase_ptr->moles_x = 0; phase_ptr->fraction_x = 0; } #ifdef NPP if (isnan(comp_ptr->Get_moles())) #else if (comp_ptr->Get_moles() == NAN) #endif { input_error++; error_string = sformatf( "Moles of solid solution component not defined, %s, assemblage %d.", comp_ptr->Get_name().c_str(), ss_assemblage_ptr->Get_n_user()); error_msg(error_string, CONTINUE); continue; } } if (ss_assemblage_ptr->Get_new_def()) { /* * Calculate a0 and a1 first */ ss_calc_a0_a1(ss_ptr); n_tot = 0; for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++) { cxxSScomp * comp_ptr = &(ss_ptr->Get_ss_comps()[k]); moles = comp_ptr->Get_moles(); if (moles <= 0.0) { moles = MIN_TOTAL_SS; comp_ptr->Set_initial_moles(moles); } n_tot += moles; } for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++) { cxxSScomp * comp_ptr = &(ss_ptr->Get_ss_comps()[k]); moles = comp_ptr->Get_moles(); if (moles <= 0.0) { moles = MIN_TOTAL_SS; } comp_ptr->Set_fraction_x(moles / n_tot); comp_ptr->Set_log10_fraction_x(log10(moles / n_tot)); } l_a0 = ss_ptr->Get_a0(); l_a1 = ss_ptr->Get_a1(); /* * Binary solid solution */ if (l_a0 != 0.0 || l_a1 != 0) { ss_ptr->Set_dn(1.0 / n_tot); nc = ss_ptr->Get_ss_comps()[0].Get_moles(); if (nc == 0) nc = MIN_TOTAL_SS; nb = ss_ptr->Get_ss_comps()[1].Get_moles(); if (nb == 0) nb = MIN_TOTAL_SS; xc = nc / n_tot; xb = nb / n_tot; /* lambdas */ ss_ptr->Get_ss_comps()[0].Set_log10_lambda(xb * xb * (l_a0 - l_a1 * (3 - 4 * xb)) / LOG_10); ss_ptr->Get_ss_comps()[1].Set_log10_lambda(xc * xc * (l_a0 + l_a1 * (4 * xb - 1)) / LOG_10); /* derivatives wrt nc and nb */ xc2 = xc * xc; xc3 = xc2 * xc; xb2 = xb * xb; xb3 = xb2 * xb; xb4 = xb3 * xb; /* component 1 */ dnb = -2 * l_a0 * xb * xc2 - 8 * l_a1 * xb2 * xc2 + 6 * l_a1 * xb * xc2 - 4 * l_a1 * xc * xb4 - 8 * l_a1 * xb3 * xc2 - 4 * l_a1 * xb2 * xc3 - 2 * l_a0 * xc * xb2 - 8 * l_a1 * xc * xb3 + 6 * l_a1 * xc * xb2 + 1; ss_ptr->Get_ss_comps()[0].Set_dnb(dnb / n_tot); dnc = 2 * l_a0 * xb3 + 2 * l_a0 * xc * xb2 + 8 * l_a1 * xb4 + 8 * l_a1 * xc * xb3 - 2 * l_a1 * xb3 - 6 * l_a1 * xc * xb2; ss_ptr->Get_ss_comps()[0].Set_dnc(-xb / nc + dnc / n_tot); ss_ptr->Get_ss_comps()[0].Set_dn(1.0 / n_tot); /* component 2 */ dnb = 2 * l_a0 * xb * xc2 + 2 * l_a0 * xc3 + 8 * l_a1 * xb2 * xc2 + 8 * l_a1 * xb * xc3 - 2 * l_a1 * xb * xc2 - 6 * l_a1 * xc3; ss_ptr->Get_ss_comps()[1].Set_dnb(-xc / nb + dnb / n_tot); dnc = -2 * l_a0 * xc * xb2 - 8 * l_a1 * xc * xb3 + 2 * l_a1 * xc * xb2 - 2 * l_a0 * xb * xc2 - 8 * l_a1 * xb2 * xc2 + 6 * l_a1 * xb * xc2 + 1; ss_ptr->Get_ss_comps()[1].Set_dnc(dnc / n_tot); ss_prep(ss_ptr->Get_tk(), ss_ptr, TRUE); ss_ptr->Get_ss_comps()[1].Set_dn(1.0 / n_tot); /* * Ideal solid solution */ } else { ss_ptr->Set_dn(1.0 / n_tot); for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++) { cxxSScomp * comp_ptr = &(ss_ptr->Get_ss_comps()[k]); comp_ptr->Set_log10_lambda(0); moles = comp_ptr->Get_moles(); if (moles <= 0.0) moles = MIN_TOTAL_SS; comp_ptr->Set_dnb((n_tot - moles) / (moles * n_tot)); comp_ptr->Set_dn(1.0 / n_tot); } } } } ss_assemblage_ptr->Set_new_def(false); /* * Duplicate ss_assemblage if necessary */ int n_user = ss_assemblage_ptr->Get_n_user(); int n_user_end = ss_assemblage_ptr->Get_n_user_end(); Utilities::Rxn_copies(Rxn_ss_assemblage_map, n_user, n_user_end); ss_assemblage_ptr->Set_n_user_end(n_user); } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: tidy_punch(void) /* ---------------------------------------------------------------------- */ { //int i, j, l; int punch_save; //char token[MAX_LENGTH]; /* * tidy punch information */ std::map < int, SelectedOutput >::iterator so_it = SelectedOutput_map.begin(); for ( ; so_it != SelectedOutput_map.end(); so_it++) { current_selected_output = &(so_it->second); if (current_selected_output == NULL) continue; /* totals */ for (size_t i = 0; i < current_selected_output->Get_totals().size(); i++) { std::pair< std::string, void *> &pair_ptr = current_selected_output->Get_totals()[i]; std::string noplus = pair_ptr.first; replace(noplus, "(+", "("); pair_ptr.second = master_bsearch(noplus.c_str()); } /* molalities */ for (size_t i = 0; i < current_selected_output->Get_molalities().size(); i++) { std::pair< std::string, void *> &pair_ptr = current_selected_output->Get_molalities()[i]; pair_ptr.second = s_search(pair_ptr.first.c_str()); } /* log activities */ //for (i = 0; i < punch.count_activities; i++) for (size_t i = 0; i < current_selected_output->Get_activities().size(); i++) { std::pair< std::string, void *> &pair_ptr = current_selected_output->Get_activities()[i]; pair_ptr.second = s_search(pair_ptr.first.c_str()); } /* equilibrium phases */ //for (i = 0; i < punch.count_pure_phases; i++) for (size_t i = 0; i < current_selected_output->Get_pure_phases().size(); i++) { int j; std::pair< std::string, void *> &pair_ptr = current_selected_output->Get_pure_phases()[i]; pair_ptr.second = phase_bsearch(pair_ptr.first.c_str(), &j, FALSE); } /* saturation indices */ //for (i = 0; i < punch.count_si; i++) for (size_t i = 0; i < current_selected_output->Get_si().size(); i++) { int j; std::pair< std::string, void *> &pair_ptr = current_selected_output->Get_si()[i]; pair_ptr.second = phase_bsearch(pair_ptr.first.c_str(), &j, FALSE); } /* gases */ //for (i = 0; i < punch.count_gases; i++) for (size_t i = 0; i < current_selected_output->Get_gases().size(); i++) { int j; std::pair< std::string, void *> &pair_ptr = current_selected_output->Get_gases()[i]; pair_ptr.second = phase_bsearch(pair_ptr.first.c_str(), &j, FALSE); } } /* * Always write new headings when SELECTED_OUTPUT is read */ so_it = SelectedOutput_map.begin(); for ( ; so_it != SelectedOutput_map.end(); so_it++) { current_selected_output = &(so_it->second); if (current_selected_output == NULL || !current_selected_output->Get_new_def()) continue; phrq_io->Set_punch_ostream(current_selected_output->Get_punch_ostream()); int l; if (current_selected_output->Get_high_precision() == false) { l = 12; } else { l = 20; } // UserPunch std::map < int, UserPunch >::iterator up_it = UserPunch_map.find(current_selected_output->Get_n_user()); current_user_punch = up_it == UserPunch_map.end() ? NULL : &(up_it->second); punch_save = pr.punch; pr.punch = TRUE; phrq_io->Set_punch_on(true); /* constant stuff, sim, pH, etc. */ if (current_selected_output->Get_sim() == TRUE) { fpunchf_heading(sformatf("%*s\t", l, "sim")); } if (current_selected_output->Get_state() == TRUE) { fpunchf_heading(sformatf("%*s\t", l, "state")); } if (current_selected_output->Get_soln() == TRUE) { fpunchf_heading(sformatf("%*s\t", l, "soln")); } if (current_selected_output->Get_dist() == TRUE) { fpunchf_heading(sformatf("%*s\t", l, "dist_x")); } if (current_selected_output->Get_time() == TRUE) { fpunchf_heading(sformatf("%*s\t", l, "time")); } if (current_selected_output->Get_step() == TRUE) { fpunchf_heading(sformatf("%*s\t", l, "step")); } if (current_selected_output->Get_ph() == TRUE) { fpunchf_heading(sformatf("%*s\t", l, "pH")); } if (current_selected_output->Get_pe() == TRUE) { fpunchf_heading(sformatf("%*s\t", l, "pe")); } if (current_selected_output->Get_rxn() == TRUE) { fpunchf_heading(sformatf("%*s\t", l, "reaction")); } if (current_selected_output->Get_temp() == TRUE) { fpunchf_heading(sformatf("%*s\t", l, "temp")); } if (current_selected_output->Get_alk() == TRUE) { fpunchf_heading(sformatf("%*s\t", l, "Alk")); } if (current_selected_output->Get_mu() == TRUE) { fpunchf_heading(sformatf("%*s\t", l, "mu")); } if (current_selected_output->Get_water() == TRUE) { fpunchf_heading(sformatf("%*s\t", l, "mass_H2O")); } if (current_selected_output->Get_charge_balance() == TRUE) { fpunchf_heading(sformatf("%*s\t", l, "charge")); } if (current_selected_output->Get_percent_error() == TRUE) { fpunchf_heading(sformatf("%*s\t", l, "pct_err")); } /* totals */ //for (i = 0; i < punch.count_totals; i++) for (size_t i = 0; i < current_selected_output->Get_totals().size(); i++) { std::pair< std::string, void *> &pair_ref = current_selected_output->Get_totals()[i]; fpunchf_heading(sformatf("%*s\t", l, pair_ref.first.c_str())); if (pair_ref.second == NULL) { error_string = sformatf( "Did not find master species," " %s.", pair_ref.first.c_str()); warning_msg(error_string); } //fpunchf_heading(sformatf("%*s\t", l, punch.totals[i].name)); //if (punch.totals[i].master == NULL) //{ // error_string = sformatf( "Did not find master species," // " %s.", punch.totals[i].name); // warning_msg(error_string); //} } /* molalities */ //for (i = 0; i < punch.count_molalities; i++) for (size_t i = 0; i < current_selected_output->Get_molalities().size(); i++) { std::pair< std::string, void *> &pair_ref = current_selected_output->Get_molalities()[i]; std::string name = "m_"; name.append(pair_ref.first); fpunchf_heading(sformatf("%*s\t", l, name.c_str())); if (pair_ref.second == NULL) { error_string = sformatf( "Did not find species," " %s.", pair_ref.first.c_str()); warning_msg(error_string); } //strcpy(token, "m_"); //strcat(token, punch.molalities[i].name); //fpunchf_heading(sformatf("%*s\t", l, token)); //if (punch.molalities[i].s == NULL) //{ // error_string = sformatf( "Did not find species," // " %s.", punch.molalities[i].name); // warning_msg(error_string); //} } /* log activities */ //for (i = 0; i < punch.count_activities; i++) for (size_t i = 0; i < current_selected_output->Get_activities().size(); i++) { std::pair< std::string, void *> &pair_ref = current_selected_output->Get_activities()[i]; std::string name = "la_"; name.append(pair_ref.first); fpunchf_heading(sformatf("%*s\t", l, name.c_str())); if (pair_ref.second == NULL) { error_string = sformatf( "Did not find species," " %s.", pair_ref.first.c_str()); warning_msg(error_string); } //strcpy(token, "la_"); //strcat(token, punch.activities[i].name); //fpunchf_heading(sformatf("%*s\t", l, token)); //if (punch.activities[i].s == NULL) //{ // error_string = sformatf( "Did not find species, " // "%s.", punch.activities[i].name); // warning_msg(error_string); //} } /* equilibrium phases */ //for (i = 0; i < punch.count_pure_phases; i++) for (size_t i = 0; i < current_selected_output->Get_pure_phases().size(); i++) { std::pair< std::string, void *> &pair_ref = current_selected_output->Get_pure_phases()[i]; fpunchf_heading(sformatf("%*s\t", l, pair_ref.first.c_str())); std::string name = "d_"; name.append(pair_ref.first); fpunchf_heading(sformatf("%*s\t", l, name.c_str())); if (pair_ref.second == NULL) { error_string = sformatf( "Did not find phase," " %s.", pair_ref.first.c_str()); warning_msg(error_string); } //strcpy(token, "d_"); //strcat(token, punch.pure_phases[i].name); //fpunchf_heading(sformatf("%*s\t", l, punch.pure_phases[i].name)); //fpunchf_heading(sformatf("%*s\t", l, token)); //if (punch.pure_phases[i].phase == NULL) //{ // error_string = sformatf( "Did not find phase, " // "%s.", punch.pure_phases[i].name); // warning_msg(error_string); //} } /* saturation indices */ //for (i = 0; i < punch.count_si; i++) for (size_t i = 0; i < current_selected_output->Get_si().size(); i++) { std::pair< std::string, void *> &pair_ref = current_selected_output->Get_si()[i]; std::string name = "si_"; name.append(pair_ref.first); fpunchf_heading(sformatf("%*s\t", l, name.c_str())); if (pair_ref.second == NULL) { error_string = sformatf( "Did not find phase," " %s.", pair_ref.first.c_str()); warning_msg(error_string); } //strcpy(token, "si_"); //strcat(token, punch.si[i].name); //fpunchf_heading(sformatf("%*s\t", l, token)); //if (punch.si[i].phase == NULL) //{ // error_string = sformatf( "Did not find phase, " // "%s.", punch.si[i].name); // warning_msg(error_string); //} } /* gases */ //if (punch.count_gases > 0) if (current_selected_output->Get_gases().size() > 0) { fpunchf_heading(sformatf("%*s\t", l, "pressure")); fpunchf_heading(sformatf("%*s\t", l, "total mol")); fpunchf_heading(sformatf("%*s\t", l, "volume")); } //for (i = 0; i < punch.count_gases; i++) for (size_t i = 0; i < current_selected_output->Get_gases().size(); i++) { std::pair< std::string, void *> &pair_ref = current_selected_output->Get_gases()[i]; std::string name = "g_"; name.append(pair_ref.first); fpunchf_heading(sformatf("%*s\t", l, name.c_str())); if (pair_ref.second == NULL) { error_string = sformatf( "Did not find phase," " %s.", pair_ref.first.c_str()); warning_msg(error_string); } //strcpy(token, "g_"); //strcat(token, punch.gases[i].name); //fpunchf_heading(sformatf("%*s\t", l, token)); //if (punch.gases[i].phase == NULL) //{ // error_string = sformatf( "Did not find phase, " // "%s.", punch.gases[i].name); // warning_msg(error_string); //} } /* kinetics */ //for (i = 0; i < punch.count_kinetics; i++) for (size_t i = 0; i < current_selected_output->Get_kinetics().size(); i++) { std::pair< std::string, void *> &pair_ref = current_selected_output->Get_kinetics()[i]; std::string name = "k_"; name.append(pair_ref.first); fpunchf_heading(sformatf("%*s\t", l, name.c_str())); name = "dk_"; name.append(pair_ref.first); fpunchf_heading(sformatf("%*s\t", l, name.c_str())); //strcpy(token, "k_"); //strcat(token, punch.kinetics[i].name); //fpunchf_heading(sformatf("%*s\t", l, token)); //strcpy(token, "dk_"); //strcat(token, punch.kinetics[i].name); //fpunchf_heading(sformatf("%*s\t", l, token)); } /* solid solutions */ //for (i = 0; i < punch.count_s_s; i++) for (size_t i = 0; i < current_selected_output->Get_s_s().size(); i++) { std::pair< std::string, void *> &pair_ref = current_selected_output->Get_s_s()[i]; std::string name = "s_"; name.append(pair_ref.first); fpunchf_heading(sformatf("%*s\t", l, name.c_str())); //strcpy(token, "s_"); //strcat(token, punch.s_s[i].name); //fpunchf_heading(sformatf("%*s\t", l, token)); } /* isotopes */ //for (i = 0; i < punch.count_isotopes; i++) for (size_t i = 0; i < current_selected_output->Get_isotopes().size(); i++) { std::pair< std::string, void *> &pair_ref = current_selected_output->Get_isotopes()[i]; if (isotope_ratio_search(pair_ref.first.c_str()) == NULL) { error_string = sformatf( "Did not find isotope_ratio definition for " "%s in -isotopes of SELECTED_OUTPUT.\n%s must be defined in ISOTOPE_RATIO data block.", pair_ref.first.c_str(), pair_ref.first.c_str()); warning_msg(error_string); } std::string name = "I_"; name.append(pair_ref.first); fpunchf_heading(sformatf("%*s\t", l, name.c_str())); //if (isotope_ratio_search(punch.isotopes[i].name) == NULL) //{ // error_string = sformatf( // "Did not find isotope_ratio definition for " // "%s in -isotopes of SELECTED_OUTPUT.\n%s must be defined in ISOTOPE_RATIO data block.", // punch.isotopes[i].name, punch.isotopes[i].name); // warning_msg(error_string); //} //strcpy(token, "I_"); //strcat(token, punch.isotopes[i].name); //fpunchf_heading(sformatf("%*s\t", l, token)); } /* calculate_values */ for (size_t i = 0; i < current_selected_output->Get_calculate_values().size(); i++) { std::pair< std::string, void *> &pair_ref = current_selected_output->Get_calculate_values()[i]; if (calculate_value_search(pair_ref.first.c_str()) == NULL) { error_string = sformatf( "Did not find calculate_values definition for " "%s in -calculate_values of SELECTED_OUTPUT.\n%s must be defined in CALCULATE_VALUES data block.", pair_ref.first.c_str(), pair_ref.first.c_str()); warning_msg(error_string); } std::string name = "V_"; name.append(pair_ref.first); fpunchf_heading(sformatf("%*s\t", l, name.c_str())); //if (calculate_value_search(punch.calculate_values[i].name) == NULL) //{ // error_string = sformatf( // "Did not find calculate_values definition for " // "%s in -calculate_values of SELECTED_OUTPUT.\n%s must be defined in CALCULATE_VALUES data block.", // punch.calculate_values[i].name, // punch.calculate_values[i].name); // warning_msg(error_string); //} //strcpy(token, "V_"); //strcat(token, punch.calculate_values[i].name); //fpunchf_heading(sformatf("%*s\t", l, token)); } /* user_punch */ if (current_user_punch != NULL && current_selected_output->Get_user_punch()) { for (size_t i = 0; i < current_user_punch->Get_headings().size(); i++) { fpunchf_heading(sformatf("%*s\t", l, current_user_punch->Get_headings()[i].c_str())); } } fpunchf_heading("\n"); current_selected_output->Set_new_def(false); pr.punch = punch_save; phrq_io->Set_punch_on(pr.punch == TRUE); punch_flush(); } current_selected_output = NULL; current_user_punch = NULL; phrq_io->Set_punch_ostream(NULL); return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: tidy_species(void) /* ---------------------------------------------------------------------- */ { int i, j; class master *master_ptr; char c; const char* cptr; /* * Make sure species pointers are ok */ if (check_species_input() == ERROR) { error_msg("Calculations terminating due to input errors.", STOP); } /* * Set secondary and primary pointers in species structures */ for (i = 0; i < (int)s.size(); i++) { s[i]->number = i; s[i]->primary = NULL; s[i]->secondary = NULL; if (s[i]->check_equation == TRUE) { species_rxn_to_trxn(s[i]); if (check_eqn(TRUE) == ERROR) { input_error++; error_string = sformatf( "Equation for species %s does not balance.", s[i]->name); error_msg(error_string, CONTINUE); } } } for (i = 0; i < (int)master.size(); i++) { cptr = master[i]->elt->name; if (cptr[0] != '[') { while ((c = (int) *(++cptr)) != '\0') { if (isupper((int) c)) { input_error++; error_string = sformatf( "Element or valence name in SOLUTION_MASTER_SPECIES should include only one element, %s.", master[i]->elt->name); error_msg(error_string, CONTINUE); break; } } } /* store sequence number in master structure */ master[i]->number = i; if (strcmp(master[i]->elt->name, "Alkalinity") != 0) { if (master[i]->primary == TRUE) { master[i]->s->primary = master[i]; } else { master[i]->s->secondary = master[i]; } } if (strcmp(master[i]->elt->name, "C") == 0) { s_co3 = master[i]->s; } if (master[i]->gfw_formula != NULL) { if (compute_gfw(master[i]->gfw_formula, &master[i]->gfw) == ERROR) { input_error++; error_string = sformatf( "Calculating gfw for master species, %s, formula %s.", master[i]->elt->name, master[i]->gfw_formula); error_msg(error_string, CONTINUE); } } } /* * Write equations for all master species in terms of primary * master species, set coefficient of element in master species */ for (i = 0; i < (int)master.size(); i++) { count_trxn = 0; if (master[i]->s->primary != NULL) { trxn_add(master[i]->s->rxn, 1.0, false); trxn_add(master[i]->s->rxn, -1.0, true); } else { trxn_add(master[i]->s->rxn, 1.0, false); rewrite_eqn_to_primary(); } trxn_copy(master[i]->rxn_primary); master[i]->coef = coef_in_master(master[i]); } /* * Rewrite all species to secondary species */ for (i = 0; i < (int)s.size(); i++) { count_trxn = 0; if (s[i]->primary != NULL || s[i]->secondary != NULL) { trxn_add(s[i]->rxn, 1.0, false); trxn_add(s[i]->rxn, -1.0, true); } else { trxn_add(s[i]->rxn, 1.0, false); rewrite_eqn_to_secondary(); } //rxn_free(s[i].rxn_s); //s[i].rxn_s = rxn_alloc(count_trxn + 1); trxn_copy(s[i]->rxn_s); /* calculate alkalinity */ s[i]->alk = calc_alk(s[i]->rxn_s); /* set co2 coefficient */ s[i]->co2 = 0.0; for (j = 1; j < count_trxn; j++) { if (trxn.token[j].s == s_co3) { s[i]->co2 = trxn.token[j].coef; break; } } } /* * Set pointer in element to master species */ for (i = 0; i < (int)elements.size(); i++) { elements[i]->master = master_bsearch(elements[i]->name); if (elements[i]->master == NULL) { input_error++; error_string = sformatf( "No master species for element %s.", elements[i]->name); error_msg(error_string, CONTINUE); } elements[i]->primary = master_bsearch_primary(elements[i]->name); if (elements[i]->primary == NULL) { input_error++; error_string = sformatf( "No master species for element %s.", elements[i]->name); error_msg(error_string, CONTINUE); } } /* * Make sure all primary master species for redox elements * are also secondary master species */ for (i = 0; i < (int)master.size(); i++) { if (master[i]->primary == FALSE) { master_ptr = master[i]->s->secondary->elt->primary; if (master_ptr == NULL) { input_error++; error_string = sformatf( "Every primary master species for a redox element\n" "\tmust also be a secondary master species.\n" "\tError in definitions related to %s .\n", master[i]->s->name); error_msg(error_string, CONTINUE); } else if (master_ptr->s->secondary == NULL) { input_error++; error_string = sformatf( "Every primary master species for a redox element\n" "\tmust also be a secondary master species.\n" "\t%s is the primary master species for element %s.\n" "\tAnother entry in SOLUTION_MASTER_SPECIES is needed.\n" "\tDefine species %s as a secondary master species for a valence state.\n" "\tFor example: \n" "\t%s(0)\t%s alk gfw", master_ptr->s->name, master_ptr->elt->name, master_ptr->s->name, master_ptr->elt->name, master_ptr->s->name); error_msg(error_string, CONTINUE); } } } /* * Calculate H and O if alternate mass balance is given */ for (i = 0; i < (int)s.size(); i++) { if (s[i]->next_secondary.size() != 0) { s[i]->h = 0.0; s[i]->o = 0.0; for (j = 0; s[i]->next_secondary[j].elt != NULL; j++) { if (s[i]->next_secondary[j].elt->primary == NULL) continue; if (s[i]->next_secondary[j].elt->primary->s == s_hplus || s[i]->next_secondary[j].elt->primary->s == s_h3oplus) { s[i]->h += s[i]->next_secondary[j].coef; } else if (s[i]->next_secondary[j].elt->primary->s == s_h2o) { s[i]->o += s[i]->next_secondary[j].coef; } else if (s[i]->mole_balance != NULL) { master_ptr = s[i]->next_secondary[j].elt->master; if (master_ptr != NULL) { if (master_ptr->primary == TRUE) { if (master_ptr->s->secondary != NULL) { master_ptr = master_ptr->s->secondary; } } } else { input_error++; error_string = sformatf( "Element in -mole_balance %s not defined for species %s.\n", s[i]->mole_balance, s[i]->name); error_msg(error_string, CONTINUE); continue; } if (master_ptr->coef != 1) { s[i]->next_secondary[j].coef /= master_ptr->coef; } } } if (s[i]->type == EX) { for (j = 0; s[i]->next_secondary[j].elt != NULL; j++) { if (s[i]->next_secondary[j].elt->primary->s->type == EX) { s[i]->equiv = s[i]->next_secondary[j].coef; break; } } } } if (s[i]->type == EX) { /* * Find valence of cation from coefficients of reaction components * Changed to be coefficient of exchanger */ LDBLE exchange_coef = 0.0; for (j = 1; s[i]->rxn_s.token[j].s != NULL; j++) { if (s[i]->rxn_s.token[j].s->type == EX) { exchange_coef = s[i]->rxn_s.token[j].coef; break; } } if (exchange_coef == 0.0) { input_error++; error_string = sformatf( "No exchange species found in equation for %s.\n", s[i]->name); error_msg(error_string, CONTINUE); continue; } s[i]->equiv = exchange_coef; } if (s[i]->type == SURF) { LDBLE surface_coef = 0.0; /* * Find coefficient of surface in rxn, store in equiv */ for (j = 1; s[i]->rxn_s.token[j].s != NULL; j++) { if (s[i]->rxn_s.token[j].s->type == SURF) { surface_coef = s[i]->rxn_s.token[j].coef; break; } } if (surface_coef == 0.0) { input_error++; error_string = sformatf( "No surface species found in equation for %s.\n", s[i]->name); error_msg(error_string, CONTINUE); continue; } s[i]->equiv = surface_coef; } } for (i = 0; i < (int)master.size(); i++) { if (master[i]->gfw <= 0.0) { if (master[i]->type >= EMINUS) continue; if ((strcmp(master[i]->elt->name, "E") != 0) && (strcmp(master[i]->elt->name, "e") != 0) && (strcmp(master[i]->elt->name, "H(1)") != 0) && (strcmp(master[i]->elt->name, "O(-2)") != 0) ) { input_error++; error_string = sformatf( "Gram formula wt in SOLUTION_MASTER_SPECIES should not be <= 0.0, %s.\n", master[i]->elt->name); error_msg(error_string, CONTINUE); } } } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: tidy_surface(void) /* ---------------------------------------------------------------------- */ { /* * After all of data are read, fill in master species for surface comps * Sort surface */ const char* cptr1; cxxSurface *surface_ptr; //std::map::iterator kit; //for (kit = Rxn_surface_map.begin(); kit != Rxn_surface_map.end(); kit++) //{ //for (size_t nn = 0; nn < Rxn_new_surface.size(); nn++) //{ //std::map::iterator kit = Rxn_surface_map.find(Rxn_new_surface[nn]); for (std::set::const_iterator nit = Rxn_new_surface.begin(); nit != Rxn_new_surface.end(); nit++) { std::map::iterator kit = Rxn_surface_map.find(*nit); if (kit == Rxn_surface_map.end()) { assert(false); } //if (!kit->second.Get_new_def()) continue; surface_ptr = &(kit->second); if (surface_ptr->Get_tidied()) continue; surface_ptr->Set_tidied(true); // ccm incompatible with Donnan or diffuse_layer if (surface_ptr->Get_type() == cxxSurface::CCM) { if (surface_ptr->Get_dl_type() == cxxSurface::BORKOVEK_DL || surface_ptr->Get_dl_type() == cxxSurface::DONNAN_DL) { input_error++; error_string = "Cannot use -diffuse_layer or -donnan calculation with Constant Capacity Model."; error_msg(error_string, CONTINUE); continue; } } for (size_t i = 0; i < surface_ptr->Get_surface_comps().size(); i++) { cxxSurfaceComp *comp_ptr = &(surface_ptr->Get_surface_comps()[i]); /* * Find master species for each surface */ cxxNameDouble::iterator jit = comp_ptr->Get_totals().begin(); for ( ; jit != comp_ptr->Get_totals().end(); jit++ ) { class element *elt_ptr = element_store(jit->first.c_str()); class master *master_ptr = elt_ptr->master; if (master_ptr == NULL) { input_error++; error_string = sformatf( "Master species not in database for %s, " "skipping element.", elt_ptr->name); error_msg(error_string, CONTINUE); continue; } if (master_ptr->type != SURF) continue; comp_ptr->Set_master_element(elt_ptr->name); /* * Set flags */ cxxSurfaceCharge *charge_ptr = surface_ptr->Find_charge(comp_ptr->Get_charge_name()); /* * Calculate moles of sites */ if (surface_ptr->Get_new_def() && surface_ptr->Get_sites_units() == cxxSurface::SITES_DENSITY && comp_ptr->Get_phase_name().size() == 0) { if (charge_ptr == NULL) { input_error++; error_string = sformatf( "Surface type is incompatible with site units for %s.", comp_ptr->Get_formula().c_str()); error_msg(error_string, CONTINUE); continue; } comp_ptr->Set_moles( comp_ptr->Get_moles() * 1.0e18 * charge_ptr->Get_specific_area() * charge_ptr->Get_grams() / AVOGADRO); /* * Calculate totals */ count_elts = 0; paren_count = 0; { cptr1 = comp_ptr->Get_formula().c_str(); get_elts_in_species(&cptr1, comp_ptr->Get_moles()); } { cxxNameDouble nd = elt_list_NameDouble(); comp_ptr->Set_totals(nd); } } if (surface_ptr->Get_type() == cxxSurface::CD_MUSIC) { charge_ptr->Set_charge_balance(charge_ptr->Get_charge_balance() + comp_ptr->Get_moles() * comp_ptr->Get_formula_z()); } break; } } /* * Check that all surface comps have a corresponding master */ for (size_t i = 0; i < surface_ptr->Get_surface_comps().size(); i++) { if (surface_ptr->Get_surface_comps()[i].Get_master_element().size() == 0) { input_error++; error_string = sformatf( "No surface master species for surface component %s, ", surface_ptr->Get_surface_comps()[i].Get_formula().c_str()); error_msg(error_string, CONTINUE); } } /* * Sort components */ std::map comp_map; for (size_t i = 0; i < surface_ptr->Get_surface_comps().size(); i++) { cxxSurfaceComp *comp_ptr = &(surface_ptr->Get_surface_comps()[i]); comp_map[comp_ptr->Get_formula()] = *comp_ptr; } std::map::iterator it = comp_map.begin(); surface_ptr->Get_surface_comps().clear(); for ( ; it != comp_map.end(); it++) { surface_ptr->Get_surface_comps().push_back(it->second); } } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: tidy_solutions(void) /* ---------------------------------------------------------------------- */ { /* * Define n_user for any solutions read by solution_spread that * don`t have n_user defined */ class master *master_ptr; /* * Calculate solution numbers */ if (unnumbered_solutions.size() > 0) { int last = 0; std::map::iterator jit; for (jit = Rxn_solution_map.begin(); jit != Rxn_solution_map.end(); jit++) { if (jit->second.Get_n_user() > last) last = jit->second.Get_n_user(); if (jit->second.Get_n_user_end() > last) last = jit->second.Get_n_user_end(); } if (save.solution == TRUE) { if (save.n_solution_user > last) last = save.n_solution_user; if (save.n_solution_user_end > last) last = save.n_solution_user_end; } // put unnumbered solutions in map for (size_t i = 0; i < unnumbered_solutions.size(); i++) { if (use.Get_n_solution_user() < 0) { use.Set_n_solution_user(last + 1); } unnumbered_solutions[i].Set_n_user_both(++last); Rxn_solution_map[last] = unnumbered_solutions[i]; Rxn_new_solution.insert(last); } unnumbered_solutions.clear(); } /* * Check that elements are in database */ //std::map::iterator it; //for (it = Rxn_solution_map.begin(); it != Rxn_solution_map.end(); it++) //for(size_t n = 0; n < Rxn_new_solution.size(); n++) //{ //std::map::iterator it = Rxn_solution_map.find(Rxn_new_solution[n]); for (std::set::const_iterator nit = Rxn_new_solution.begin(); nit != Rxn_new_solution.end(); nit++) { std::map::iterator it = Rxn_solution_map.find(*nit); if (it == Rxn_solution_map.end()) { assert(false); continue; } cxxSolution &solution_ref = it->second; //if (solution_ref.Get_new_def()) { cxxISolution *initial_data_ptr = solution_ref.Get_initial_data(); if (initial_data_ptr != NULL) { std::map::iterator iit = initial_data_ptr->Get_comps().begin(); for ( ; iit != initial_data_ptr->Get_comps().end(); iit++) { cxxISolutionComp &comp_ref = iit->second; if (strcmp(comp_ref.Get_description().c_str(), "H(1)") == 0 || strcmp(comp_ref.Get_description().c_str(), "E") == 0) { comp_ref.Set_moles(0.0); continue; } std::string token; std::string description = comp_ref.Get_description(); std::string::iterator b = description.begin(); std::string::iterator e = description.end(); CParser::copy_token(token, b, e); master_ptr = master_bsearch(token.c_str()); if (master_ptr == NULL) { error_string = sformatf( "Could not find element in database, %s.\n\tConcentration is set to zero.", comp_ref.Get_description().c_str()); warning_msg(error_string); comp_ref.Set_input_conc(0.0); continue; } } } } } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: species_rxn_to_trxn(class species *s_ptr) /* ---------------------------------------------------------------------- */ { /* * Copy reaction from reaction structure to * temp reaction structure. */ if (trxn.token.size() <= s_ptr->rxn.token.size()) { trxn.token.resize(s_ptr->rxn.token.size()); } count_trxn = 0; for (size_t i = 0; s_ptr->rxn.token[i].s != NULL; i++) { trxn.token[i].name = s_ptr->rxn.token[i].s->name; trxn.token[i].z = s_ptr->rxn.token[i].s->z; trxn.token[i].s = s_ptr->rxn.token[i].s; trxn.token[i].unknown = NULL; trxn.token[i].coef = s_ptr->rxn.token[i].coef; count_trxn = i + 1; if (count_trxn + 1 > trxn.token.size()) trxn.token.resize(count_trxn + 1); } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: tidy_isotopes(void) /* ---------------------------------------------------------------------- */ { /* * Isotope ratios for each element or element valence state */ LDBLE isotope_number; class master *master_ptr, *primary_ptr; size_t primary_number = 0; primary_ptr = NULL; std::map::iterator it; for (it = Rxn_solution_map.begin(); it != Rxn_solution_map.end(); it++) { std::map new_isotopes; cxxSolution &solution_ref = it->second; if (!solution_ref.Get_new_def()) continue; if (solution_ref.Get_isotopes().size() == 0) continue; std::map primary_isotopes; /* * Make list of primary master species for isotopes */ std::map < std::string, cxxSolutionIsotope >::iterator kit = solution_ref.Get_isotopes().begin(); for ( ; kit != solution_ref.Get_isotopes().end(); kit++) { cxxSolutionIsotope &isotope_ref = kit->second; master_ptr = master_bsearch_primary(isotope_ref.Get_elt_name().c_str()); isotope_number = isotope_ref.Get_isotope_number(); if (master_ptr == NULL) { input_error++; error_string = sformatf( "In isotope calculation: element not defined: %s.", isotope_ref.Get_elt_name().c_str()); error_msg(error_string, CONTINUE); continue; } std::ostringstream iso_name_str; iso_name_str << (int) isotope_number << master_ptr->elt->name; std::map < std::string, cxxSolutionIsotope >::iterator jit; jit = primary_isotopes.find(iso_name_str.str().c_str()); if (jit == primary_isotopes.end()) { cxxSolutionIsotope temp_isotope; temp_isotope.Set_isotope_name(iso_name_str.str().c_str()); temp_isotope.Set_elt_name(master_ptr->elt->name); temp_isotope.Set_isotope_number(isotope_number); primary_isotopes[iso_name_str.str().c_str()] = temp_isotope; } } if (get_input_errors() > 0) return (ERROR); /* * Go through all redox states of the list of primary species and isotope number */ for (kit = primary_isotopes.begin(); kit != primary_isotopes.end(); kit++) { /* find index number of master species, set flag to FALSE */ master_ptr = master_bsearch(kit->second.Get_elt_name().c_str()); isotope_number = kit->second.Get_isotope_number(); for (int k = 0; k < (int)master.size(); k++) { master[k]->isotope = FALSE; } primary_number = master_ptr->number; primary_ptr = master_ptr; /* go through isotopes of solution and fill in master species */ std::map < std::string, cxxSolutionIsotope >::iterator lit = solution_ref.Get_isotopes().begin(); for ( ; lit != solution_ref.Get_isotopes().end(); lit++) { master_ptr = master_bsearch(lit->second.Get_elt_name().c_str()); if (master_ptr == NULL) { input_error++; error_string = sformatf( "In isotope calculation: element not defined: %s.", lit->second.Get_elt_name().c_str()); error_msg(error_string, CONTINUE); continue; } /* only fill for pertinent isotope */ if (master_ptr->elt->primary != primary_ptr) continue; if (lit->second.Get_isotope_number() != isotope_number) continue; /* for primary, fill in ratio for all secondary species */ if (master_ptr->primary == TRUE && master_ptr->s->secondary != NULL) { for (size_t k = primary_number + 1; k < (int)master.size(); k++) { if (master[k]->elt->primary != primary_ptr) break; master[k]->isotope_ratio = lit->second.Get_ratio(); master[k]->isotope_ratio_uncertainty = lit->second.Get_ratio_uncertainty(); if (master[k]->isotope == TRUE) { error_string = sformatf( "In isotope calculation: redefinition of isotope ratio for %s.", lit->second.Get_elt_name().c_str()); error_msg(error_string, CONTINUE); } master[k]->isotope = TRUE; } } /* for secondary and non redox, set ratio */ else { master_ptr->isotope_ratio = lit->second.Get_ratio(); master_ptr->isotope_ratio_uncertainty = lit->second.Get_ratio_uncertainty(); if (master_ptr->isotope == TRUE) { error_string = sformatf( "In isotope calculation: redefinition of isotope ratio for %s.", lit->second.Get_elt_name().c_str()); error_msg(error_string, CONTINUE); } master_ptr->isotope = TRUE; } } /* * Write new isotope structure */ for (int k = 0; k < (int)master.size(); k++) { /* skip primary master species of redox elements */ if (master[k]->primary == TRUE && master[k]->s->secondary != NULL) continue; if (master[k]->elt->primary == primary_ptr && master[k]->isotope == FALSE) { input_error++; error_string = sformatf( "Isotopic ratio not defined for element or valence state %g%s, using 0.", (double) isotope_number, master[k]->elt->name); warning_msg(error_string); master[k]->isotope = TRUE; master[k]->isotope_ratio = 0.0; master[k]->isotope_ratio_uncertainty = 0.001; } if (master[k]->isotope == FALSE) continue; cxxSolutionIsotope temp_iso; temp_iso.Set_isotope_number(isotope_number); temp_iso.Set_elt_name(master[k]->elt->name); temp_iso.Set_total(0); temp_iso.Set_ratio(master[k]->isotope_ratio); temp_iso.Set_ratio_uncertainty(master[k]->isotope_ratio_uncertainty); #ifdef NPP if (!isnan(master[k]->isotope_ratio_uncertainty)) #else if (master[k]->isotope_ratio_uncertainty != NAN) #endif { temp_iso.Set_ratio_uncertainty_defined(true); } std::string token = sformatf("%d%s", (int) isotope_number, master[k]->elt->name); temp_iso.Set_isotope_name(token.c_str()); new_isotopes[token] = temp_iso; } } solution_ref.Set_isotopes(new_isotopes); } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: tidy_kin_exchange(void) /* ---------------------------------------------------------------------- */ /* * If exchanger is related to mineral, exchanger amount is * set in proportion */ { cxxKinetics *kinetics_ptr; const char* cptr; LDBLE conc; //std::map::iterator it = Rxn_exchange_map.begin(); //for ( ; it != Rxn_exchange_map.end(); it++) //for (size_t nn = 0; nn < Rxn_new_exchange.size(); nn++) //{ //std::map::iterator it = Rxn_exchange_map.find(Rxn_new_exchange[nn]); for (std::set::const_iterator nit = Rxn_new_exchange.begin(); nit != Rxn_new_exchange.end(); nit++) { std::map::iterator it = Rxn_exchange_map.find(*nit); if (it == Rxn_exchange_map.end()) { assert(false); } cxxExchange * exchange_ptr = &(it->second); if (!exchange_ptr->Get_new_def()) continue; if (exchange_ptr->Get_n_user() < 0) continue; // check elements for (size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++) { cxxExchComp & comp_ref = exchange_ptr->Get_exchange_comps()[j]; if (comp_ref.Get_rate_name().size() == 0) continue; /* First find exchange master species */ cxxNameDouble nd = comp_ref.Get_totals(); cxxNameDouble::iterator kit = nd.begin(); bool found_exchange = false; for (; kit != nd.end(); kit++) { /* Find master species */ class element *elt_ptr = element_store(kit->first.c_str()); if (elt_ptr == NULL || elt_ptr->master == NULL) { input_error++; error_string = sformatf( "Master species not in database " "for %s, skipping element.", kit->first.c_str()); error_msg(error_string, CONTINUE); continue; } if (elt_ptr->master->type == EX) found_exchange = true;; } if (!found_exchange) { input_error++; error_string = sformatf( "Exchange formula does not contain an exchange master species, %s", comp_ref.Get_formula().c_str()); error_msg(error_string, CONTINUE); continue; } /* Now find associated kinetic reaction ... */ if ((kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, exchange_ptr->Get_n_user())) == NULL) { input_error++; error_string = sformatf( "Kinetics %d must be defined to use exchange related to kinetic reaction, %s", exchange_ptr->Get_n_user(), comp_ref.Get_formula().c_str()); error_msg(error_string, CONTINUE); continue; } size_t k; for (k = 0; k < kinetics_ptr->Get_kinetics_comps().size(); k++) { if (strcmp_nocase (comp_ref.Get_rate_name().c_str(), kinetics_ptr->Get_kinetics_comps()[k].Get_rate_name().c_str()) == 0) { break; } } if (k == kinetics_ptr->Get_kinetics_comps().size()) { input_error++; error_string = sformatf( "Kinetic reaction, %s, related to exchanger, %s, not found in KINETICS %d", comp_ref.Get_rate_name().c_str(), comp_ref.Get_formula().c_str(), exchange_ptr->Get_n_user()); error_msg(error_string, CONTINUE); continue; } /* use database name for phase */ comp_ref.Set_rate_name(kinetics_ptr->Get_kinetics_comps()[k].Get_rate_name().c_str()); /* make exchanger concentration proportional to mineral ... */ conc = kinetics_ptr->Get_kinetics_comps()[k].Get_m() * comp_ref.Get_phase_proportion(); count_elts = 0; paren_count = 0; { cptr = comp_ref.Get_formula().c_str(); get_elts_in_species(&cptr, conc); } comp_ref.Set_totals(elt_list_NameDouble()); /* * No check on availability of exchange elements */ } } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: update_kin_exchange(void) /* ---------------------------------------------------------------------- */ /* * If exchanger is related to mineral, exchanger amount is * set in proportion. Exchange needs to be updated if the * amount of kinetic reaction has changed. Corner case of * zero moles. */ { cxxKinetics* kinetics_ptr; const char* cptr; LDBLE conc; std::map::iterator it = Rxn_exchange_map.begin(); for ( ; it != Rxn_exchange_map.end(); it++) { cxxExchange* exchange_ptr = &(it->second); if (exchange_ptr->Get_n_user() < 0) continue; // check elements for (size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++) { cxxExchComp& comp_ref = exchange_ptr->Get_exchange_comps()[j]; if (comp_ref.Get_rate_name().size() == 0) continue; double comp_moles = 0.0; /* First find exchange master species */ cxxNameDouble nd = comp_ref.Get_totals(); cxxNameDouble::iterator kit = nd.begin(); bool found_exchange = false; for (; kit != nd.end(); kit++) { /* Find master species */ class element* elt_ptr = element_store(kit->first.c_str()); if (elt_ptr == NULL || elt_ptr->master == NULL) { input_error++; error_string = sformatf("Master species not in database " "for %s, skipping element.", kit->first.c_str()); error_msg(error_string, CONTINUE); continue; } if (elt_ptr->master->type == EX) { comp_moles = kit->second; found_exchange = true; } } //if (!found_exchange) //{ // input_error++; // error_string = sformatf( // "Exchange formula does not contain an exchange master species, %s", // comp_ref.Get_formula().c_str()); // error_msg(error_string, CONTINUE); // continue; //} /* Now find associated kinetic reaction ... */ if ((kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, exchange_ptr->Get_n_user())) == NULL) { input_error++; error_string = sformatf( "Kinetics %d must be defined to use exchange related to kinetic reaction, %s", exchange_ptr->Get_n_user(), comp_ref.Get_formula().c_str()); error_msg(error_string, CONTINUE); continue; } size_t k; for (k = 0; k < kinetics_ptr->Get_kinetics_comps().size(); k++) { if (strcmp_nocase (comp_ref.Get_rate_name().c_str(), kinetics_ptr->Get_kinetics_comps()[k].Get_rate_name().c_str()) == 0) { break; } } if (k == kinetics_ptr->Get_kinetics_comps().size()) { input_error++; error_string = sformatf( "Kinetic reaction, %s, related to exchanger, %s, not found in KINETICS %d", comp_ref.Get_rate_name().c_str(), comp_ref.Get_formula().c_str(), exchange_ptr->Get_n_user()); error_msg(error_string, CONTINUE); continue; } /* use database name for phase */ comp_ref.Set_rate_name(kinetics_ptr->Get_kinetics_comps()[k].Get_rate_name().c_str()); /* make exchanger concentration proportional to mineral ... */ conc = kinetics_ptr->Get_kinetics_comps()[k].Get_m() * comp_ref.Get_phase_proportion(); if (found_exchange && comp_moles > 0.0) { /* parse formula */ count_elts = 0; paren_count = 0; { cptr = comp_ref.Get_formula().c_str(); get_elts_in_species(&cptr, 1.0); } cxxNameDouble nd_formula = elt_list_NameDouble(); double comp_coef = 0; for (kit = nd_formula.begin(); kit != nd_formula.end(); kit++) { /* Find master species */ class element* elt_ptr = element_store(kit->first.c_str()); if (elt_ptr->master->type == EX) { comp_coef = kit->second; } } comp_ref.multiply(comp_coef * conc / comp_moles); } else /* need to generate totals from scratch */ { count_elts = 0; paren_count = 0; { cptr = comp_ref.Get_formula().c_str(); get_elts_in_species(&cptr, conc); } comp_ref.Set_totals(elt_list_NameDouble()); } } } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: tidy_min_exchange(void) /* ---------------------------------------------------------------------- */ /* * If exchanger is related to mineral, exchanger amount is * set in proportion */ { int n, jj; const char* cptr; LDBLE conc; //std::map::iterator it = Rxn_exchange_map.begin(); //for ( ; it != Rxn_exchange_map.end(); it++) //{ //for (size_t nn = 0; nn < Rxn_new_exchange.size(); nn++) //{ //std::map::iterator it = Rxn_exchange_map.find(Rxn_new_exchange[nn]); for (std::set::const_iterator nit = Rxn_new_exchange.begin(); nit != Rxn_new_exchange.end(); nit++) { std::map::iterator it = Rxn_exchange_map.find(*nit); if (it == Rxn_exchange_map.end()) { assert(false); } cxxExchange * exchange_ptr = &(it->second); if (!exchange_ptr->Get_new_def()) continue; if (exchange_ptr->Get_n_user() < 0) continue; n = exchange_ptr->Get_n_user(); // check elements for (size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++) { cxxExchComp & comp_ref = exchange_ptr->Get_exchange_comps()[j]; if (comp_ref.Get_phase_name().size() == 0) continue; /* First find exchange master species */ cxxNameDouble nd = comp_ref.Get_totals(); cxxNameDouble::iterator kit = nd.begin(); bool found_exchange = false; for (; kit != nd.end(); kit++) { /* Find master species */ class element *elt_ptr = element_store(kit->first.c_str()); if (elt_ptr == NULL || elt_ptr->master == NULL) { input_error++; error_string = sformatf( "Master species not in database " "for %s, skipping element.", kit->first.c_str()); error_msg(error_string, CONTINUE); continue; } if (elt_ptr->master->type == EX) { found_exchange = true;; } } if (!found_exchange) { input_error++; error_string = sformatf( "Exchange formula does not contain an exchange master species, %s", comp_ref.Get_formula().c_str()); error_msg(error_string, CONTINUE); continue; } cxxPPassemblage *pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, n); /* Now find the mineral on which exchanger depends... */ if (pp_assemblage_ptr == NULL) { input_error++; error_string = sformatf( "Equilibrium_phases %d must be defined to use exchange related to mineral phase, %s", n, comp_ref.Get_formula().c_str()); error_msg(error_string, CONTINUE); continue; } std::map::iterator jit; jit = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); for ( ; jit != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); jit++) { if (strcmp_nocase(comp_ref.Get_phase_name().c_str(), jit->first.c_str()) == 0) { break; } } if (jit == pp_assemblage_ptr->Get_pp_assemblage_comps().end() ) { input_error++; error_string = sformatf( "Mineral, %s, related to exchanger, %s, not found in Equilibrium_Phases %d", comp_ref.Get_phase_name().c_str(), comp_ref.Get_formula().c_str(), n); error_msg(error_string, CONTINUE); continue; } /* use database name for phase */ comp_ref.Set_phase_name(jit->first.c_str()); /* make exchanger concentration proportional to mineral ... */ conc = jit->second.Get_moles() * comp_ref.Get_phase_proportion(); count_elts = 0; paren_count = 0; { cptr = comp_ref.Get_formula().c_str(); get_elts_in_species(&cptr, conc); } comp_ref.Set_totals(elt_list_NameDouble()); /* * make sure exchange elements are in phase */ count_elts = 0; paren_count = 0; { cptr = comp_ref.Get_formula().c_str(); get_elts_in_species(&cptr, -comp_ref.Get_phase_proportion()); } int l; class phase *phase_ptr = phase_bsearch(jit->first.c_str(), &l, FALSE); if (phase_ptr != NULL) { cptr = phase_ptr->formula; get_elts_in_species(&cptr, 1.0); } else { input_error++; error_string = sformatf( "Mineral, %s, related to exchanger, %s, not found in Equilibrium_Phases %d", comp_ref.Get_phase_name().c_str(), comp_ref.Get_formula().c_str(), n); error_msg(error_string, CONTINUE); continue; } elt_list_combine(); for (jj = 0; jj < count_elts; jj++) { if (elt_list[jj].elt->primary->s->type != EX && elt_list[jj].coef < 0) { input_error++; error_string = sformatf( "Stoichiometry of exchanger, %s * %g mol sites/mol phase,\n\tmust be a subset of the related phase %s, %s.", comp_ref.Get_formula().c_str(), (double) comp_ref.Get_phase_proportion(), phase_ptr->name, phase_ptr->formula); error_msg(error_string, CONTINUE); break; } } } } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: update_min_exchange(void) /* ---------------------------------------------------------------------- */ /* * If exchanger is related to mineral, exchanger amount is * set in proportion. Need to check in case exchange or min * are modified. */ { int n, jj; const char* cptr; LDBLE conc; std::map::iterator it = Rxn_exchange_map.begin(); for ( ; it != Rxn_exchange_map.end(); it++) { cxxExchange* exchange_ptr = &(it->second); if (exchange_ptr->Get_n_user() < 0) continue; n = exchange_ptr->Get_n_user(); // check elements for (size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++) { double comp_moles = 0.0; cxxExchComp& comp_ref = exchange_ptr->Get_exchange_comps()[j]; if (comp_ref.Get_phase_name().size() == 0) continue; /* First find exchange master species */ cxxNameDouble nd = comp_ref.Get_totals(); cxxNameDouble::iterator kit = nd.begin(); bool found_exchange = false; for (; kit != nd.end(); kit++) { /* Find master species */ class element* elt_ptr = element_store(kit->first.c_str()); if (elt_ptr == NULL || elt_ptr->master == NULL) { input_error++; error_string = sformatf("Master species not in database " "for %s, skipping element.", kit->first.c_str()); error_msg(error_string, CONTINUE); continue; } if (elt_ptr->master->type == EX) { comp_moles = kit->second; found_exchange = true; } } //if (!found_exchange) //{ // input_error++; // error_string = sformatf( // "Exchange formula does not contain an exchange master species, %s", // comp_ref.Get_formula().c_str()); // error_msg(error_string, CONTINUE); // continue; //} cxxPPassemblage* pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, n); /* Now find the mineral on which exchanger depends... */ if (pp_assemblage_ptr == NULL) { input_error++; error_string = sformatf( "Equilibrium_phases %d must be defined to use exchange related to mineral phase, %s", n, comp_ref.Get_formula().c_str()); error_msg(error_string, CONTINUE); continue; } std::map::iterator jit; jit = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); for (; jit != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); jit++) { if (strcmp_nocase(comp_ref.Get_phase_name().c_str(), jit->first.c_str()) == 0) { break; } } if (jit == pp_assemblage_ptr->Get_pp_assemblage_comps().end()) { input_error++; error_string = sformatf( "Mineral, %s, related to exchanger, %s, not found in Equilibrium_Phases %d", comp_ref.Get_phase_name().c_str(), comp_ref.Get_formula().c_str(), n); error_msg(error_string, CONTINUE); continue; } /* use database name for phase */ comp_ref.Set_phase_name(jit->first.c_str()); /* make exchanger concentration proportional to mineral ... */ conc = jit->second.Get_moles() * comp_ref.Get_phase_proportion(); if (found_exchange && comp_moles > 0.0) { /* parse formula */ count_elts = 0; paren_count = 0; { cptr = comp_ref.Get_formula().c_str(); get_elts_in_species(&cptr, 1.0); } cxxNameDouble nd_formula = elt_list_NameDouble(); double comp_coef = 0; for (kit = nd_formula.begin(); kit != nd_formula.end(); kit++) { /* Find master species */ class element* elt_ptr = element_store(kit->first.c_str()); if (elt_ptr->master->type == EX) { comp_coef = kit->second; } } comp_ref.multiply(comp_coef * conc / comp_moles); } else /* comp_moles is zero, need to redefine totals from scratch */ { count_elts = 0; paren_count = 0; { cptr = comp_ref.Get_formula().c_str(); get_elts_in_species(&cptr, conc); } comp_ref.Set_totals(elt_list_NameDouble()); /* * make sure exchange elements are in phase */ count_elts = 0; paren_count = 0; { cptr = comp_ref.Get_formula().c_str(); get_elts_in_species(&cptr, -comp_ref.Get_phase_proportion()); } int l; class phase* phase_ptr = phase_bsearch(jit->first.c_str(), &l, FALSE); if (phase_ptr != NULL) { cptr = phase_ptr->formula; get_elts_in_species(&cptr, 1.0); } else { input_error++; error_string = sformatf( "Mineral, %s, related to exchanger, %s, not found in Equilibrium_Phases %d", comp_ref.Get_phase_name().c_str(), comp_ref.Get_formula().c_str(), n); error_msg(error_string, CONTINUE); continue; } elt_list_combine(); for (jj = 0; jj < count_elts; jj++) { if (elt_list[jj].elt->primary->s->type != EX && elt_list[jj].coef < 0) { input_error++; error_string = sformatf( "Stoichiometry of exchanger, %s * %g mol sites/mol phase,\n\tmust be a subset of the related phase %s, %s.", comp_ref.Get_formula().c_str(), (double)comp_ref.Get_phase_proportion(), phase_ptr->name, phase_ptr->formula); error_msg(error_string, CONTINUE); break; } } } } } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: tidy_min_surface(void) /* ---------------------------------------------------------------------- */ /* * If surface is related to mineral, surface amount is * set in proportion */ { //std::map::iterator kit; //for (kit = Rxn_surface_map.begin(); kit != Rxn_surface_map.end(); kit++) //{ //for (size_t nn = 0; nn < Rxn_new_surface.size(); nn++) //{ // std::map::iterator kit = Rxn_surface_map.find(Rxn_new_surface[nn]); for (std::set::const_iterator nit = Rxn_new_surface.begin(); nit != Rxn_new_surface.end(); nit++) { std::map::iterator kit = Rxn_surface_map.find(*nit); if (kit == Rxn_surface_map.end()) { assert(false); } cxxSurface *surface_ptr = &(kit->second); if (!surface_ptr->Get_new_def()) continue; if (surface_ptr->Get_n_user() < 0) continue; for (size_t j = 0; j < surface_ptr->Get_surface_comps().size(); j++) { cxxSurfaceComp *surface_comp_ptr = &(surface_ptr->Get_surface_comps()[j]); cxxSurfaceCharge *surface_charge_ptr = surface_ptr->Find_charge(surface_comp_ptr->Get_charge_name()); if (surface_comp_ptr->Get_phase_name().size() == 0) continue; int n = surface_ptr->Get_n_user(); /* First find surface master species */ cxxNameDouble::iterator it; for (it = surface_comp_ptr->Get_totals().begin(); it != surface_comp_ptr->Get_totals().end(); it++) { /* Find master species */ class element *elt_ptr = element_store(it->first.c_str()); class master *master_ptr = elt_ptr->master; if (master_ptr == NULL) { input_error++; error_string = sformatf( "Master species not in database " "for %s, skipping element.", elt_ptr->name); error_msg(error_string, CONTINUE); continue; } if (master_ptr->type != SURF) continue; surface_comp_ptr->Set_master_element(elt_ptr->name); break; } if (surface_comp_ptr->Get_master_element().size() == 0) { input_error++; error_string = sformatf( "Surface formula does not contain a surface master species, %s", surface_comp_ptr->Get_formula().c_str()); error_msg(error_string, CONTINUE); continue; } /* Now find the mineral on which surface depends... */ cxxPPassemblage * pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, n); if (pp_assemblage_ptr == NULL) { input_error++; error_string = sformatf( "Equilibrium_phases %d must be defined to use surface related to mineral phase, %s", n, surface_comp_ptr->Get_formula().c_str()); error_msg(error_string, CONTINUE); continue; } std::map::iterator jit; jit = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); for ( ; jit != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); jit++) { if (strcmp_nocase(surface_comp_ptr->Get_phase_name().c_str(), jit->first.c_str()) == 0) { break; } } if (jit == pp_assemblage_ptr->Get_pp_assemblage_comps().end()) { input_error++; error_string = sformatf( "Mineral, %s, related to surface, %s, not found in Equilibrium_Phases %d", surface_comp_ptr->Get_phase_name().c_str(), surface_comp_ptr->Get_formula().c_str(), n); error_msg(error_string, CONTINUE); continue; } int l; class phase *phase_ptr = phase_bsearch(jit->first.c_str(), &l, FALSE); if (phase_ptr == NULL) { input_error++; error_string = sformatf( "Mineral, %s, related to surface, %s, not found in database.", jit->first.c_str(), surface_comp_ptr->Get_formula().c_str()); error_msg(error_string, CONTINUE); continue; } /* use database name for phase */ surface_comp_ptr->Set_phase_name(phase_ptr->name); /* make surface concentration proportional to mineral ... */ LDBLE conc = jit->second.Get_moles() * surface_comp_ptr->Get_phase_proportion(); /* if (conc < MIN_RELATED_SURFACE) conc = 0.0; */ { char * temp_formula = string_duplicate(surface_comp_ptr->Get_formula().c_str()); const char* cptr = temp_formula; count_elts = 0; paren_count = 0; get_elts_in_species(&cptr, conc); free_check_null(temp_formula); } { if (surface_ptr->Get_new_def()) { cxxNameDouble nd = elt_list_NameDouble(); surface_comp_ptr->Set_totals(nd); } else { surface_comp_ptr->Get_totals()[surface_comp_ptr->Get_master_element()] = conc; } } /* area */ if (surface_charge_ptr) { surface_charge_ptr->Set_grams(jit->second.Get_moles()); } /* * make sure surface elements are in phase * logically necessary for mass balance and to avoid negative concentrations when dissolving phase */ count_elts = 0; paren_count = 0; { const char* cptr = phase_ptr->formula; get_elts_in_species(&cptr, 1.0); } // Revise logic for surface related to mineral for (size_t jj = 0; jj < surface_ptr->Get_surface_comps().size(); jj++) { cxxSurfaceComp *comp_jj_ptr = &(surface_ptr->Get_surface_comps()[jj]); // Use formula for all types of surfaces { const char* cptr = comp_jj_ptr->Get_formula().c_str(); get_elts_in_species(&cptr, -comp_jj_ptr->Get_phase_proportion()); if (surface_ptr->Get_type() != cxxSurface::CD_MUSIC) { // Warn if not master species and charge balanced class element *elt_ptr = element_store(comp_jj_ptr->Get_master_element().c_str()); if (elt_ptr->master == NULL) { input_error++; error_string = sformatf("Unknown element definition in SURFACE \n\t for surface related to equilibrium_phase: SURFACE %d.", surface_ptr->Get_n_user()); error_msg(error_string); continue; } if (elt_ptr->master->s == NULL || elt_ptr->master->s->name == NULL) { input_error++; error_string = sformatf("Unknown master species definition in SURFACE \n\t for surface related to equilibrium_phase: SURFACE %d.", surface_ptr->Get_n_user()); error_msg(error_string); continue; } //if (strcmp(elt_ptr->master->s->name, temp_formula) != 0) //{ // error_string = sformatf("Suggest using master species formula in SURFACE \n\t for surface related to equilibrium_phase: %s.", // elt_ptr->master->s->name); // warning_msg(error_string); //} if (elt_ptr->master->s->z != 0.0 && surface_ptr->Get_dl_type() != cxxSurface::DONNAN_DL) { error_string = sformatf( "Use the -donnan option when coupling surface %s to an equilibrium_phase, \n\t and note to give the equilibrium_phase the surface charge.", elt_ptr->master->s->name); warning_msg(error_string); } } } } elt_list_combine(); /* Makes no sense: sorbed species need not be in mineral structure... */ /* But elements that can desorb into solution must be in mineral */ /* If you precipitate Ca-Mont, and make SurfMg (assuming this is the formula in SURFACE), where does the Mg come from? Further, if you precipitate Ca-Mont, make SurfCa, desorb all the Ca, then dissolve the "Ca-Mont", you must remove SurfCa, or you will end up with Ca in solution. H and O are excluded */ /* Example that makes montmorillonite a cation exchanger: PHASES Summ_Montmorillonite; Al2.33Si3.67O10(OH)2-0.33 + 12 H2O = 2.33 Al(OH)4- + 3.67 H4SiO4 + 2 H+; -log_k -44.4 SURFACE_MASTER_SPECIES; Summ Summ-; SURFACE_SPECIES; Summ- = Summ- SOLUTION 1; Na 1e1; Cl 1e1; pH 7 charge; C(4) 1 CO2(g) -2 EQUILIBRIUM_PHASES 1; Ca-Montmorillonite 0 1e-3 Summ_Montmorillonite 0 0 SURFACE 1; Summ Summ_Montmorillonite 0.33 3.11e5; -donnan; -equil 1 END */ for (int jj = 0; jj < count_elts; jj++) { if (elt_list[jj].elt->primary == NULL) { error_string = sformatf("Primary master species missing for %s", elt_list[jj].elt->name); error_msg(error_string, CONTINUE); break; } if (elt_list[jj].elt->primary->s == NULL) { error_string = sformatf( "Species missing for %s", elt_list[jj].elt->name); error_msg(error_string, CONTINUE); break; } if (elt_list[jj].elt->primary->s->type != SURF && elt_list[jj].coef < 0 //&& elt_list[jj].elt->primary->s != s_hplus //&& elt_list[jj].elt->primary->s != s_h2o ) { class element *elt_ptr = element_store(surface_comp_ptr->Get_master_element().c_str()); error_string = sformatf( "Element %s in sum of surface sites,\n" "\t including %s * %g mol sites/mol phase,\n" "\t exceeds stoichiometry in the related phase %s, %s.", elt_list[jj].elt->name, elt_ptr->master->s->name, (double) surface_comp_ptr->Get_phase_proportion(), phase_ptr->name, phase_ptr->formula); warning_msg(error_string); warning_msg("The mismatch in stoichiometry may cause mass-balance errors or unwanted redox reactions."); break; } } } } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: update_min_surface(void) /* ---------------------------------------------------------------------- */ /* * If surface is related to mineral, surface amount is * set in proportion */ { std::map::iterator kit; for (kit = Rxn_surface_map.begin(); kit != Rxn_surface_map.end(); kit++) { cxxSurface* surface_ptr = &(kit->second); if (surface_ptr->Get_n_user() < 0) continue; for (size_t j = 0; j < surface_ptr->Get_surface_comps().size(); j++) { double comp_moles = 0.0; cxxSurfaceComp* surface_comp_ptr = &(surface_ptr->Get_surface_comps()[j]); if (surface_comp_ptr->Get_phase_name().size() == 0) continue; cxxSurfaceCharge* surface_charge_ptr = NULL; if (surface_ptr->Get_type() != cxxSurface::NO_EDL) { surface_charge_ptr = surface_ptr->Find_charge(surface_comp_ptr->Get_charge_name()); if (surface_charge_ptr == NULL) { input_error++; error_string = sformatf("Data structure for surface charge not found " "for %s ", surface_comp_ptr->Get_formula().c_str()); error_msg(error_string, CONTINUE); continue; } } int n = surface_ptr->Get_n_user(); /* First find surface master species */ cxxNameDouble::iterator it; for (it = surface_comp_ptr->Get_totals().begin(); it != surface_comp_ptr->Get_totals().end(); it++) { /* Find master species */ class element* elt_ptr = element_store(it->first.c_str()); class master* master_ptr = elt_ptr->master; if (master_ptr == NULL) { input_error++; error_string = sformatf("Master species not in database " "for %s, skipping element.", elt_ptr->name); error_msg(error_string, CONTINUE); continue; } if (master_ptr->type != SURF) continue; comp_moles = it->second; surface_comp_ptr->Set_master_element(elt_ptr->name); break; } //if (surface_comp_ptr->Get_master_element().size() == 0) //{ // input_error++; // error_string = sformatf( // "Surface formula does not contain a surface master species, %s", // surface_comp_ptr->Get_formula().c_str()); // error_msg(error_string, CONTINUE); // continue; //} /* Now find the mineral on which surface depends... */ cxxPPassemblage* pp_assemblage_ptr = Utilities::Rxn_find(Rxn_pp_assemblage_map, n); if (pp_assemblage_ptr == NULL) { input_error++; error_string = sformatf( "Equilibrium_phases %d must be defined to use surface related to mineral phase, %s", n, surface_comp_ptr->Get_formula().c_str()); error_msg(error_string, CONTINUE); continue; } std::map::iterator jit; jit = pp_assemblage_ptr->Get_pp_assemblage_comps().begin(); for (; jit != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); jit++) { if (strcmp_nocase(surface_comp_ptr->Get_phase_name().c_str(), jit->first.c_str()) == 0) { break; } } if (jit == pp_assemblage_ptr->Get_pp_assemblage_comps().end()) { input_error++; error_string = sformatf( "Mineral, %s, related to surface, %s, not found in Equilibrium_Phases %d", surface_comp_ptr->Get_phase_name().c_str(), surface_comp_ptr->Get_formula().c_str(), n); error_msg(error_string, CONTINUE); continue; } int l; class phase* phase_ptr = phase_bsearch(jit->first.c_str(), &l, FALSE); if (phase_ptr == NULL) { input_error++; error_string = sformatf( "Mineral, %s, related to surface, %s, not found in database.", jit->first.c_str(), surface_comp_ptr->Get_formula().c_str()); error_msg(error_string, CONTINUE); continue; } /* use database name for phase */ surface_comp_ptr->Set_phase_name(phase_ptr->name); /* make surface concentration proportional to mineral ... */ LDBLE conc = jit->second.Get_moles() * surface_comp_ptr->Get_phase_proportion(); double grams = 0.0; if (surface_charge_ptr != NULL) { grams = surface_charge_ptr->Get_grams(); } if (comp_moles > 0.0) { surface_comp_ptr->multiply(conc / comp_moles); } else /* need to generate from scratch */ { char* temp_formula = string_duplicate(surface_comp_ptr->Get_formula().c_str()); const char* cptr = temp_formula; count_elts = 0; paren_count = 0; get_elts_in_species(&cptr, conc); free_check_null(temp_formula); cxxNameDouble nd = elt_list_NameDouble(); surface_comp_ptr->Set_totals(nd); } if (grams > 0.0) { surface_charge_ptr->multiply(jit->second.Get_moles() / grams); } else if (surface_charge_ptr != NULL) /* need to generate from scratch */ { surface_charge_ptr->Set_grams(jit->second.Get_moles()); surface_charge_ptr->Set_charge_balance(0.0); } } } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: tidy_kin_surface(void) /* ---------------------------------------------------------------------- */ /* * If surface is related to mineral, surface amount is * set in proportion */ { cxxKinetics *kinetics_ptr; class phase *phase_ptr; std::vector elt_list_kinetics; size_t count_elts_kinetics; //std::map::iterator it; //for (it = Rxn_surface_map.begin(); it != Rxn_surface_map.end(); it++) //{ //for (size_t nn = 0; nn < Rxn_new_surface.size(); nn++) //{ // std::map::iterator it = Rxn_surface_map.find(Rxn_new_surface[nn]); for (std::set::const_iterator nit = Rxn_new_surface.begin(); nit != Rxn_new_surface.end(); nit++) { std::map::iterator it = Rxn_surface_map.find(*nit); if (it == Rxn_surface_map.end()) { assert(false); } cxxSurface *surface_ptr = &(it->second); if (!surface_ptr->Get_new_def()) continue; if (surface_ptr->Get_n_user() < 0) continue; int n = surface_ptr->Get_n_user(); for (size_t j = 0; j < surface_ptr->Get_surface_comps().size(); j++) { cxxSurfaceComp *comp_ptr = &(surface_ptr->Get_surface_comps()[j]); if (comp_ptr->Get_rate_name().size() == 0) continue; comp_ptr->Set_master_element(""); /* First find surface master species */ int k; cxxNameDouble::iterator kit; for (kit = comp_ptr->Get_totals().begin(); kit != comp_ptr->Get_totals().end(); kit++) { /* Find master species */ class element *elt_ptr = element_store(kit->first.c_str()); class master *master_ptr = elt_ptr->master; if (master_ptr == NULL) { input_error++; error_string = sformatf( "Master species not in database " "for %s, skipping element.", elt_ptr->name); error_msg(error_string, CONTINUE); continue; } if (master_ptr->type != SURF) continue; comp_ptr->Set_master_element(elt_ptr->name); break; } if (comp_ptr->Get_master_element().size() == 0) { input_error++; error_string = sformatf( "Surface formula does not contain a surface master species, %s", comp_ptr->Get_formula().c_str()); error_msg(error_string, CONTINUE); continue; } /* Now find the kinetic reaction on which surface depends... */ if ((kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, n)) == NULL) { input_error++; error_string = sformatf( "Kinetics %d must be defined to use surface related to kinetic reaction, %s", n, comp_ptr->Get_formula().c_str()); error_msg(error_string, CONTINUE); continue; } for (k = 0; k < (int) kinetics_ptr->Get_kinetics_comps().size(); k++) { cxxKineticsComp *kin_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[k]); if (strcmp_nocase (comp_ptr->Get_rate_name().c_str(), kin_comp_ptr->Get_rate_name().c_str()) == 0) { break; } } if (k == (int) kinetics_ptr->Get_kinetics_comps().size()) { input_error++; error_string = sformatf( "Kinetic reaction, %s, related to surface, %s, not found in Kinetics %d", comp_ptr->Get_rate_name().c_str(), comp_ptr->Get_formula().c_str(), n); error_msg(error_string, CONTINUE); continue; } cxxKineticsComp *kin_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[k]); /* use database name for phase */ comp_ptr->Set_rate_name(kin_comp_ptr->Get_rate_name().c_str()); /* make surface concentration proportional to mineral ... */ LDBLE conc = kin_comp_ptr->Get_m() * comp_ptr->Get_phase_proportion(); /* if (conc < MIN_RELATED_SURFACE) conc = 0.0; */ { const char* cptr = comp_ptr->Get_formula().c_str(); count_elts = 0; paren_count = 0; get_elts_in_species(&cptr, conc); } { if (surface_ptr->Get_new_def()) { cxxNameDouble nd = elt_list_NameDouble(); comp_ptr->Set_totals(nd); } else { comp_ptr->Get_totals()[comp_ptr->Get_master_element()] = conc; } } /* area */ cxxSurfaceCharge *charge_ptr = surface_ptr->Find_charge(comp_ptr->Get_charge_name()); charge_ptr->Set_grams(kin_comp_ptr->Get_m()); } /* * check on elements */ /* Go through each kinetic reaction, add all related surface compositions * check for negative values */ if (!surface_ptr->Get_related_rate()) continue; kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, n); if (kinetics_ptr == NULL) { input_error++; error_string = sformatf( "Error in SURFACE related to KINETICS. "); error_msg(error_string, CONTINUE); continue; } for (size_t k = 0; k < kinetics_ptr->Get_kinetics_comps().size(); k++) { cxxKineticsComp *kin_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[k]); count_elts = 0; paren_count = 0; /* added in kinetics formula */ cxxNameDouble::iterator jit = kin_comp_ptr->Get_namecoef().begin(); for (; jit != kin_comp_ptr->Get_namecoef().end(); jit++) { std::string name = jit->first; LDBLE coef = jit->second; phase_ptr = NULL; int jj; phase_ptr = phase_bsearch(name.c_str(), &jj, FALSE); if (phase_ptr != NULL) { add_elt_list(phase_ptr->next_elt, 1.0); } else { const char* cptr = name.c_str(); get_elts_in_species(&cptr, coef); } } /* save kinetics formula */ if (count_elts > 0) { elt_list_combine(); } elt_list_kinetics = elt_list_vsave(); count_elts_kinetics = count_elts; /* get surface formulas */ count_elts = 0; paren_count = 0; cxxSurfaceComp *comp_ptr_save = NULL; for (size_t j = 0; j < surface_ptr->Get_surface_comps().size(); j++) { cxxSurfaceComp *comp_ptr = &(surface_ptr->Get_surface_comps()[j]); if (comp_ptr->Get_rate_name().size() == 0) continue; comp_ptr_save = comp_ptr; if (strcmp_nocase (comp_ptr->Get_rate_name().c_str(), kin_comp_ptr->Get_rate_name().c_str()) == 0) { const char* cptr = comp_ptr->Get_formula().c_str(); get_elts_in_species(&cptr, -1 * comp_ptr->Get_phase_proportion()); } } elt_list_combine(); for (int j = 0; j < count_elts; j++) { if (elt_list[j].elt == NULL) { input_error++; error_string = sformatf( "Cannot identify elements in kinetics component %s.", comp_ptr_save->Get_formula().c_str()); error_msg(error_string, CONTINUE); continue; } if (elt_list[j].elt->primary == NULL ) { input_error++; error_string = sformatf( "Cannot identify primary element in kinetics component %s.", comp_ptr_save->Get_formula().c_str()); error_msg(error_string, CONTINUE); continue; } if (elt_list[j].elt->primary->s == NULL) { input_error++; error_string = sformatf( "Cannot identify primary species for an element in kinetics component %s.", comp_ptr_save->Get_formula().c_str()); error_msg(error_string, CONTINUE); continue; } if (elt_list[j].elt->primary->s->type <= H2O) { int l; for (l = 0; l < count_elts_kinetics; l++) { if (elt_list[j].elt == elt_list_kinetics[l].elt) { break; } } if (l == count_elts_kinetics) { input_error++; error_string = sformatf( "Stoichiometry of surface, %s * %g mol sites/mol reactant,\n\tmust be a subset of the formula defined for the related reactant %s.\n\tElement %s is not present in reactant formula.", comp_ptr_save->Get_formula().c_str(), (double) comp_ptr_save->Get_phase_proportion(), comp_ptr_save->Get_rate_name().c_str(), elt_list[j].elt->name); error_msg(error_string, CONTINUE); } else if (fabs(elt_list[j].coef) > fabs(elt_list_kinetics[l].coef)) { input_error++; error_string = sformatf( "Stoichiometry of surface, %s * %g mol sites/mol reactant,\n\tmust be a subset of the formula defined for the related reactant %s.\n\tCoefficient of element %s in surface exceeds amount present in reactant formula.", comp_ptr_save->Get_formula().c_str(), (double) comp_ptr_save->Get_phase_proportion(), comp_ptr_save->Get_rate_name().c_str(), elt_list[j].elt->name); error_msg(error_string, CONTINUE); } } } elt_list_kinetics.clear(); } } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: update_kin_surface(void) /* ---------------------------------------------------------------------- */ /* * If surface is related to mineral, surface amount is * set in proportion. Need to update surface if * moles of kinetic reaction changes */ { cxxKinetics* kinetics_ptr; std::map::iterator it; for (it = Rxn_surface_map.begin(); it != Rxn_surface_map.end(); it++) { cxxSurface* surface_ptr = &(it->second); if (surface_ptr->Get_n_user() < 0) continue; int n = surface_ptr->Get_n_user(); for (size_t j = 0; j < surface_ptr->Get_surface_comps().size(); j++) { double comp_moles = 0.0; cxxSurfaceComp* comp_ptr = &(surface_ptr->Get_surface_comps()[j]); if (comp_ptr->Get_rate_name().size() == 0) continue; comp_ptr->Set_master_element(""); /* First find surface master species */ int k; cxxNameDouble::iterator kit; for (kit = comp_ptr->Get_totals().begin(); kit != comp_ptr->Get_totals().end(); kit++) { /* Find master species */ class element* elt_ptr = element_store(kit->first.c_str()); class master* master_ptr = elt_ptr->master; if (master_ptr == NULL) { input_error++; error_string = sformatf("Master species not in database " "for %s, skipping element.", elt_ptr->name); error_msg(error_string, CONTINUE); continue; } if (master_ptr->type != SURF) continue; comp_ptr->Set_master_element(elt_ptr->name); comp_moles = kit->second; break; } if (comp_ptr->Get_master_element().size() == 0) { input_error++; error_string = sformatf( "Surface formula does not contain a surface master species, %s", comp_ptr->Get_formula().c_str()); error_msg(error_string, CONTINUE); continue; } /* Now find the kinetic reaction on which surface depends... */ if ((kinetics_ptr = Utilities::Rxn_find(Rxn_kinetics_map, n)) == NULL) { input_error++; error_string = sformatf( "Kinetics %d must be defined to use surface related to kinetic reaction, %s", n, comp_ptr->Get_formula().c_str()); error_msg(error_string, CONTINUE); continue; } for (k = 0; k < (int)kinetics_ptr->Get_kinetics_comps().size(); k++) { cxxKineticsComp* kin_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[k]); if (strcmp_nocase (comp_ptr->Get_rate_name().c_str(), kin_comp_ptr->Get_rate_name().c_str()) == 0) { break; } } if (k == (int)kinetics_ptr->Get_kinetics_comps().size()) { input_error++; error_string = sformatf( "Kinetic reaction, %s, related to surface, %s, not found in Kinetics %d", comp_ptr->Get_rate_name().c_str(), comp_ptr->Get_formula().c_str(), n); error_msg(error_string, CONTINUE); continue; } cxxKineticsComp* kin_comp_ptr = &(kinetics_ptr->Get_kinetics_comps()[k]); /* use database name for rate */ comp_ptr->Set_rate_name(kin_comp_ptr->Get_rate_name().c_str()); cxxSurfaceCharge* charge_ptr = surface_ptr->Find_charge(comp_ptr->Get_charge_name()); if (surface_ptr->Get_type() != cxxSurface::NO_EDL) { charge_ptr = surface_ptr->Find_charge(comp_ptr->Get_charge_name()); if (charge_ptr == NULL) { input_error++; error_string = sformatf("Data structure for surface charge not found " "for %s ", comp_ptr->Get_formula().c_str()); error_msg(error_string, CONTINUE); continue; } } /* make surface concentration proportional to mineral ... */ LDBLE conc = kin_comp_ptr->Get_m() * comp_ptr->Get_phase_proportion(); double grams = 0.0; if (charge_ptr != NULL) charge_ptr->Get_grams(); if (comp_moles > 0.0) { comp_ptr->multiply(conc / comp_moles); } else /* need to generate from scratch */ { const char* cptr = comp_ptr->Get_formula().c_str(); count_elts = 0; paren_count = 0; get_elts_in_species(&cptr, conc); cxxNameDouble nd = elt_list_NameDouble(); comp_ptr->Set_totals(nd); } if (grams > 0.0) { charge_ptr->multiply(kin_comp_ptr->Get_m() / grams); } else if (charge_ptr != NULL) /* need to generate from scratch */ { charge_ptr->Set_grams(kin_comp_ptr->Get_m()); charge_ptr->Set_charge_balance(0.0); } } } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: ss_prep(LDBLE t, cxxSS *ss_ptr, int print) /* ---------------------------------------------------------------------- */ { int i, j, k, converged, divisions; LDBLE r, rt, ag0, ag1, crit_pt; LDBLE xc, tc; LDBLE l_x, x0, x1, xsm1, xsm2, xb1, xb2; LDBLE xc1, xc2; LDBLE facb1, faca1, spim1, xblm1, acrae, acrael, xliapt, xliapm; LDBLE xaly, xaly1, xaly2; LDBLE faca, facb, spialy, facal, facbl; LDBLE tol; if (pr.ss_assemblage == FALSE) print = FALSE; tol = 1e-6; r = R_KJ_DEG_MOL; rt = r * t; a0 = ss_ptr->Get_ag0() / rt; a1 = ss_ptr->Get_ag1() / rt; ss_ptr->Set_a0(a0); ss_ptr->Set_a1(a1); ag0 = a0 * rt; ag1 = a1 * rt; cxxSScomp *comp0_ptr = &(ss_ptr->Get_ss_comps()[0]); cxxSScomp *comp1_ptr = &(ss_ptr->Get_ss_comps()[1]); class phase *phase0_ptr = phase_bsearch(comp0_ptr->Get_name().c_str(), &k, FALSE); class phase *phase1_ptr = phase_bsearch(comp1_ptr->Get_name().c_str(), &k, FALSE); kc = exp(k_calc(phase0_ptr->rxn.logk, t, REF_PRES_PASCAL) * LOG_10); kb = exp(k_calc(phase1_ptr->rxn.logk, t, REF_PRES_PASCAL) * LOG_10); crit_pt = fabs(a0) + fabs(a1); /* * Default, no miscibility or spinodal gaps */ ss_ptr->Set_miscibility(false); ss_ptr->Set_spinodal(false); xsm1 = 0.5; xsm2 = 0.5; xb1 = 0.5; xb2 = 0.5; xc1 = 0; xc2 = 0; if (crit_pt >= tol) { /* * Miscibility gap information */ if (fabs(a1) < tol) { xc = 0.5; tc = ag0 / (2 * r); } else { xc = 0.5 + (pow((ag0 * ag0 + 27 * ag1 * ag1), (LDBLE) 0.5) - ag0) / (18 * ag1); tc = (12 * ag1 * xc - 6 * ag1 + 2 * ag0) * (xc - xc * xc) / r; } if (print == TRUE) { error_string = sformatf( "Description of Solid Solution %s", ss_ptr->Get_name().c_str()); dup_print(error_string, TRUE); } if (print == TRUE) { output_msg(sformatf( "\t Temperature: %g kelvin\n", (double) t)); output_msg(sformatf( "\t A0 (dimensionless): %g\n", (double) a0)); output_msg(sformatf( "\t A1 (dimensionless): %g\n", (double) a1)); output_msg(sformatf( "\t A0 (kJ/mol): %g\n", (double) ag0)); output_msg(sformatf( "\t A1 (kJ/mol): %g\n\n", (double) ag1)); } if (xc < 0 || xc > 1) { if (print == TRUE) output_msg(sformatf( "No miscibility gap above 0 degrees kelvin.\n")); } else { if (print == TRUE) { output_msg(sformatf( "\t Critical mole-fraction of component 2: %g\n", (double) xc)); output_msg(sformatf( "\t Critical temperature: %g kelvin\n", (double) tc)); output_msg(sformatf( "\n(The critical temperature calculation assumes that the Guggenheim model\ndefined at %g kelvin is valid at the critical temperature.)\n\n\n", (double) t)); } } /* * Calculate miscibility and spinodal gaps */ if (tc >= t) { /* search for sign changes */ x0 = 0; x1 = 1; if (scan(f_spinodal, &x0, &x1) == TRUE) { /* find first spinodal pt */ xsm1 = halve(f_spinodal, x0, x1, tol); xsm1 = halve(f_spinodal, x0, x1, tol); ss_ptr->Set_spinodal(true); /* find second spinodal pt */ x0 = x1; x1 = 1; if (scan(f_spinodal, &x0, &x1) == TRUE) { xsm2 = halve(f_spinodal, x0, x1, tol); } else { error_msg("Failed to find second spinodal point.", STOP); } } } } /* * Now find Miscibility gap */ if (ss_ptr->Get_spinodal()) { if (print == TRUE) output_msg(sformatf( "\t Spinodal-gap mole fractions, component 2: %g\t%g\n", (double) xsm1, (double) xsm2)); converged = FALSE; if (converged == FALSE) { for (i = 1; i < 3; i++) { divisions = (int) pow(10., i); for (j = 0; j < divisions; j++) { for (k = divisions; k > 0; k--) { xc1 = (LDBLE) j / divisions + 0.001; xc2 = (LDBLE) k / divisions; converged = solve_misc(&xc1, &xc2, tol); if (converged == TRUE) break; } if (converged == TRUE) break; } if (converged == TRUE) break; } } if (converged == FALSE) { error_msg("Failed to find miscibility gap.", STOP); } ss_ptr->Set_miscibility(true); if (xc1 < xc2) { xb1 = 1 - xc2; xb2 = 1 - xc1; xc1 = 1 - xb1; xc2 = 1 - xb2; } else { xb1 = 1 - xc1; xb2 = 1 - xc2; } facb1 = kb * xb1 * exp(xc1 * xc1 * (a0 + a1 * (4 * xb1 - 1))); faca1 = kc * xc1 * exp(xb1 * xb1 * (a0 - a1 * (3 - 4 * xb1))); spim1 = log10(faca1 + facb1); xblm1 = 1. / (1. + faca1 / facb1); acrae = facb1 / faca1; acrael = log10(acrae); xliapt = log10(facb1); xliapm = log10(faca1); if (print == TRUE) { output_msg(sformatf( "\t Miscibility-gap fractions, component 2: %g\t%g\n", (double) xb1, (double) xb2)); output_msg(sformatf( "\n\t\t\tEutectic Point Calculations\n\n")); output_msg(sformatf( "\t Aqueous activity ratio (comp2/comp1): %g\n", (double) acrae)); output_msg(sformatf( "\t Log aqueous activity ratio (comp2/comp1): %g\n", (double) acrael)); output_msg(sformatf( "\t Aqueous activity fraction of component 2: %g\n", (double) xblm1)); output_msg(sformatf( "\t Log IAP (component 2): %g\n", (double) xliapt)); output_msg(sformatf( "\t Log IAP (component 1): %g\n", (double) xliapm)); output_msg(sformatf( "\t Log Sum Pi: %g\n", (double) spim1)); } ss_ptr->Set_tk(t); ss_ptr->Set_xb1(xb1); ss_ptr->Set_xb2(xb2); } /* * Alyotropic point calculation */ xaly = -1.0; l_x = a0 * a0 + 3 * a1 * a1 + 6 * a1 * log(kb / kc); if (l_x > 0) { if (fabs(l_x - a0 * a0) >= tol) { xaly1 = (-(a0 - 3 * a1) + pow(l_x, (LDBLE) 0.5)) / (6 * a1); xaly2 = (-(a0 - 3 * a1) - pow(l_x, (LDBLE) 0.5)) / (6 * a1); if (xaly1 >= 0 && xaly1 <= 1) { xaly = xaly1; } if (xaly2 >= 0 && xaly2 <= 1) { xaly = xaly2; } } else { xaly = 0.5 + log(kb / kc) / (2 * a0); } if (xaly > 0 && xaly < 1) { faca = kc * (1 - xaly) * exp(xaly * xaly * (a0 - a1 * (3 - 4 * xaly))); facb = kb * xaly * exp((1 - xaly) * (1 - xaly) * (a0 + a1 * (4 * xaly - 1.0))); spialy = log10(faca + facb); facal = log10(faca); facbl = log10(facb); if (xaly > xb1 && xaly < xb2) { if (print == TRUE) output_msg(sformatf( "\nLocal minimum in the solidus curve coresponding to a maximum\nin the minimum stoichiometric saturation curve.\n\n")); } else { if (print == TRUE) output_msg(sformatf( "\n\t\t\tAlyotropic Point\n\n")); } if (print == TRUE) { output_msg(sformatf( "\t Solid mole fraction of component 2: %g\n", (double) xaly)); output_msg(sformatf( "\t Log IAP (component 2): %g\n", (double) facbl)); output_msg(sformatf( "\t Log IAP (component 1): %g\n", (double) facal)); output_msg(sformatf( "\t Log Sum Pi: %g\n", (double) spialy)); } } } return (OK); } /* ---------------------------------------------------------------------- */ LDBLE Phreeqc:: halve(LDBLE f(LDBLE x, void *), LDBLE x0, LDBLE x1, LDBLE tol) /* ---------------------------------------------------------------------- */ { int i; LDBLE l_x, y, y0, dx; y0 = f(x0, this); dx = (x1 - x0); /* * Loop for interval halving */ for (i = 0; i < 100; i++) { dx *= 0.5; l_x = x0 + dx; y = f(l_x, this); if (dx < tol || y == 0) { break; } if (y0 * y >= 0) { x0 = l_x; y0 = y; } } return (x0 + dx); } /* ---------------------------------------------------------------------- */ int Phreeqc:: scan(LDBLE f(LDBLE x, void *), LDBLE * xx0, LDBLE * xx1) /* ---------------------------------------------------------------------- */ { int i, j; LDBLE fx0, fx1, divisions; LDBLE x0, x1, diff; x0 = *xx0; x1 = *xx1; diff = x1 - x0; for (j = 0; j < 3; j++) { fx0 = f(x0, this); divisions = (int) pow((LDBLE) 10, (LDBLE) j); for (i = 1; i < divisions; i++) { x1 = *xx0 + diff * (LDBLE) i / divisions; fx1 = f(x1, this); if (fx0 * fx1 <= 0) { *xx0 = x0; *xx1 = x1; return (TRUE); } x0 = x1; fx0 = fx1; } } return (FALSE); } /* ---------------------------------------------------------------------- */ LDBLE Phreeqc:: f_spinodal(LDBLE x, void * cookie) /* ---------------------------------------------------------------------- */ { LDBLE fx; Phreeqc * pThis; pThis = (Phreeqc *) cookie; fx = -12 * pThis->a1 * x * x * x + (18 * pThis->a1 - 2 * pThis->a0) * x * x + (2 * pThis->a0 - 6 * pThis->a1) * x - 1.0; return (fx); } /* ---------------------------------------------------------------------- */ int Phreeqc:: slnq(int n, LDBLE * a, LDBLE * l_delta, int ncols, int print) /* ---------------------------------------------------------------------- */ { int i, j, k, m; /* debug */ int row; LDBLE b; /* Debug */ if (print == TRUE) { output_msg(sformatf( "\nArray in slnq: \n\n")); for (i = 0; i < ncols - 1; i++) { row = i * (n + 1); for (j = 0; j < ncols; j++) { output_msg(sformatf( "%10.2e", (double) a[row + j])); } output_msg(sformatf( "\n")); } output_msg(sformatf( "\n")); } if (n == 0) return (OK); /* Trivial case */ if (n == 1) { if (fabs(a[0]) < ZERO_TOL) goto slnq_error; l_delta[0] = a[1] / a[0]; return (OK); } /* Reduction loop */ for (i = 0; i < n - 1; i++) { b = fabs(a[i * ncols + i]); m = i; /* Find maximum value in column */ for (j = i + 1; j < n; j++) { if (fabs(a[j * ncols + i]) > b) { b = fabs(a[j * ncols + i]); m = j; } } /* Check for singularity */ if (b < ZERO_TOL) goto slnq_error; /* Exchange rows if necessary */ if (m != i) { for (j = i; j <= n; j++) { b = a[i * ncols + j]; a[i * ncols + j] = a[m * ncols + j]; a[m * ncols + j] = b; } } /* Make a[i][i]=1.0 */ for (j = n; j >= i; j--) { a[i * ncols + j] /= a[i * ncols + i]; } /* Reduction step */ for (j = i + 1; j < n; j++) { if (a[j * ncols + i] == 0.0) continue; b = -a[j * ncols + i]; for (k = i + 1; k <= n; k++) { a[j * ncols + k] += b * a[i * ncols + k]; } } } /* Calculation of l_delta[n] */ if (fabs(a[(n - 1) * ncols + n - 1]) > ZERO_TOL) { l_delta[n - 1] = a[(n - 1) * ncols + n] / a[(n - 1) * ncols + n - 1]; } else { output_msg(sformatf( "Error: Divide by zero in slnq.\n")); l_delta[n] = 0.0; goto slnq_error; } /* Back substitution for other delta values */ for (i = n - 2; i >= 0; i--) { l_delta[i] = a[i * ncols + n]; for (j = i + 1; j < n; j++) { l_delta[i] -= a[i * ncols + j] * l_delta[j]; } } if (print == TRUE) { output_msg(sformatf( "\nResults from slnq: \n\n")); for (i = 0; i < n; i++) { output_msg(sformatf( "%10.2e", (double) l_delta[i])); } output_msg(sformatf( "\n")); } return (OK); slnq_error:{ error_string = sformatf( "Error: Singular matrix in subroutine slnq. \n"); warning_msg(error_string); } return (ERROR); } /* ---------------------------------------------------------------------- */ int Phreeqc:: solve_misc(LDBLE * xxc1, LDBLE * xxc2, LDBLE tol) /* ---------------------------------------------------------------------- */ { int i, repeat, converged, max_iter; LDBLE x1, x2, xb1, xb2; LDBLE xc1, xc1_2, xc1_3, xc2, xc2_2, xc2_3; LDBLE lc1, lc2, lb1, lb2; LDBLE a[6], d[2]; LDBLE t; d[0] = d[1] = 0; xc1 = *xxc1; xc2 = *xxc2; x1 = 0; x2 = 0; converged = TRUE; max_iter = 25; for (i = 0; i < max_iter; i++) { /* output_msg(sformatf( "%d xc1: %g\txc2 %g\n", i, xc1, xc2)); */ xb1 = 1 - xc1; xb2 = 1 - xc2; xc1_2 = xc1 * xc1; xc1_3 = xc1_2 * xc1; xc2_2 = xc2 * xc2; xc2_3 = xc2_2 * xc2; lc1 = exp(xb1 * xb1 * (a0 - a1 * (3 - 4 * xb1))); lb1 = exp(xc1 * xc1 * (a0 + a1 * (4 * xb1 - 1))); lc2 = exp(xb2 * xb2 * (a0 - a1 * (3 - 4 * xb2))); lb2 = exp(xc2 * xc2 * (a0 + a1 * (4 * xb2 - 1))); /* -fb */ a[2] = -(xb1 * lb1 - xb2 * lb2); /* -fc */ a[5] = -(xc1 * lc1 - xc2 * lc2); if (fabs(a[2]) < tol && fabs(a[5]) < tol) break; /* dfb/dxc1 */ t = exp(a0 * xc1_2 - 4 * a1 * xc1_3 + 3 * a1 * xc1_2); a[0] = (2 * a0 * xc1 + 6 * a1 * xc1 - 2 * a0 * xc1_2 + 12 * a1 * xc1_3 - 18 * a1 * xc1_2 - 1) * t; /* dfb/dxc2 */ t = exp(a0 * xc2_2 - 4 * a1 * xc2_3 + 3 * a1 * xc2_2); a[1] = (2 * a0 * xc2_2 - 12 * a1 * xc2_3 - 2 * a0 * xc2 + 18 * a1 * xc2_2 - 6 * a1 * xc2 + 1) * t; /* dfc/dxc1 */ t = exp(a0 * xc1_2 - 2 * a0 * xc1 + a0 - 4 * a1 * xc1_3 + 9 * a1 * xc1_2 - 6 * a1 * xc1 + a1); a[3] = (2 * a0 * xc1_2 - 2 * a0 * xc1 - 12 * a1 * xc1_3 + 18 * a1 * xc1_2 - 6 * a1 * xc1 + 1) * t; /* dfc/dxc2 */ t = exp(a0 * xc2_2 - 2 * a0 * xc2 + a0 - 4 * a1 * xc2_3 + 9 * a1 * xc2_2 - 6 * a1 * xc2 + a1); a[4] = (-2 * a0 * xc2_2 + 2 * a0 * xc2 + 12 * a1 * xc2_3 - 18 * a1 * xc2_2 + 6 * a1 * xc2 - 1) * t; /* solve for dxc1 and dxc2 */ slnq(2, a, d, 3, FALSE); repeat = TRUE; while (repeat == TRUE) { x1 = xc1 + d[0]; x2 = xc2 + d[1]; if (x1 > 1 || x1 < 0 || x2 > 1 || x2 < 0) { d[0] *= 0.5; d[1] *= 0.5; } else { repeat = FALSE; } }; xc1 = x1; xc2 = x2; if (fabs(xc1 - xc2) < .01) { converged = FALSE; break; } } if (i == max_iter) converged = FALSE; *xxc1 = xc1; *xxc2 = xc2; return (converged); } /* ---------------------------------------------------------------------- */ int Phreeqc:: ss_calc_a0_a1(cxxSS *ss_ptr) /* ---------------------------------------------------------------------- */ { int i, done; LDBLE r, rt; std::vector p; LDBLE q1, q2, xbq1, xbq2, xb1, xb2, xc1, xc2; LDBLE r1, r2, pa1, pb1, pa2, pb2, xsm1, xsm2; LDBLE pn9, pn10, c5, c6, pl9, pl10, pj9, pj10; LDBLE xc, tc; LDBLE spialy, azero, phi1, phi2, test; LDBLE dq1, dq2, denom, ratio, dr1, dr2, x21, x22, x61, x62; LDBLE l_a0, l_a1, ag0, ag1; LDBLE wg2, wg1, alpha2, alpha3; LDBLE l_kc, l_kb; LDBLE xaly, xcaly, alpha0, alpha1, fx, fx1; LDBLE tol; tol = 1e-6; rt = ss_ptr->Get_tk() * R_KJ_DEG_MOL; if (ss_ptr->Get_ss_comps().size() < 2) { input_error++; error_string = sformatf( "Two components not defined for solid solution ", ss_ptr->Get_name().c_str()); error_msg(error_string, CONTINUE); return (ERROR); } cxxSScomp *comp0_ptr = &(ss_ptr->Get_ss_comps()[0]); cxxSScomp *comp1_ptr = &(ss_ptr->Get_ss_comps()[1]); int k; class phase *phase0_ptr = phase_bsearch(comp0_ptr->Get_name().c_str(), &k, FALSE); class phase *phase1_ptr = phase_bsearch(comp1_ptr->Get_name().c_str(), &k, FALSE); if (phase0_ptr == NULL || phase1_ptr == NULL) { input_error++; error_string = sformatf( "Two components were not defined for %s solid solution", ss_ptr->Get_name().c_str()); error_msg(error_string, CONTINUE); return (ERROR); } l_kc = exp(k_calc(phase0_ptr->rxn.logk, ss_ptr->Get_tk(), REF_PRES_PASCAL) * LOG_10); l_kb = exp(k_calc(phase1_ptr->rxn.logk, ss_ptr->Get_tk(), REF_PRES_PASCAL) * LOG_10); p = ss_ptr->Get_p(); l_a0 = 0; l_a1 = 0; ag0 = 0; ag1 = 0; dq2 = 0; switch (ss_ptr->Get_input_case()) { /* * dimensionless a0 and a1 */ case cxxSS::SS_PARM_A0_A1: l_a0 = p[0]; l_a1 = p[1]; ag0 = l_a0 * rt; ag1 = l_a1 * rt; break; /* * two activity coefficients * q1, q2, xbq1, xbq2 */ case cxxSS::SS_PARM_GAMMAS: q1 = p[0]; q2 = p[1]; xbq1 = p[2]; xbq2 = p[3]; done = FALSE; if (fabs(1 - xbq1) > 0 && q1 > 0) { dq1 = log(q1) / ((1 - xbq1) * (1 - xbq1)); if (xbq2 <= 0 || xbq2 > 1) { l_a0 = dq1; l_a1 = 0; done = TRUE; } } if (done == FALSE) { if (fabs(xbq2) < 0 || q2 <= 0) { input_error++; error_string = sformatf( "No solution possible for A0 and A1 calculation from two activity coefficients, %s.\n", ss_ptr->Get_name().c_str()); error_msg(error_string, CONTINUE); done = TRUE; } } if (done == FALSE) { dq2 = log(q2) / (xbq2 * xbq2); if (xbq1 < 0. || xbq2 > 1.) { l_a0 = dq2; l_a1 = 0; done = TRUE; } } if (done == FALSE) { denom = 4 * (xbq1 - xbq2) + 2; if (fabs(denom) >= tol) { if (fabs(1 - xbq1) > 0 && q1 > 0) { dq1 = log(q1) / ((1 - xbq1) * (1 - xbq1)); l_a0 = (dq1 * (3 - 4 * xbq2) + dq2 * (4 * xbq1 - 1)) / denom; l_a1 = (dq1 - dq2) / denom; done = TRUE; } } } if (done == FALSE) { input_error++; error_string = sformatf( "No solution possible for A0 and A1 calculation from two activity coefficients, %s.\n", ss_ptr->Get_name().c_str()); error_msg(error_string, CONTINUE); } /* io = 1 */ ag0 = l_a0 * rt; ag1 = l_a1 * rt; break; /* * two distribution coefficients * q1, q2, xbq1, xbq2 */ case cxxSS::SS_PARM_DIST_COEF: q1 = p[0]; q2 = p[1]; xbq1 = p[2]; xbq2 = p[3]; ratio = l_kc / l_kb; dr1 = log(q1 / ratio); x21 = 2 * xbq1 - 1; if (fabs(xbq1 - xbq2) < tol || xbq2 < 0) { l_a0 = dr1 / x21; l_a1 = 0; } else { dr2 = log(q2 / ratio); x22 = 2 * xbq2 - 1; if (xbq1 < 0.) { l_a0 = dr2 / x22; l_a1 = 0; } else { x61 = 6 * xbq1 * xbq1 - 6 * xbq1 + 1; x62 = 6 * xbq2 * xbq2 - 6 * xbq2 + 1; if (fabs(x22 * x61 - x21 * x62) < tol) { input_error++; error_string = sformatf( "No solution possible for A0 and A1 calculation from two distribution coefficients, %s.\n", ss_ptr->Get_name().c_str()); error_msg(error_string, CONTINUE); } l_a0 = (x61 * dr2 - x62 * dr1) / (x22 * x61 - x21 * x62); l_a1 = (x21 * dr2 - x22 * dr1) / (x21 * x62 - x22 * x61); } } /* io = 1 */ ag0 = l_a0 * rt; ag1 = l_a1 * rt; break; /* * from miscibility gap fractions * q1, q2 */ case cxxSS::SS_PARM_MISCIBILITY: q1 = p[0]; q2 = p[1]; xb1 = q1; xb2 = q2; xc1 = 1 - xb1; xc2 = 1 - xb2; r1 = log(xb1 / xb2); r2 = log(xc1 / xc2); pa1 = xc2 * xc2 - xc1 * xc1; pb1 = 3 * (xc2 * xc2 - xc1 * xc1) - 4 * (xc2 * xc2 * xc2 - xc1 * xc1 * xc1); pa2 = xb2 * xb2 - xb1 * xb1; pb2 = -(3 * (xb2 * xb2 - xb1 * xb1) - 4 * (xb2 * xb2 * xb2 - xb1 * xb1 * xb1)); l_a0 = (r1 - pb1 / pb2 * r2) / (pa1 - pa2 * pb1 / pb2); l_a1 = (r1 - pa1 / pa2 * r2) / (pb1 - pb2 * pa1 / pa2); /* io = 1 */ ag0 = l_a0 * rt; ag1 = l_a1 * rt; break; /* * from spinodal gap fractions * q1, q2 */ case cxxSS::SS_PARM_SPINODAL: q1 = p[0]; q2 = p[1]; xsm1 = q1; xsm2 = q2; pn9 = 1 / xsm1; pn10 = 1 / xsm2; c5 = 1 - xsm1; c6 = 1 - xsm2; pl9 = 6 * c5 - 12 * c5 * c5; pl10 = 6 * c6 - 12 * c6 * c6; pj9 = 2 * c5; pj10 = 2 * c6; l_a0 = (pn9 - pl9 / pl10 * pn10) / (pj9 - pl9 / pl10 * pj10); l_a1 = (pn9 - pj9 / pj10 * pn10) / (pl9 - pj9 / pj10 * pl10); /* io = 1 */ ag0 = l_a0 * rt; ag1 = l_a1 * rt; break; /* * from critical point * q1, q2 */ case cxxSS::SS_PARM_CRITICAL: xc = p[0]; tc = p[1]; r = R_KJ_DEG_MOL; ag1 = r * tc * (2 * xc - 1) / (12 * xc * xc * (1 - xc) * (1 - xc)); ag0 = (r * tc / (xc * (1 - xc)) - (12 * xc - 6) * ag1) / 2; /* io = 0 */ l_a0 = ag0 / rt; l_a1 = ag1 / rt; break; /* * from alyotropic point * q1, q2 */ case cxxSS::SS_PARM_ALYOTROPIC: q1 = p[0]; q2 = p[1]; xaly = q1; r = log(l_kb / l_kc); alpha0 = 2 * xaly - 1; alpha1 = 6 * xaly * (xaly - 1) + 1; spialy = pow((LDBLE) 10., q2); l_a0 = -999.; l_a1 = -999.; if (fabs(alpha0) < tol) { input_error++; error_string = sformatf( "No solution possible for A0 and A1 calculation from alyotropic point, %s.\n", ss_ptr->Get_name().c_str()); error_msg(error_string, CONTINUE); } else { azero = 1; if (fabs(alpha0) > tol) azero = r / alpha0; xcaly = 1 - xaly; /* * Solve for a0 by Newton's method */ for (i = 0; i < 50; i++) { phi1 = xcaly * xcaly * (azero + (r - azero * alpha0) * (4 * xaly - 1) / alpha1); phi2 = xaly * xaly * (azero + (3 - 4 * xaly) * (azero * alpha0 - r) / alpha1); phi1 = xaly * l_kb * exp(phi1); phi2 = xcaly * l_kc * exp(phi2); fx = phi1 + phi2 - spialy; fx1 = xcaly * xcaly * (1 - alpha0 * (4 * xaly - 1) / alpha1) * phi1 + xaly * xaly * (1 + alpha0 * (3 - 4 * xaly) / alpha1) * phi2; if (fabs(fx1) < 1e-10) { input_error++; error_string = sformatf( "Could not find A0 and A1 calculation from alyotropic point, %s.\n", ss_ptr->Get_name().c_str()); error_msg(error_string, CONTINUE); break; } l_a0 = azero - fx / fx1; test = fabs(l_a0 - azero) + fabs(fx); azero = l_a0; if (test < tol) break; } if (i == 50) { input_error++; error_string = sformatf( "Too many iterations, could not find A0 and A1 calculation from alyotropic point, %s.\n", ss_ptr->Get_name().c_str()); error_msg(error_string, CONTINUE); } else { l_a1 = (r - l_a0 * alpha0) / alpha1; /* io = 0 */ ag0 = l_a0 * rt; ag1 = l_a1 * rt; } } break; /* * dimensional (kJ/mol) Guggenheim parameters * ag0, ag1 */ case cxxSS::SS_PARM_DIM_GUGG: ag0 = p[0]; ag1 = p[1]; l_a0 = ag0 / rt; l_a1 = ag1 / rt; break; /* * Waldbaum-Thompson * wg2, wg1 */ case cxxSS::SS_PARM_WALDBAUM: wg2 = p[0]; wg1 = p[1]; ag0 = (wg2 + wg1) / 2; ag1 = (wg2 - wg1) / 2; l_a0 = ag0 / rt; l_a1 = ag1 / rt; break; /* * Margules * alpha2, alpha3 */ case cxxSS::SS_PARM_MARGULES: alpha2 = p[0]; alpha3 = p[1]; l_a0 = alpha2 + 3 * alpha3 / 4; l_a1 = alpha3 / 4; ag0 = l_a0 * rt; ag1 = l_a1 * rt; break; case cxxSS::SS_PARM_NONE: break; } ss_ptr->Set_ag0(ag0); ss_ptr->Set_ag1(ag1); ss_ptr->Set_a0(l_a0); ss_ptr->Set_a1(l_a1); return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: tidy_master_isotope(void) /* ---------------------------------------------------------------------- */ { int i; class master *master_ptr; for (i = 0; i < (int)master_isotope.size(); i++) { /* * Mark master species list as minor isotope */ if (master_isotope[i]->minor_isotope == TRUE) { master_ptr = master_bsearch(master_isotope[i]->name); if (master_ptr == NULL) { input_error++; error_string = sformatf( "Did not find master species for isotope, %s", master_isotope[i]->name); error_msg(error_string, CONTINUE); master_isotope[i]->master = NULL; continue; } else { master_isotope[i]->master = master_ptr; } master_ptr->minor_isotope = TRUE; } } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: tidy_isotope_ratios(void) /* ---------------------------------------------------------------------- */ { int i; class master *master_ptr; class master_isotope *master_isotope_ptr; class calculate_value *calculate_value_ptr; for (i = 0; i < (int)isotope_ratio.size(); i++) { /* * Mark master species list as minor isotope */ master_isotope_ptr = master_isotope_search(isotope_ratio[i]->isotope_name); if (master_isotope_ptr == NULL) { input_error++; error_string = sformatf( "For ISOTOPE_RATIO %s, did not find ISOTOPE definition for this isotope, %s", isotope_ratio[i]->name, isotope_ratio[i]->isotope_name); error_msg(error_string, CONTINUE); } master_ptr = master_bsearch(isotope_ratio[i]->isotope_name); if (master_ptr == NULL) { input_error++; error_string = sformatf( "For ISOTOPE_RATIO %s, did not find SOLUTION_MASTER_SPECIES for isotope, %s", isotope_ratio[i]->name, isotope_ratio[i]->isotope_name); error_msg(error_string, CONTINUE); } calculate_value_ptr = calculate_value_search(isotope_ratio[i]->name); if (calculate_value_ptr == NULL) { input_error++; error_string = sformatf( "For ISOTOPE_RATIOS %s, did not find corresponding CALCULATE_VALUE definition", isotope_ratio[i]->name); error_msg(error_string, CONTINUE); } } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: tidy_isotope_alphas(void) /* ---------------------------------------------------------------------- */ { int i; class calculate_value *calculate_value_ptr; class logk *logk_ptr; for (i = 0; i < (int)isotope_alpha.size(); i++) { /* * Mark master species list as minor isotope */ calculate_value_ptr = calculate_value_search(isotope_alpha[i]->name); if (calculate_value_ptr == NULL) { input_error++; error_string = sformatf( "For ISOTOPE_ALPHAS %s, did not find corresponding CALCULATE_VALUE definition", isotope_alpha[i]->name); error_msg(error_string, CONTINUE); } if (isotope_alpha[i]->named_logk != NULL) { logk_ptr = logk_search(isotope_alpha[i]->named_logk); if (logk_ptr == NULL) { input_error++; error_string = sformatf( "For ISOTOPE_ALPHAS %s, did not find corresponding NAMED_EXPRESSION definition %s.", isotope_alpha[i]->name, isotope_alpha[i]->named_logk); error_msg(error_string, CONTINUE); } } } return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: reset_last_model(void) /* ---------------------------------------------------------------------- */ { /* * Initialize model */ last_model.force_prep = true; last_model.gas_phase.clear(); last_model.ss_assemblage.clear(); last_model.pp_assemblage.clear(); last_model.add_formula.clear(); last_model.si.clear(); last_model.dl_type = cxxSurface::NO_DL; last_model.surface_comp.clear(); last_model.surface_charge.clear(); return (OK); } /* ---------------------------------------------------------------------- */ int Phreeqc:: tidy_exchange(void) /* ---------------------------------------------------------------------- */ /* * If exchanger is related to mineral, exchanger amount is * set in proportion */ { //std::map::iterator it = Rxn_exchange_map.begin(); //for ( ; it != Rxn_exchange_map.end(); it++) //for (size_t nn = 0; nn < Rxn_new_exchange.size(); nn++) //{ // std::map::iterator it = Rxn_exchange_map.find(Rxn_new_exchange[nn]); for (std::set::const_iterator nit = Rxn_new_exchange.begin(); nit != Rxn_new_exchange.end(); nit++) { std::map::iterator it = Rxn_exchange_map.find(*nit); if (it == Rxn_exchange_map.end()) { assert(false); } //std::map::iterator it = Rxn_exchange_map.begin(); cxxExchange * exchange_ptr = &(it->second); //if (!exchange_ptr->Get_new_def()) // continue; //if (exchange_ptr->Get_n_user() < 0) // continue; // check elements for (size_t j = 0; j < exchange_ptr->Get_exchange_comps().size(); j++) { cxxExchComp & comp_ref = exchange_ptr->Get_exchange_comps()[j]; if (comp_ref.Get_phase_name().size() > 0) continue; if (comp_ref.Get_rate_name().size() > 0) continue; /* Check elements */ cxxNameDouble nd = comp_ref.Get_totals(); cxxNameDouble::iterator kit = nd.begin(); for (; kit != nd.end(); kit++) { /* Find master species */ class element *elt_ptr = element_store(kit->first.c_str()); if (elt_ptr == NULL || elt_ptr->master == NULL) { input_error++; error_string = sformatf( "Master species not in database " "for %s, skipping element.", kit->first.c_str()); error_msg(error_string, CONTINUE); break; } } } } return (OK); }