#include "Phreeqc.h"
#include "phqalloc.h"

#include "Utils.h"
#include "NameDouble.h"
#include "PBasic.h"
#include "Exchange.h"
#include "GasPhase.h"
#include "PPassemblage.h"
#include "SSassemblage.h"
#include "cxxKinetics.h"
#include "Solution.h"
#include "Parser.h"

#if defined(PHREEQCI_GUI)
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#endif

/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
activity(const char *species_name)
/* ---------------------------------------------------------------------- */
{
	class species *s_ptr;
	LDBLE a;

	s_ptr = s_search(species_name);
	if (s_ptr == s_h2o)
	{
		a = pow((LDBLE) 10., s_h2o->la);
	}
	else if (s_ptr == s_eminus)
	{
		a = pow((LDBLE) 10., s_eminus->la);
	}
	else if (s_ptr == NULL || s_ptr->in == FALSE)
	{
		a = 1e-99;
	}
	else
	{
		a = pow((LDBLE) 10., s_ptr->lm + s_ptr->lg);
	}
	return (a);
}

/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
activity_coefficient(const char *species_name)
/* ---------------------------------------------------------------------- */
{
	class species *s_ptr;
	LDBLE g, dum = 0.0;

	s_ptr = s_search(species_name);
	if (s_ptr != NULL && s_ptr->in != FALSE && ((s_ptr->type < EMINUS) || (s_ptr->type == EX) || (s_ptr->type == SURF)))
	{
		if (s_ptr->type == EX && s_ptr->equiv && s_ptr->alk)
			dum = log10(s_ptr->equiv / s_ptr->alk);
		g = pow((LDBLE) 10., s_ptr->lg - dum);
	}
	else
	{
		g = 0;
	}
	return (g);
}

/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
log_activity_coefficient(const char *species_name)
/* ---------------------------------------------------------------------- */
{
	class species *s_ptr;
	LDBLE g, dum = 0.0;

	s_ptr = s_search(species_name);
	if (s_ptr != NULL && s_ptr->in != FALSE && ((s_ptr->type < EMINUS) || (s_ptr->type == EX) || (s_ptr->type == SURF)))
	{
		if (s_ptr->type == EX && s_ptr->equiv && s_ptr->alk)
			dum = log10(s_ptr->equiv / s_ptr->alk);
		g = s_ptr->lg - dum;
	}
	else
	{
		g = 0;
	}
	return (g);
}

/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
aqueous_vm(const char *species_name)
/* ---------------------------------------------------------------------- */
{
	class species *s_ptr;
	LDBLE g;

	s_ptr = s_search(species_name);
	if (s_ptr != NULL && s_ptr->in != FALSE && s_ptr->type < EMINUS)
	{
		g = s_ptr->logk[vm_tc];
	}
	else
	{
		g = 0;
	}
	return (g);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
phase_vm(const char *phase_name)
/* ---------------------------------------------------------------------- */
{
	class phase *phase_ptr;
	int l;
	LDBLE g;

	phase_ptr = phase_bsearch(phase_name, &l, FALSE);
	if (phase_ptr == NULL)
	{
		g = 0.0;
	}
	else
	{
		g = phase_ptr->logk[vm0];
	}
	return (g);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
sa_declercq(double sa_type, double Sa, double d, double m, double m0, double gfw)
/* ---------------------------------------------------------------------- */
{
	if (sa_type == 0) 
	{
		// surface-area-calculation-Fixed_Surface
		return Sa;
	}
	else if (sa_type == 1)
		// surface-area-calculation-Square
	{
		double mass0 = m0 * gfw;
		double V0 = mass0 / d;
		double St0 = mass0 * Sa;        // total surface
		double a0 = pow(V0, 1.0/3.0);   // side length
		double Sp0 = 6.0 * a0*a0;       // surface particle
		double np = St0 / Sp0;          // number of particles
	    double RATS = Sa / St0;
		double mass = m * gfw;
		double V = mass / d;
		double a = pow(V, 1.0/3.0); 
		double St = 6.0 * a*a*np;
		return St * RATS;               // total current surface
	}
	else if (sa_type == 2)
	{
		//double pi = 3.14159265359;
		double mass0 = m0 * gfw;
		double V0 = mass0 / d;                         // volume
		double St0 = mass0 * Sa;                       // total surface
		double a0 = pow(3.0 * V0/(4.0 * pi), 1.0/3.0); // ((3*V0)/(4 * 3.14159265359))^(1/3)  
		double Sp0 = (4.0 * pi) * a0 * a0;             // surface particle
		double np = St0 / Sp0;                         // number of particles
		double RATS = Sa / St0;
 
		double mass = m * gfw;
		double V = mass / d;
		double a = pow(3.0 * V/(4.0 * pi), 1.0/3.0);  //((3*V)/(4 * 3.14159265359))^(1/3)
		double St = 4.0 * pi * a * a * np;
		return St * RATS;                             // total current surface
	}
	error_string = sformatf( "Unknown surface area type in SA_DECLERCQ %d.", (int) sa_type);
	error_msg(error_string, CONTINUE);
	input_error++;
	return (MISSING);
 
}

/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
diff_c(const char *species_name)
/* ---------------------------------------------------------------------- */
{
	class species *s_ptr;
	LDBLE g;

	s_ptr = s_search(species_name);
	if (s_ptr != NULL /*&& s_ptr->in != FALSE && s_ptr->type < EMINUS*/)
	{
		g = s_ptr->dw;
		if (s_ptr->dw_t)
				g *= exp(s_ptr->dw_t / tk_x - s_ptr->dw_t / 298.15);
		g *= viscos_0_25 / viscos * tk_x / 298.15;
	}
	else
	{
		g = 0;
	}
	return (g);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
setdiff_c(const char *species_name, double d)
/* ---------------------------------------------------------------------- */
{
	class species *s_ptr;
	LDBLE g;

	s_ptr = s_search(species_name);
	if (s_ptr != NULL)
	{
		s_ptr->dw = d;
		g = s_ptr->dw;
		if (s_ptr->dw_t)
				g *= exp(s_ptr->dw_t / tk_x - s_ptr->dw_t / 298.15);
		g *= viscos_0_25 / viscos * tk_x / 298.15;;
	}
	else
	{
		g = 0;
	}
	return (g);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
calc_SC(void)
/* ---------------------------------------------------------------------- */
{
	//int i;
	//LDBLE lm, a, l_z, Dw, SC, ff;

	//SC = 0;
# ifdef SKIP
	for (i = 0; i < count_species_list; i++)
	{
		if (species_list[i].s->type == EX)
			continue;
		if (species_list[i].s->type == SURF)
			continue;
		if (i > 0
			&& strcmp(species_list[i].s->name,
					  species_list[i - 1].s->name) == 0)
			continue;
		if (species_list[i].s == s_h2o)
			continue;
		if ((Dw = species_list[i].s->dw) == 0)
			continue;
		if ((l_z = fabs(species_list[i].s->z)) == 0)
			continue;

		lm = species_list[i].s->lm;
		if (lm > -9)
		{
			ff = (mu_x < .36 * l_z ? 0.6 / sqrt(l_z) : sqrt(mu_x) / l_z);

			ff *= species_list[i].s->lg;
			if (ff > 0) ff = 0;
			a = under(lm + ff);
			if (species_list[i].s->dw_t)
				Dw *= exp(species_list[i].s->dw_t / tk_x - species_list[i].s->dw_t / 298.15); // the viscosity multiplier is done in SC
			SC += a * l_z * l_z * Dw;
		}
	}
	SC *= 1e7 * F_C_MOL * F_C_MOL / (R_KJ_DEG_MOL * 298160.0);
 /* correct for temperature dependency...
       SC_T = SC_298 * (Dw_T / T) * (298 / Dw_298) and
	     Dw_T = Dw_298 * (T / 298) * (viscos_298 / viscos_T) give:
	 SC_T = SC_298 * (viscos_298 / viscos_T)
 */
	SC *= viscos_0_25 / viscos;

	return (SC);
//# endif
	for (i = 0; i < (int)this->s_x.size(); i++)
	{
		if (s_x[i]->type != AQ && s_x[i]->type != HPLUS)
		  continue;
		if ((Dw = s_x[i]->dw) == 0)
			continue;
		if ((l_z = fabs(s_x[i]->z)) == 0)
			continue;

		lm = s_x[i]->lm;
		if (lm > -9)
		{
			ff = (mu_x < .36 * l_z ? 0.6 / sqrt(l_z) : sqrt(mu_x) / l_z);

			ff *= s_x[i]->lg;
			if (ff > 0) ff = 0;
			a = under(lm + ff);
			if (s_x[i]->dw_t)
				Dw *= exp(s_x[i]->dw_t / tk_x - s_x[i]->dw_t / 298.15); // the viscosity multiplier is done in SC
			SC += a * l_z * l_z * Dw;
		}
	}
	SC *= 1e7 * F_C_MOL * F_C_MOL / (R_KJ_DEG_MOL * 298160.0);
 /* correct for temperature dependency...
       SC_T = SC_298 * (Dw_T / T) * (298 / Dw_298) and
	     Dw_T = Dw_298 * (T / 298) * (viscos_298 / viscos_T) give:
	 SC_T = SC_298 * (viscos_298 / viscos_T)
 */
	SC *= viscos_0_25 / viscos;

	return (SC);
# endif
	int i;
	LDBLE ka, l_z, Dw, ff, sqrt_mu;
	sqrt_mu = sqrt(mu_x);

	SC = 0;
	//LDBLE ta1, ta2, ta3, ta4;
	for (i = 0; i < (int)this->s_x.size(); i++)
	{
		// ** for optimizing, get numbers from -analyt for H+ = H+...
		//if (!strcmp(s_x[i]->name, "H+"))
		//{
		//	ta1 = s_x[i]->logk[2];
		//	ta2 = s_x[i]->logk[3];
		//	ta3 = s_x[i]->logk[4];
		//	ta4 = s_x[i]->logk[5];
		//	break;
		//}
		//
	}
	for (i = 0; i < (int)this->s_x.size(); i++)
	{
		if (s_x[i]->type != AQ && s_x[i]->type != HPLUS)
			continue;
		if ((Dw = s_x[i]->dw) == 0)
		{
			if (correct_Dw)
				Dw = default_Dw;
			else
				continue;
		}
		if ((l_z = fabs(s_x[i]->z)) == 0)
			l_z = 1; // only a 1st approximation for correct_Dw in electrical field
		if (s_x[i]->dw_t)
			Dw *= exp(s_x[i]->dw_t / tk_x - s_x[i]->dw_t / 298.15); // the viscosity multiplier is done in SC
		if (s_x[i]->dw_a2)
			ka = DH_B * s_x[i]->dw_a2 * sqrt_mu / (1 + pow(mu_x, 0.75));
		else
		{
			ka = DH_B * 4.73 * sqrt_mu / (1 + pow(mu_x , 0.75));
			//ka = DH_B * ta1 * sqrt_mu / (1 + pow(mu_x, ta2));
			//ka = DH_B * ta1 * sqrt_mu / (1 + mu_x / ta2);
		}
		if (s_x[i]->dw_a)
		{
			ff = exp(-s_x[i]->dw_a * DH_A * l_z * sqrt_mu / (1 + ka));
			if (print_viscosity)
			{
				ff *= pow((viscos_0 / viscos), s_x[i]->dw_a_visc);
			}
		}
		else
		{
			ff = exp(-1.6 * DH_A * l_z * sqrt_mu / (1 + ka));
			//ff = exp(-ta3 * DH_A * l_z * sqrt_mu / (1 + ka));
		}
		Dw *= ff;
		s_x[i]->dw_corr = Dw;

		if (s_x[i]->z == 0)
			continue;
		s_x[i]->dw_t_SC = s_x[i]->moles / mass_water_aq_x * l_z * l_z * Dw;
		SC += s_x[i]->dw_t_SC;
	}
	SC *= 1e7 * F_C_MOL * F_C_MOL / (R_KJ_DEG_MOL * 298150.0);
	/* correct for temperature dependency...
	SC_T = SC_298 * (Dw_T / T) * (298 / Dw_298) and
	Dw_T = Dw_298 * (T / 298) * (viscos_298 / viscos_T) give:
	SC_T = SC_298 * (viscos_298 / viscos_T)
	*/
	SC *= viscos_0_25 / viscos_0;
	return (SC);
}
#ifdef SKIP
/*Debye-Onsager according to Robinson and Stokes, 1954, JACS 75, 1991, 
     but with sqrt charge multiplier for B2 and mu^ff dependent ka */
	LDBLE q, B1, B2, m_plus, m_min, eq_plus, eq_min, eq_dw_plus, eq_dw_min, Sum_m_dw, z_plus, z_min, t1, t2, Dw_SC;

	m_plus = m_min = eq_plus = eq_min = eq_dw_plus = eq_dw_min = Sum_m_dw = z_plus = z_min = 0;
	SC = 0;
	for (i = 0; i < (int)this->s_x.size(); i++)
	{
		if (s_x[i]->type != AQ && s_x[i]->type != HPLUS)
			continue;
		if ((l_z = s_x[i]->z) == 0)
			continue;
		if ((lm = s_x[i]->lm) < -9)
			continue;
		if ((Dw = s_x[i]->dw) == 0)
			Dw = 1e-9;
		if (s_x[i]->dw_t)
			Dw *= exp(s_x[i]->dw_t / tk_x - s_x[i]->dw_t / 298.15); // the viscosity multiplier cancels in q...
		if (l_z > 0)
		{
			m_plus += s_x[i]->moles;
			t1 = s_x[i]->moles * l_z;
			eq_plus += t1;
			eq_dw_plus += t1 * Dw;
			Sum_m_dw += s_x[i]->moles * Dw;
		}
		else
		{
			m_min += s_x[i]->moles;
			t1 = s_x[i]->moles * l_z;
			eq_min -= t1;
			eq_dw_min -= t1 * Dw;
			Sum_m_dw += s_x[i]->moles * Dw;
		}
	}
	// Falkenhagen, q = (Sum(z1 * m1*Dw1) + Sum(z2 *m2*Dw2)) / ((Sum(m1*Dw1) + Sum(m2*Dw2))(av_z1 + av_z2))
	z_plus = eq_plus / m_plus; // |av_z1|
	z_min = eq_min / m_min;    // |av_z2|
	q = (eq_dw_plus + eq_dw_min) / (Sum_m_dw * (z_min + z_plus));
	t1 = 1.60218e-19 * 1.60218e-19 / (6 * pi);
	B1 = t1  / (2 * 8.8542e-12 * eps_r * 1.38066e-23 * tk_x) *
		q / (1 + sqrt(q)) * DH_B * 1e10 * z_plus * z_min;  // DH_B is per Angstrom (*1e10)
	t2 = viscos_0; // (1 - 0.5) * viscos_0 + 0.5 * viscos;
	B2 = t1 * AVOGADRO / t2 * DH_B * 1e17;  // DH_B per Angstrom (*1e10), viscos in mPa.s (*1e3), B2 in cm2 (*1e4)

	Dw_SC = viscos_0_25 / t2 * 1e4 * F_C_MOL * F_C_MOL / (R_KJ_DEG_MOL * 298160.0);
	for (i = 0; i < (int)this->s_x.size(); i++)
	{
		if (s_x[i]->type != AQ && s_x[i]->type != HPLUS)
			continue;
		if ((l_z = fabs(s_x[i]->z)) == 0)
			continue;
		if ((lm = s_x[i]->lm) < -9)
			continue;
		if ((Dw = s_x[i]->dw) == 0)
			Dw = 1e-9;

		Dw *= Dw_SC;
		if (s_x[i]->dw_t)
			Dw *= exp(s_x[i]->dw_t / tk_x - s_x[i]->dw_t / 298.15); // the viscosity factor is in Dw_SC
		a = (s_x[i]->dw_a ? s_x[i]->dw_a : 3.5);
		ka = DH_B * a;
		if (s_x[i]->dw_a2)
			ka *= pow((double) mu_x, s_x[i]->dw_a2);
		else
			ka *= sqrt_mu;

		// Falkenhagen...
		//SC += under(lm) * l_z * l_z * (Dw - B2 * l_z * sqrt_mu / (1 + ka)) * (1 - B1 * sqrt_mu /
		//		((1 + ka) * (1 + ka * sqrt(q) + ka * ka / 6)));

		t1 = (Dw - (B1 * Dw + B2) *	sqrt_mu / (1 + ka));
		//t1 = (Dw - (B1 * Dw + B2 * sqrt(l_z)) *	sqrt_mu / (1 + ka));
		//t1 = (Dw - (B1 * Dw + B2 * l_z * l_z) *	sqrt_mu / (1 + ka));
		if (t1 > 0)
			SC += under(lm) * l_z * l_z * t1;
	}
	return (SC * 1e3);
#endif

/* VP: Density Start */
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
calc_dens(void)
/* ---------------------------------------------------------------------- */
{
/*
 *   Calculates density based on the formulas and parameters from Millero,
 *   2000.
 *
 *   Millero, F.J. (2000) The equation of state of lakes. Aquatic geochemistry
 *   volume 6, pages 1-17
 */
	int i;
	LDBLE M_T, /*rho_new,*/ gfw;
	/* 2 options: original VP, assign the volumes of species with zero molar volume to their master species,
	              but this doubles counts of complexes with -Vm defined. And, cation-OH and H-anion
				  complexes are counted once. Also, must add H+ and OH-... */
	//class species *s_ptr;

	//V_solutes = M_T = 0.0;
	//for (i = 0; i < count_species_list; i++)
	//{
	//	if (species_list[i].s->type != AQ && species_list[i].s->type != HPLUS)
	//	  continue;

	//	//if (species_list[i].master_s->secondary != NULL)
	//	//	gfw = species_list[i].master_s->secondary->gfw;
	//	//else
	//	//	gfw = species_list[i].master_s->primary->gfw;

	//	/* OH-... */
	//	if (!strcmp(species_list[i].s->name, "OH-"))
	//		s_ptr = s_search("OH-");
	//	else if (species_list[i].s->logk[vm_tc] == 0)
	//	{
	//		s_ptr = species_list[i].master_s;
	//		if (s_ptr->secondary)
	//			gfw = s_ptr->secondary->gfw;
	//		else
	//			gfw = s_ptr->primary->gfw;
	//	}
	//	else
	//	{
	//		s_ptr = species_list[i].s;
	//		compute_gfw(species_list[i].s->name, &gfw);
	//	}

	//	/* Special case: CO3-2 species */
	//	if (strcmp(s_ptr->name, "CO3-2") == 0)
	//	{
	//		if (strstr(species_list[i].s->name, "HCO3") != NULL)
	//		{
	//			s_ptr = s_search("HCO3-");
	//		} else if (strstr(species_list[i].s->name, "CO3") != NULL)
	//		{
	//			compute_gfw("CO3-2", &gfw);
	//		}
	//	}
	//	if (!gfw || !strcmp(species_list[i].s->name, "CO2")) /* CO2, H+ and OH- */
	//		compute_gfw(species_list[i].s->name, &gfw);

	//	M_T += (species_list[i].s->moles * gfw);
	//	V_solutes += species_list[i].s->moles * s_ptr->logk[vm_tc];
	//}

	/* 2nd option, use species_x, vm = 0 for complexes with undefined volume... */
	V_solutes = M_T = 0.0;
	for (i = 0; i < (int)this->s_x.size(); i++)
	{
		if (s_x[i]->type != AQ && s_x[i]->type != HPLUS)
		  continue;

		//compute_gfw(s_x[i]->name, &gfw);
		gfw = s_x[i]->gfw;
		M_T += s_x[i]->moles * gfw;
		V_solutes += s_x[i]->moles * s_x[i]->logk[vm_tc];
	}
	/* If pure water then return rho_0 */
	if (M_T == 0)
		return rho_0;
	else
		return rho_0 * (1e3 + M_T / mass_water_aq_x) / (rho_0 * V_solutes / mass_water_aq_x + 1e3);

	//M_T /= 1e3;
	//solution_mass =  mass_water_aq_x + M_T;
	//V_solutes = M_T - rho_0 * V_solutes / 1e3;

	//rho_new = halve(f_rho, 0.5, 2.0, 1e-7);
	//if (!PHR_ISFINITE(rho_new) || rho_new > 1.99999) rho_new = 1.99999;

	//return rho_new;
}
/* VP: Density End */
/* DP: Function for interval halving */

LDBLE Phreeqc::
f_rho(LDBLE rho_old, void *cookie)
/* ---------------------------------------------------------------------- */
{
	LDBLE rho = 1.0;
	Phreeqc * pThis;

	pThis = (Phreeqc *) cookie;

	pThis->solution_volume = pThis->solution_mass / rho_old;
	if (pThis->solution_volume != 0)
	{
		rho = pThis->V_solutes / pThis->solution_volume;
	}
	rho = rho + pThis->rho_0;
	return (rho - rho_old);
}

/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
calc_solution_volume(void)
/* ---------------------------------------------------------------------- */
{
/*
 *   Calculates solution volume based on sum of mass of element plus density
 */
	LDBLE total_mass = 0;
	LDBLE gfw;
	//compute_gfw("H", &gfw);
	gfw = s_hplus->primary->gfw;
	total_mass = total_h_x * gfw;
	//compute_gfw("O", &gfw);
	gfw = s_h2o->primary->gfw;
	total_mass += total_o_x * gfw;

	for (int i = 0; i < (int)master.size(); i++)
	{
		if (master[i]->s->type != AQ) continue;
		class master *master_ptr = master[i];
		if (master_ptr->primary == TRUE && strcmp(master_ptr->elt->name, "Alkalinity"))
		{
			total_mass += master_ptr->total_primary * master_ptr->elt->gfw; 
		}
	}
	LDBLE rho = calc_dens();
	LDBLE vol = 1e-3 * total_mass / rho;
	return (vol);
}

/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
calc_logk_n(const char *name)
/* ---------------------------------------------------------------------- */
{
	char token[MAX_LENGTH];
	int i;
	LDBLE lk;
	class logk *logk_ptr;
	LDBLE l_logk[MAX_LOG_K_INDICES];
	class name_coef add_logk;
	std::vector<class name_coef> add_logk_v;

	for (i = 0; i < MAX_LOG_K_INDICES; i++)
	{
		l_logk[i] = 0.0;
	}
	strcpy(token, name);
	logk_ptr = logk_search(token);
	if (logk_ptr != NULL)
	{
		add_logk.name = token;
		add_logk.coef = 1.0;
		add_logk_v.push_back(add_logk);
		add_other_logk(l_logk, add_logk_v);
	lk = k_calc(l_logk, tk_x, patm_x * PASCAL_PER_ATM);
		return (lk);
	}
	return (-999.99);
}

/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
calc_logk_p(const char *name)
/* ---------------------------------------------------------------------- */
{
	int i, j;
	char token[MAX_LENGTH];
	class phase *phase_ptr;
	LDBLE lk=-999.9;
	LDBLE l_logk[MAX_LOG_K_INDICES];

	strcpy(token, name);
	phase_ptr = phase_bsearch(token, &j, FALSE);

	if (phase_ptr != NULL)
	{		
		CReaction* reaction_ptr;
		if (phase_ptr->replaced)
			reaction_ptr = &phase_ptr->rxn_s;
		else
			reaction_ptr = &phase_ptr->rxn;
		/*
		*   Print saturation index
		*/
		reaction_ptr->logk[delta_v] = calc_delta_v(*reaction_ptr, true) -
			phase_ptr->logk[vm0];
		if (reaction_ptr->logk[delta_v])
			mu_terms_in_logk = true;
		for (i = 0; i < MAX_LOG_K_INDICES; i++)
		{
			l_logk[i] = 0.0;
		}
		//lk = k_calc(reaction_ptr->logk, tk_x, patm_x * PASCAL_PER_ATM);
		select_log_k_expression(reaction_ptr->logk, l_logk);
		add_other_logk(l_logk, phase_ptr->add_logk); 
		lk = k_calc(l_logk, tk_x, patm_x * PASCAL_PER_ATM);
	}
	return (lk);
}

/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
calc_logk_s(const char *name)
/* ---------------------------------------------------------------------- */
{
	int i;
	char token[MAX_LENGTH];
	class species *s_ptr;
	LDBLE lk, l_logk[MAX_LOG_K_INDICES];

	strcpy(token, name);
	s_ptr = s_search(token);
	if (s_ptr != NULL)
	{
		//if (s_ptr->logk[vm_tc])
		/* calculate delta_v for the reaction... */
			s_ptr->logk[delta_v] = calc_delta_v(*&s_ptr->rxn, false);
		for (i = 0; i < MAX_LOG_K_INDICES; i++)
		{
			l_logk[i] = 0.0;
		}
		select_log_k_expression(s_ptr->logk, l_logk);
		mu_terms_in_logk = true;
		add_other_logk(l_logk, s_ptr->add_logk);
		lk = k_calc(l_logk, tk_x, patm_x * PASCAL_PER_ATM);
		return (lk);
	}
	return (-999.99);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
dh_a0(const char* name)
/* ---------------------------------------------------------------------- */
{
	char token[MAX_LENGTH];
	class species* s_ptr;
	double a = -999.99;

	strcpy(token, name);
	s_ptr = s_search(token);
	if (s_ptr != NULL)
	{
		a = s_ptr->dha;
	}
	return (a);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
dh_bdot(const char* name)
/* ---------------------------------------------------------------------- */
{
	char token[MAX_LENGTH];
	class species* s_ptr;
	double b = -999.99;
	if (llnl_temp.size() > 0)
	{
		b = bdot_llnl;
	}
	else
	{
		strcpy(token, name);
		s_ptr = s_search(token);
		if (s_ptr != NULL)
		{
			b = s_ptr->dhb;
		}
	}
	return (b);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
calc_deltah_p(const char* name)
/* ---------------------------------------------------------------------- */
{
	int i, j;
	char token[MAX_LENGTH];
	class phase* phase_ptr;
	LDBLE lkm, lkp;
	LDBLE l_logk[MAX_LOG_K_INDICES];
	double dh = -999.99;
	strcpy(token, name);
	phase_ptr = phase_bsearch(token, &j, FALSE);

	if (phase_ptr != NULL)
	{
		CReaction* reaction_ptr;
		if (phase_ptr->replaced)
			reaction_ptr = &phase_ptr->rxn_s;
		else
			reaction_ptr = &phase_ptr->rxn;
		/*
		*   Print saturation index
		*/
		reaction_ptr->logk[delta_v] = calc_delta_v(*reaction_ptr, true) -
			phase_ptr->logk[vm0];
		if (reaction_ptr->logk[delta_v])
			mu_terms_in_logk = true;
		for (i = 0; i < MAX_LOG_K_INDICES; i++)
		{
			l_logk[i] = 0.0;
		}
		//lk = k_calc(reaction_ptr->logk, tk_x, patm_x * PASCAL_PER_ATM);
		select_log_k_expression(reaction_ptr->logk, l_logk);
		add_other_logk(l_logk, phase_ptr->add_logk);
		lkm = k_calc(l_logk, tk_x - 1.0, patm_x * PASCAL_PER_ATM);
		lkp = k_calc(l_logk, tk_x + 1.0, patm_x * PASCAL_PER_ATM);
		dh = (lkp - lkm) / 2.0 * LOG_10 * R_KJ_DEG_MOL * pow(tk_x, 2.0);
	}
	return (dh);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
calc_deltah_s(const char* name)
/* ---------------------------------------------------------------------- */
{
	int i;
	char token[MAX_LENGTH];
	class species* s_ptr;
	LDBLE lkm, lkp, l_logk[MAX_LOG_K_INDICES];
	double dh = -999.99;
	strcpy(token, name);
	s_ptr = s_search(token);
	if (s_ptr != NULL)
	{
		/* calculate delta_v for the reaction... */
		s_ptr->logk[delta_v] = calc_delta_v(*&s_ptr->rxn, false);
		for (i = 0; i < MAX_LOG_K_INDICES; i++)
		{
			l_logk[i] = 0.0;
		}
		select_log_k_expression(s_ptr->logk, l_logk);
		mu_terms_in_logk = true;
		add_other_logk(l_logk, s_ptr->add_logk);
		lkm = k_calc(l_logk, tk_x-1.0, patm_x * PASCAL_PER_ATM);
		lkp = k_calc(l_logk, tk_x + 1.0, patm_x * PASCAL_PER_ATM);
		dh = (lkp - lkm) / 2.0 * LOG_10 * R_KJ_DEG_MOL * pow(tk_x,2.0);
		return (dh);
	}
	return (0.0);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
calc_surface_charge(const char *surface_name)
/* ---------------------------------------------------------------------- */
{
	char token[MAX_LENGTH], token1[MAX_LENGTH];
	const char* cptr;
	int i, j, k;
	LDBLE charge;
	class rxn_token_temp *token_ptr;
	class master *master_ptr;
	/*
	 *   Go through species, sum charge
	 */
	charge = 0;
	for (k = 0; k < (int)this->s_x.size(); k++)
	{
		if (s_x[k]->type != SURF)
			continue;
		/*
		 *   Match surface_name
		 */
		count_trxn = 0;
		trxn_add(s_x[k]->rxn_s, 1.0, false);	/* rxn_s is set in tidy_model */
		for (i = 1; i < count_trxn; i++)
		{
			token_ptr = &(trxn.token[i]);
			if (token_ptr->s->type != SURF)
				continue;
			master_ptr = trxn.token[i].s->primary;
			strcpy(token, master_ptr->elt->name);
			replace("_", " ", token);
			cptr = token;
			copy_token(token1, &cptr, &j);
			if (strcmp(surface_name, token1) == 0)
			{
				charge += s_x[k]->moles * s_x[k]->z;
			}
		}
	}
	return (charge);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
diff_layer_total(const char *total_name, const char *surface_name)
/* ---------------------------------------------------------------------- */
{
/*
 *   Provides total moles in DDL layer
 */
	cxxSurfaceCharge *surface_charge_ptr1;
	std::string name, token, surface_name_local;
	class master *master_ptr;

	LDBLE mass_water_surface;
	LDBLE molality, moles_excess, moles_surface, charge;

	if (use.Get_surface_ptr() == NULL || (dl_type_x == cxxSurface::NO_DL &&
									strcmp_nocase("psi", total_name) != 0 &&
									strcmp_nocase("psi1", total_name) != 0 &&
									strcmp_nocase("psi2", total_name) != 0 &&
									strcmp_nocase("charge", total_name) != 0
									&& strcmp_nocase("charge1",
													 total_name) != 0
									&& strcmp_nocase("charge2",
													 total_name) != 0
									&& strcmp_nocase("sigma",
													 total_name) != 0
									&& strcmp_nocase("sigma1",
													 total_name) != 0
									&& strcmp_nocase("sigma2",
													 total_name) != 0))
		return (0);

/*
 *   Find surface...
 */
	int j;
	for (j = 0; j < count_unknowns; j++)
	{
		if (use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM)
		{
			if (x[j]->type != SURFACE_CB)
				continue;
			name = x[j]->master[0]->elt->name;
			Utilities::replace("_psi", "", name);
		}
		else if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC)
		{
			if (x[j]->type != SURFACE_CB)
				continue;
			name = x[j]->master[0]->elt->name;
			Utilities::replace("_psi", "", name);
		}
		else
		{
			if (x[j]->type != SURFACE)
				continue;
			token =  x[j]->master[0]->elt->name;
			Utilities::replace("_", " ", token);
			std::string::iterator b = token.begin();
			std::string::iterator e = token.end();
			CParser::copy_token(name, b, e);
		}
		if (surface_name != NULL)
		{
			if (strcmp(name.c_str(), surface_name) == 0)
				break;
		}
		else
		{
			break;
		}
	}
	if (j >= count_unknowns)
		return (0);
	surface_name_local = name;
	/*
	 *   Psi, charge, sigma
	 */
	if (strcmp_nocase("psi", total_name) == 0)
	{
		if (use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM)
		{
			return ((LDBLE) (x[j]->master[0]->s->la * 2 * R_KJ_DEG_MOL *
							 tk_x * LOG_10 / F_KJ_V_EQ));
		}
		else if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC)
		{
			master_ptr = surface_get_psi_master(surface_name, SURF_PSI);
			if (master_ptr != NULL)
			{
				return ((LDBLE)
						(-master_ptr->s->la * R_KJ_DEG_MOL * tk_x * LOG_10 /
						 F_KJ_V_EQ));
			}
			else
			{
				return (0.0);
			}
		}
		else
		{
			return (0);
		}
	}
	else if (strcmp_nocase("psi1", total_name) == 0)
	{
		master_ptr = surface_get_psi_master(surface_name, SURF_PSI1);
		if (master_ptr != NULL)
		{
			return ((LDBLE)
					(-master_ptr->s->la * R_KJ_DEG_MOL * tk_x * LOG_10 /
					 F_KJ_V_EQ));
		}
		else
		{
			return (0.0);
		}
	}
	else if (strcmp_nocase("psi2", total_name) == 0)
	{
		master_ptr = surface_get_psi_master(surface_name, SURF_PSI2);
		if (master_ptr != NULL)
		{
			return ((LDBLE)
					(-master_ptr->s->la * R_KJ_DEG_MOL * tk_x * LOG_10 /
					 F_KJ_V_EQ));
		}
		else
		{
			return (0.0);
		}
	}
	else if (strcmp_nocase("charge", total_name) == 0)
	{
		if ((use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM) && dl_type_x == cxxSurface::NO_DL)
		{
			return ((LDBLE) (x[j]->f));
		}
		else if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC)
		{
			cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge);
			return ((charge_ptr->Get_sigma0() *
					(charge_ptr->Get_specific_area() *
					 charge_ptr->Get_grams()) / F_C_MOL));
		}
		else
		{
			return (calc_surface_charge(surface_name_local.c_str()));
		}
	}
	else if (strcmp_nocase("charge1", total_name) == 0)
	{
		if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC)
		{
			cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge);
			return ((charge_ptr->Get_sigma1() *
					(charge_ptr->Get_specific_area() *
					 charge_ptr->Get_grams()) / F_C_MOL));
		}
		else
		{
			return (0);
		}
	}
	else if (strcmp_nocase("charge2", total_name) == 0)
	{
		if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC)
		{
			cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge);
			return ((charge_ptr->Get_sigma2() *
					(charge_ptr->Get_specific_area() *
					 charge_ptr->Get_grams()) / F_C_MOL));
		}
		else
		{
			return (0);
		}
	}
	else if (strcmp_nocase("sigma", total_name) == 0)
	{
		if (use.Get_surface_ptr()->Get_type() == cxxSurface::DDL || use.Get_surface_ptr()->Get_type() == cxxSurface::CCM)
		{
			cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge);
			if (dl_type_x != cxxSurface::NO_DL)
			{
				charge = calc_surface_charge(surface_name_local.c_str());
			}
			else
			{
				charge = x[j]->f;
			}
			if ((charge_ptr->Get_specific_area() *
				 charge_ptr->Get_grams()) > 0)
			{
				return ((charge * F_C_MOL /
						 (charge_ptr->Get_specific_area() *
						  charge_ptr->Get_grams())));
			}
			else
			{
				return (0);
			}
		}
		else if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC)
		{
			cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge);
			return ((LDBLE) (charge_ptr->Get_sigma0()));
		}
		else
		{
			return (0);
		}
	}
	else if (strcmp_nocase("sigma1", total_name) == 0)
	{
		if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC)
		{
			cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge);
			return ((LDBLE) (charge_ptr->Get_sigma1()));
		}
		else
		{
			return (0);
		}
	}
	else if (strcmp_nocase("sigma2", total_name) == 0)
	{
		if (use.Get_surface_ptr()->Get_type() == cxxSurface::CD_MUSIC)
		{
			cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge);
			return ((LDBLE) (charge_ptr->Get_sigma2()));
		}
		else
		{
			return (0);
		}
	}
	else if (strcmp_nocase("water", total_name) == 0)
	{
		if (dl_type_x != cxxSurface::NO_DL)
		{
			cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge);
			return (charge_ptr->Get_mass_water());
		}
		else
		{
			return (0);
		}
	}
/*
 *   find total moles of each element in diffuse layer...
 */
	surface_charge_ptr1 = use.Get_surface_ptr()->Find_charge(x[j]->surface_charge);
	if (surface_charge_ptr1)
	{
		mass_water_surface = surface_charge_ptr1->Get_mass_water();
		count_elts = 0;
		paren_count = 0;
		for (j = 0; j < (int)this->s_x.size(); j++)
		{
			if (s_x[j]->type > HPLUS)
				continue;
			molality = under(s_x[j]->lm);
			LDBLE g = surface_charge_ptr1->Get_g_map()[s_x[j]->z].Get_g();

			moles_excess = mass_water_aq_x * molality * (g * s_x[j]->erm_ddl +
										  mass_water_surface /
										  mass_water_aq_x * (s_x[j]->erm_ddl - 1));
			moles_surface = mass_water_surface * molality + moles_excess;
/*
 *   Accumulate elements in diffuse layer
 */
			add_elt_list(s_x[j]->next_elt, moles_surface);
		}
		elt_list_combine();
/*
 *   Return totals
 */
		for (j = 0; j < count_elts; j++)
		{
			if (strcmp(elt_list[j].elt->name, total_name) == 0)
			{
				return ((LDBLE) elt_list[j].coef);
			}
		}
	}
	return (0);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
calc_t_sc(const char *name)
/* ---------------------------------------------------------------------- */
{
	char token[MAX_LENGTH];
	class species *s_ptr;

	strcpy(token, name);
	s_ptr = s_search(token);
	if (s_ptr != NULL)
	{
		if (!s_ptr->z)
			return (0);
		calc_SC();
		if (!SC)
			return (0);
		LDBLE t = s_ptr->dw_t_SC * 1e7 * F_C_MOL * F_C_MOL / (R_KJ_DEG_MOL * 298150.0) * viscos_0_25 / viscos_0;
		return (t / SC);
	}
	return (-999.99);
}

/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
equi_phase(const char *phase_name)
/* ---------------------------------------------------------------------- */
{
	int j;

	if (use.Get_pp_assemblage_in() == FALSE || use.Get_pp_assemblage_ptr() == NULL)
		return (0);
	for (j = 0; j < count_unknowns; j++)
	{
		if (x[j]->type != PP)
			continue;
		if (strcmp_nocase(x[j]->pp_assemblage_comp_name, phase_name) == 0)
		{
			break;
		}
	}
/*
 *   Print pure phase assemblage data
 */
	cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr();
	if (j == count_unknowns)
	{
		/* if not an unknown */
		std::map<std::string, cxxPPassemblageComp>::iterator it;
		it =  pp_assemblage_ptr->Get_pp_assemblage_comps().begin();
		for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++)
		{
			if (strcmp_nocase
				(it->second.Get_name().c_str(), phase_name) == 0)
			{
				return (it->second.Get_moles());
			}
		}
	}
	else
	{
		/* if an unknown */
		if (x[j]->moles < 0.0)
			x[j]->moles = 0.0;
		return (x[j]->moles);
	}
	return (0);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
equi_phase_delta(const char *phase_name)
/* ---------------------------------------------------------------------- */
{
	int j;

	if (use.Get_pp_assemblage_in() == FALSE || use.Get_pp_assemblage_ptr() == NULL)
		return (0);
	for (j = 0; j < count_unknowns; j++)
	{
		if (x[j]->type != PP)
			continue;
		if (strcmp_nocase(x[j]->pp_assemblage_comp_name, phase_name) == 0)
		{
			break;
		}
	}
/*
 *   Print pure phase assemblage data
 */
	cxxPPassemblage * pp_assemblage_ptr = use.Get_pp_assemblage_ptr();
	if (j == count_unknowns)
	{
		/* if not an unknown */
		std::map<std::string, cxxPPassemblageComp>::iterator it;
		it =  pp_assemblage_ptr->Get_pp_assemblage_comps().begin();
		for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++)
		{
			if (strcmp_nocase
				(it->second.Get_name().c_str(), phase_name) == 0)
			{
				cxxPPassemblageComp * comp_ptr = &(it->second);
				if (state != TRANSPORT && state != PHAST)
				{
					//LDBLE moles = it->second.Get_moles();
					//LDBLE delta_moles = moles - comp_ptr->Get_moles() -
					//	comp_ptr->Get_delta();
					return(0);
				}
				else
				{
					LDBLE moles = it->second.Get_moles();
					LDBLE delta_moles =	moles - comp_ptr->Get_initial_moles();
					return(delta_moles);
				}
			}
		}
	}
	else
	{
		//cxxPPassemblageComp * comp_ptr = pp_assemblage_ptr->Find(x[j]->pp_assemblage_comp_name);
		cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp *) x[j]->pp_assemblage_comp_ptr;
		if (state != TRANSPORT && state != PHAST)
		{
			LDBLE delta_moles =
				x[j]->moles - comp_ptr->Get_moles() -
				comp_ptr->Get_delta();
			return(delta_moles);
		}
		else
		{
			LDBLE delta_moles =
				x[j]->moles - comp_ptr->Get_initial_moles();
			return(delta_moles);
		}
	}
	return (0);
}

/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
equivalent_fraction(const char *name, LDBLE *eq, std::string &elt_name)
/* ---------------------------------------------------------------------- */
{
	class species *s_ptr = s_search(name);
	*eq = 0;
	elt_name.clear();
	LDBLE f = 0;
	if (s_ptr != NULL && (s_ptr->type == EX || s_ptr->type == SURF))
	{
		*eq = s_ptr->equiv;
		const class elt_list *next_elt;
		LDBLE tot=0.0;
		for (next_elt = &s_ptr->next_elt[0]; next_elt->elt != NULL;	next_elt++)
		{
			if (next_elt->elt->master->s->type == SURF ||
				next_elt->elt->master->s->type == EX)
			{
				tot = total_mole(next_elt->elt->name);
				elt_name = next_elt->elt->name;
			}
		}
		if (s_ptr->in == TRUE && tot > 0.0)
		{
			f = s_ptr->moles * s_ptr->equiv / tot; 
		}
	}
	return f;
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
find_gas_comp(const char *gas_comp_name)
/* ---------------------------------------------------------------------- */
{
	int i;

	if (use.Get_gas_phase_in() == FALSE || use.Get_gas_phase_ptr() == NULL)
		return (0);
	cxxGasPhase * gas_phase_ptr = use.Get_gas_phase_ptr();
	for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++)
	{
		if (strcmp_nocase(gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str(), gas_comp_name) == 0)
		{
			class phase *phase_ptr = phase_bsearch(gas_comp_name, &i, false);
			if (phase_ptr)
			{
				return (phase_ptr->moles_x);
			}
		}
	}
	return (0);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
find_gas_p(void)
/* ---------------------------------------------------------------------- */
{
	if (use.Get_gas_phase_in() == FALSE || use.Get_gas_phase_ptr() == NULL)
		return (0);
	cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr();
	if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE)
	{
		if (gas_unknown == NULL)
			return (0);
		if (gas_unknown->moles < 1e-12)
			return (0);
	}
	return (gas_phase_ptr->Get_total_p());
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
find_gas_vm(void)
/* ---------------------------------------------------------------------- */
{
	if (use.Get_gas_phase_in() == FALSE || use.Get_gas_phase_ptr() == NULL)
		return (0);
	cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr();
	if (gas_phase_ptr->Get_type() == cxxGasPhase::GP_PRESSURE)
	{
		if (gas_unknown == NULL)
			return (0);
		if (gas_unknown->moles < 1e-12)
			return (0);
		gas_phase_ptr->Set_total_moles(gas_unknown->moles);
		gas_phase_ptr->Set_volume(gas_phase_ptr->Get_total_moles() * R_LITER_ATM * tk_x /
			gas_phase_ptr->Get_total_p());
		if (gas_phase_ptr->Get_v_m() >= 0.01)
			gas_phase_ptr->Set_volume(gas_phase_ptr->Get_v_m() * gas_unknown->moles);
	}
	return gas_phase_ptr->Get_volume() / gas_phase_ptr->Get_total_moles();
}

/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
find_misc1(const char *ss_name)
/* ---------------------------------------------------------------------- */
{
	if (use.Get_ss_assemblage_in() == FALSE || use.Get_ss_assemblage_ptr() == NULL)
		return (0.0);
	std::vector<cxxSS *> ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize();
	for (size_t j = 0; j < ss_ptrs.size(); j++)
	{
		cxxSS *ss_ptr = ss_ptrs[j];
		if (strcmp_nocase(ss_ptr->Get_name().c_str(), ss_name) == 0)
		{
			if (ss_ptr->Get_miscibility())
			{
				return (ss_ptr->Get_xb1());
			}
			else
			{
				return (1.0);
			}
		}
	}
	return (0);
}

/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
find_misc2(const char *ss_name)
/* ---------------------------------------------------------------------- */
{
	if (use.Get_ss_assemblage_in() == FALSE || use.Get_ss_assemblage_ptr() == NULL)
		return (0.0);
	std::vector<cxxSS *> ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize();
	for (size_t j = 0; j < ss_ptrs.size(); j++)
	{
		cxxSS *ss_ptr = ss_ptrs[j];
		if (strcmp_nocase(ss_ptr->Get_name().c_str(), ss_name) == 0)
		{
			if (ss_ptr->Get_miscibility())
			{
				return (ss_ptr->Get_xb2());
			}
			else
			{
				return (1.0);
			}
		}
	}
	return (0);
}

/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
find_ss_comp(const char *ss_comp_name)
/* ---------------------------------------------------------------------- */
{
	if (use.Get_ss_assemblage_in() == FALSE || use.Get_ss_assemblage_ptr() == NULL)
		return (0);

	std::vector<cxxSS *> ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize();
	for (size_t j = 0; j < ss_ptrs.size(); j++)
	{
		cxxSS *ss_ptr = ss_ptrs[j];
		for (size_t i = 0; i < ss_ptr->Get_ss_comps().size(); i++)
		{
			cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[i]);
			if (strcmp_nocase(comp_ptr->Get_name().c_str(), ss_comp_name) == 0)
			{
				if (ss_ptr->Get_ss_in())
				{
					return (comp_ptr->Get_moles());
				}
				else
				{
					return (0.0);
				}
			}
		}
	}
	return (0);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
get_calculate_value(const char *name)
/* ---------------------------------------------------------------------- */
/*
 *   Gets value from a calclate_value structure
 *	  arguments: name
 *	  return: LDBLE of value
 */
{
	class calculate_value *calculate_value_ptr;
	calculate_value_ptr = calculate_value_search(name);
	if (calculate_value_ptr == NULL)
	{
		error_string = sformatf( "CALC_VALUE Basic function, %s not found.",
				name);
		//error_msg(error_string, CONTINUE);
		//input_error++;
		warning_msg(error_string);
		return (MISSING);
	}
	if (name == NULL)
	{
		error_string = sformatf(
				"Definition for calculated value not found, %s", name);
		input_error++;
		error_msg(error_string, CONTINUE);
		return (MISSING);
	}

	char l_command[] = "run";
	PBasic interp(this, this->phrq_io);
	if (calculate_value_ptr->new_def == TRUE)
	{
		if (interp.basic_compile
			(calculate_value_ptr->commands.c_str(), 
			&calculate_value_ptr->linebase,
			&calculate_value_ptr->varbase,
			&calculate_value_ptr->loopbase) != 0)
		{
			error_string = sformatf(
					"Fatal Basic error in CALCULATE_VALUES %s.",
					calculate_value_ptr->name);
			error_msg(error_string, STOP);
		}
		calculate_value_ptr->new_def = FALSE;
	}

	if (interp.basic_run(l_command, 
		calculate_value_ptr->linebase,
		calculate_value_ptr->varbase, 
		calculate_value_ptr->loopbase) != 0)
	{
		error_string = sformatf( "Fatal Basic error in calculate_value %s.",
				calculate_value_ptr->name);
		error_msg(error_string, STOP);
	}
#ifdef NPP
	if (isnan(rate_moles))
#else
	if (rate_moles == NAN)
#endif
	{
		error_string = sformatf( "Calculated value not SAVEed for %s.",
				calculate_value_ptr->name);
		error_msg(error_string, STOP);
	}
	else
	{
		calculate_value_ptr->calculated = TRUE;
		calculate_value_ptr->value = rate_moles;
	}
	return (calculate_value_ptr->value);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
kinetics_moles(const char *kinetics_name)
/* ---------------------------------------------------------------------- */
{

	if (use.Get_kinetics_in() == FALSE || use.Get_kinetics_ptr() == NULL)
		return (0);
	for (size_t i = 0; i < use.Get_kinetics_ptr()->Get_kinetics_comps().size(); i++)
	{
		cxxKineticsComp *kinetics_comp_ptr = &(use.Get_kinetics_ptr()->Get_kinetics_comps()[i]);
		if (strcmp_nocase
			(kinetics_comp_ptr->Get_rate_name().c_str(), kinetics_name) == 0)
		{
			return (kinetics_comp_ptr->Get_m());
		}
	}

	error_string = sformatf( "No data for rate %s in KINETICS keyword.",
			kinetics_name);
	//if (count_warnings >= 0) // appt debug cvode
	//	warning_msg(error_string);
	return (0);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
kinetics_moles_delta(const char *kinetics_name)
/* ---------------------------------------------------------------------- */
{

	if (use.Get_kinetics_in() == FALSE || use.Get_kinetics_ptr() == NULL)
		return (0);
	for (size_t i = 0; i < use.Get_kinetics_ptr()->Get_kinetics_comps().size(); i++)
	{
		cxxKineticsComp *kinetics_comp_ptr = &(use.Get_kinetics_ptr()->Get_kinetics_comps()[i]);
		if (strcmp_nocase
			(kinetics_comp_ptr->Get_rate_name().c_str(), kinetics_name) == 0)
		{
			//return (kinetics_comp_ptr->Get_m());

			if (state != TRANSPORT && state != PHAST)
			{
				//LDBLE moles = kinetics_comp_ptr->Get_m();
				LDBLE delta_moles = - kinetics_comp_ptr->Get_moles();
				return delta_moles;
			}
			else
			{
				//moles =  kinetics_comp_ptr->Get_m();
				LDBLE delta_moles =
						kinetics_comp_ptr->Get_m() -
						kinetics_comp_ptr->Get_initial_moles();
				return delta_moles;
			}
		}
	}

	//error_string = sformatf( "No data for rate %s in KINETICS keyword.",
	//		kinetics_name);
	//warning_msg(error_string);
	return (0);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
log_activity(const char *species_name)
/* ---------------------------------------------------------------------- */
{
	class species *s_ptr;
	LDBLE la;

	s_ptr = s_search(species_name);

	if (s_ptr == s_eminus)
	{
		la = s_eminus->la;
	}
	else if (s_ptr == NULL || s_ptr->in == FALSE)
	{
		la = -99.99;
	}
	else if (s_ptr == s_h2o)
	{
		la = s_h2o->la;
	}
	else
	{
		la = s_ptr->lm + s_ptr->lg;
	}
	return (la);
}

/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
log_molality(const char *species_name)
/* ---------------------------------------------------------------------- */
{
	class species *s_ptr;
	LDBLE lm;

	s_ptr = s_search(species_name);

	if (s_ptr == s_eminus)
	{
		lm = -99.99;
	}
	else if (s_ptr == NULL || s_ptr->in == FALSE)
	{
		lm = -99.99;
	}
	else if (s_ptr == s_h2o)
	{
		lm = log10(s_ptr->moles / mass_water_aq_x);
	}
	else
	{
		lm = s_ptr->lm;
	}
	return (lm);
}

/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
molality(const char *species_name)
/* ---------------------------------------------------------------------- */
{
	class species *s_ptr;
	LDBLE m;

	s_ptr = s_search(species_name);
	if (s_ptr == NULL || s_ptr == s_eminus || s_ptr->in == FALSE)
	{
		m = 1e-99;
	}
	else
	{
		/* m = pow(10., s_ptr->lm); */
		m = s_ptr->moles / mass_water_aq_x;
	}
	return (m);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
pr_pressure(const char *phase_name)
/* ---------------------------------------------------------------------- */
{
	class phase *phase_ptr;
	int l;

	phase_ptr = phase_bsearch(phase_name, &l, FALSE);
	if (phase_ptr == NULL)
	{
		error_string = sformatf( "Gas %s, not found.", phase_name);
		warning_msg(error_string);
		return (1e-99);
	}
	else if (phase_ptr->in != FALSE && phase_ptr->pr_in)
	{
		return phase_ptr->pr_p;
	}
	return (0.0);
}

/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
pressure(void)
/* ---------------------------------------------------------------------- */
{
	return (patm_x);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
pr_phi(const char *phase_name)
/* ---------------------------------------------------------------------- */
{
	class phase *phase_ptr;
	int l;

	phase_ptr = phase_bsearch(phase_name, &l, FALSE);
	if (phase_ptr == NULL)
	{
		error_string = sformatf( "Gas %s, not found.", phase_name);
		warning_msg(error_string);
		return (1e-99);
	}
	else if (phase_ptr->in != FALSE && phase_ptr->pr_in)
	{
		return phase_ptr->pr_phi;
	}
	return (1.0);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
saturation_ratio(const char *phase_name)
/* ---------------------------------------------------------------------- */
{
	class rxn_token *rxn_ptr;
	class phase *phase_ptr;
	int l;
	LDBLE si, iap;

	iap = 0.0;
	phase_ptr = phase_bsearch(phase_name, &l, FALSE);
	if (phase_ptr == NULL)
	{
		error_string = sformatf( "Mineral %s, not found.", phase_name);
		warning_msg(error_string);
		return (1e-99);
	}
	else if (phase_ptr->in != FALSE)
	{
		for (rxn_ptr = &phase_ptr->rxn_x.token[0] + 1; rxn_ptr->s != NULL;
			 rxn_ptr++)
		{
			iap += rxn_ptr->s->la * rxn_ptr->coef;
		}
		si = iap - phase_ptr->lk;
		return (pow((LDBLE) 10.0, si));
	}
	return (0.0);

}

/* ---------------------------------------------------------------------- */
int Phreeqc::
saturation_index(const char *phase_name, LDBLE * iap, LDBLE * si)
/* ---------------------------------------------------------------------- */
{
	class rxn_token *rxn_ptr;
	class phase *phase_ptr;
	int l;

	*si = -99.99;
	*iap = 0.0;
	phase_ptr = phase_bsearch(phase_name, &l, FALSE);
	if (phase_ptr == NULL)
	{
		error_string = sformatf( "Mineral %s, not found.", phase_name);
		warning_msg(error_string);
		*si = -99;
	}
	else if (phase_ptr->in != FALSE)
	{
		for (rxn_ptr = &phase_ptr->rxn_x.token[0] + 1; rxn_ptr->s != NULL;
			 rxn_ptr++)
		{
			*iap += rxn_ptr->s->la * rxn_ptr->coef;
		}
		*si = *iap - phase_ptr->lk;
	}
	else
	{
		return (ERROR);
	}
	return (OK);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
sum_match_gases(const char *mytemplate, const char *name)
/* ---------------------------------------------------------------------- */
{
	int i;
	LDBLE tot;
	const class elt_list *next_elt;

	if (use.Get_gas_phase_in() == FALSE || use.Get_gas_phase_ptr() == NULL)
		return (0);
	cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr();
	tot = 0;
	for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++)
	{
		class phase * phase_ptr = phase_bsearch(gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str(),
			&i, FALSE);
		if (match_elts_in_species(phase_ptr->formula, mytemplate) == TRUE)
		{
			if (name == NULL)
			{
				tot += phase_ptr->moles_x;
			}
			else
			{
				for (next_elt = &phase_ptr->next_elt[0];
					 next_elt->elt != NULL; next_elt++)
				{
					if (strcmp(next_elt->elt->name, name) == 0)
					{
						tot += next_elt->coef * phase_ptr->moles_x;
						break;
					}
				}
			}
		}
	}
	return (tot);
}

/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
sum_match_species(const char *mytemplate, const char *name)
/* ---------------------------------------------------------------------- */
{
	int i;
	LDBLE tot;
	const class elt_list *next_elt;

	count_elts = 0;
	paren_count = 0;
	tot = 0;
	if (sum_species_map.find(mytemplate) == sum_species_map.end())
	{
		std::vector<std::string> species_list;
		for (i = 0; i < (int)this->s_x.size(); i++)
		{
			class species *s_ptr = s_x[i];
			if (match_elts_in_species(s_ptr->name, mytemplate) == TRUE)
			{
				species_list.push_back(s_ptr->name);
			}
		}
		sum_species_map[mytemplate] = species_list;
	}
	std::vector<std::string> &species_list = (sum_species_map.find(mytemplate))->second;
	for (size_t i=0; i < species_list.size(); i++)
	{
		class species *s_ptr = s_search(species_list[i].c_str());
		if (s_ptr->in == FALSE) continue;
		if (name == NULL)
		{
			tot += s_ptr->moles;
		}
		else
		{
			for (next_elt = &s_ptr->next_elt[0]; next_elt->elt != NULL;
					next_elt++)
			{
				if (strcmp(next_elt->elt->name, name) == 0)
				{
					tot += next_elt->coef * s_ptr->moles;
					break;
				}
			}
		}
	}
	return (tot);
}



/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
sum_match_ss(const char *mytemplate, const char *name)
/* ---------------------------------------------------------------------- */
{
	LDBLE tot;
	const class elt_list *next_elt;

	if (use.Get_ss_assemblage_in() == FALSE || use.Get_ss_assemblage_ptr() == NULL)
		return (0);
	tot = 0;
	std::vector<cxxSS *> ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize();
	for (size_t j = 0; j < ss_ptrs.size(); j++)
	{
		cxxSS *ss_ptr = ss_ptrs[j];
		if (strcmp_nocase(ss_ptr->Get_name().c_str(), mytemplate) == 0)
		{
			if (!ss_ptr->Get_ss_in())
			{
				tot = 0;
				break;
			}
			for (size_t i = 0; i < ss_ptr->Get_ss_comps().size(); i++)
			{
				cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[i]);
				if (name == NULL)
				{
					tot += comp_ptr->Get_moles();
				}
				else
				{
					int l;
					class phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE);
					for (next_elt = &phase_ptr->next_elt[0]; next_elt->elt != NULL; next_elt++)
					{
						if (strcmp(next_elt->elt->name, name) == 0)
						{
							tot += next_elt->coef *	comp_ptr->Get_moles();
							break;
						}
					}
				}
			}
			break;
		}
	}
	return (tot);
}

/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
list_ss(std::string ss_name, cxxNameDouble &composition)
/* ---------------------------------------------------------------------- */
{
	LDBLE tot = 0;
	composition.clear();
	if (use.Get_ss_assemblage_in() == FALSE || use.Get_ss_assemblage_ptr() == NULL)
		return (0);

	std::vector<cxxSS *> ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize();
	for (size_t j = 0; j < ss_ptrs.size(); j++)
	{
		cxxSS *ss_ptr = ss_ptrs[j];
		if (strcmp_nocase(ss_ptr->Get_name().c_str(), ss_name.c_str()) == 0)
		{
			for (size_t i = 0; i < ss_ptr->Get_ss_comps().size(); i++)
			{
				cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[i]);
				composition.add(comp_ptr->Get_name().c_str(), comp_ptr->Get_moles());
				tot += comp_ptr->Get_moles();
			}
			break;
		}
	}
	return (tot);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
match_elts_in_species(const char *name, const char *mytemplate)
/* ---------------------------------------------------------------------- */
{
/*
 *	Makes a list of elements with their coefficients, stores elements
 *	in elt_list at position count_elts.  Global variable count_elts is
 *	updated with each stored element.  Also uses static global variable
 *	paren_count.
 *
 *	Arguments:
 *	   **t_ptr	input, point in token string to start looking
 *				  output, is next position to start looking
 *		 coef	 input, coefficient to multiply subscripts by
 */
	int i, i1, l, case_no, match;
	char c, c1;
	const char* cptr, *ptr1;
	LDBLE d;
	char token[MAX_LENGTH], equal_list[MAX_LENGTH]; 
	char token1[MAX_LENGTH], template1[MAX_LENGTH], equal_list1[MAX_LENGTH];
	char str[2];

	strcpy(token, name);
	squeeze_white(token);
	replace("(+", "(", token);
	if (strstr("token", "++") != NULL)
	{
		replace("++++++", "+6", token);
		replace("+++++", "+5", token);
		replace("++++", "+4", token);
		replace("+++", "+3", token);
		replace("++", "+2", token);
	}
	if (strstr("token", "--") != NULL)
	{
		replace("------", "-6", token);
		replace("-----", "-5", token);
		replace("----", "-4", token);
		replace("---", "-3", token);
		replace("--", "-2", token);
	}

	cptr = token;
	std::vector< std::pair<std::string, LDBLE> > match_vector;
	while ((c = *cptr) != '\0')
	{
		c1 = *(cptr + 1);
		str[0] = c;
		str[1] = '\0';
/*
 * New element
 */
		if (isupper((int) c) || (c == 'e' && c1 == '-') || (c == '['))
		{
			/*
			 *   Get new element and subscript
			 */
			std::string element;
			if (get_elt(&cptr, element, &l) == ERROR)
			{
				return (ERROR);
			}
			if (get_num(&cptr, &d) == ERROR)
			{
				return (ERROR);
			}
			std::pair<std::string, LDBLE> pr(element, d);
			match_vector.push_back(pr);			
		}
		else
		{
			std::pair<std::string, LDBLE> pr(str, 1.0);
			match_vector.push_back(pr);		
			cptr += 1;
		}
	}
	/*
	 *  Replace elements with first of equivalent elements
	 */
	strcpy(template1, mytemplate);
	squeeze_white(template1);
	cptr = template1;
	while (extract_bracket(&cptr, equal_list) == TRUE)
	{
		replace("{", "", equal_list);
		replace("}", "", equal_list);
		while (replace(",", " ", equal_list) == TRUE);
		ptr1 = equal_list;
		/*
		 *   Get first name in a list from template
		 */
		std::string elt_name;
		if (copy_token(elt_name, &ptr1) == EMPTY)
		{
			error_string = sformatf(
					"Expecting a nonempty list of element names in isotope sum. %s",
					mytemplate);
			error_msg(error_string, CONTINUE);
			return (ERROR);
		}
		std::string replace_name = elt_name;
		/*
		 *   Replace in species all equivalent names from template
		 */
		while (copy_token(elt_name, &ptr1) != EMPTY)
		{
			for (i = 0; i < (int) match_vector.size(); i++)
			{
				if (elt_name == match_vector[i].first)
				{
					match_vector[i].first = replace_name;
				}
			}
		}
	}
	/*
	 *  Combine contiguous elements
	 */
	i1 = 0;		
	for (i = 1; i < (int) match_vector.size(); i++)
	{
		if ((isupper((int) (match_vector[i].first[0])) != FALSE)
		&& (match_vector[i].first == match_vector[i1].first))
		{
			match_vector[i1].second += match_vector[i].second;
		}
		else
		{
			i1++;
			match_vector[i1].first = match_vector[i].first;
			match_vector[i1].second = match_vector[i].second;
		}
	}
	int count_match_tokens = i1 + 1;
	/*
	 * write out string
	 */
	token[0] = '\0';
	for (i = 0; i < count_match_tokens; i++)
	{
		strcat(token, match_vector[i].first.c_str());
		if (match_vector[i].second != 1.0)
		{
			sprintf(token1, "%g", (double) match_vector[i].second);
			strcat(token, token1);
		}
	}
	/*
	 *  Write a template name using first of equivalent elements
	 */
	strcpy(template1, mytemplate);
	squeeze_white(template1);
	cptr = template1;
	while (extract_bracket(&cptr, equal_list) == TRUE)
	{
		strcpy(equal_list1, equal_list);
		replace("{", "", equal_list);
		replace("}", "", equal_list);
		while (replace(",", " ", equal_list) == TRUE);
		ptr1 = equal_list;
		/*
		 *   Get first name in a list
		 */
		std::string elt_name;
		if (copy_token(elt_name, &ptr1) == EMPTY)
		{
			error_string = sformatf(
					"Expecting a nonempty list of element names in isotope sum. %s",
					mytemplate);
			error_msg(error_string, CONTINUE);
			return (ERROR);
		}
		replace(equal_list1, elt_name.c_str(), template1);
		squeeze_white(template1);
		cptr = template1;
	}
	/*
	 *   Compare string
	 */
	/* Cases: 0 exact match
	 *	  1 leading wild card
	 *	  2 trailing wild card
	 *	  3 leading and trailing wild card
	 */
	case_no = 0;
	if (template1[0] == '*')
		case_no = 1;
	l = (int) strlen(template1);
	if (template1[l - 1] == '*')
	{
		if (case_no != 1)
		{
			case_no = 2;
		}
		else
		{
			case_no = 3;
		}
	}
	while (replace("*", "", template1));
	match = FALSE;
	switch (case_no)
	{
	case 0:
		/* exact match */
		if (strcmp(token, template1) == 0)
			match = TRUE;
		break;
	case 1:
		/* leading wild card */
		if ((cptr = strstr(token, template1)) == NULL)
		{
			match = FALSE;
		}
		else
		{
			if (strcmp(cptr, template1) == 0)
				match = TRUE;
		}
		break;
	case 2:
		/* trailing wild card */
		if (strstr(token, template1) == token)
			match = TRUE;
		break;
	case 3:
		/* trailing wild card */
		if (strstr(token, template1) != NULL)
			match = TRUE;
		break;
	}
	return (match);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
extract_bracket(const char **string, char *bracket_string)
/* ---------------------------------------------------------------------- */
{
	const char* cptr;
	char *ptr1;

	if ((cptr = strstr(*string, "{")) == NULL)
		return (FALSE);
	strcpy(bracket_string, cptr);
	if ((ptr1 = strstr(bracket_string, "}")) == NULL)
	{
		error_string = sformatf(
				"No matching bracket (}) in isotope template string %s",
				*string);
		error_msg(error_string, CONTINUE);
		input_error++;
		return (FALSE);
	}
	ptr1[1] = '\0';
	*string = strstr(*string, "}");
	*string += 1;
	return (TRUE);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
surf_total(const char *total_name, const char *surface_name)
/* ---------------------------------------------------------------------- */
{
/*
 *   Provides total moles in LDBLE layer
 */
	int j;

	if (use.Get_surface_ptr() == NULL || surface_name == NULL || total_name == NULL)
		return (0);

	bool redox = false;
	if (strstr(total_name, "(") != NULL)
	{
		redox = true;
	}
	if (!redox)
	{
		if (strcmp(total_name, "H") == 0 || strcmp(total_name, "O") == 0)
		{
			return surf_total_no_redox(total_name, surface_name);
		}
	}	
/*
 *   Find surface...
 */
	for (j = 0; j < count_unknowns; j++)
	{
		if (x[j]->type != SURFACE)
			continue;
		
		std::string token;
		token = x[j]->master[0]->elt->name;
		replace("_", " ", token);
		std::string::iterator b = token.begin();
		std::string::iterator e = token.end();
		std::string name;
		CParser::copy_token(name, b, e);
		if (strcmp(name.c_str(), surface_name) == 0)
				break;
	}
	if (j >= count_unknowns)
		return (0);
/*
 *   find total moles for redox state
 */
	LDBLE t = 0;
	for (j = 0; j < (int)this->s_x.size(); j++)
	{
		if (s_x[j]->type != SURF)
			continue;

		std::string token;
		bool match = false; 

		// find if surface matches 
		for (int i = 0; s_x[j]->next_elt[i].elt != NULL; i++)
		{
			if (s_x[j]->next_elt[i].elt->master->type != SURF) continue;

			//strcpy(token, s_x[j]->next_elt[i].elt->name);
			//replace("_", " ", token);
			//cptr = token;
			//copy_token(name, &cptr, &k);
			token = s_x[j]->next_elt[i].elt->name;
			replace("_", " ", token);
			std::string::iterator b = token.begin();
			std::string::iterator e = token.end();
			std::string name;
			CParser::copy_token(name, b, e);
			if (strcmp(name.c_str(), surface_name) == 0)
			{
				match = true;
				break;
			}
		}
		if (!match) continue;

		// surface matches, now match element or redox state
		class rxn_token *rxn_ptr;
		if (s_x[j]->mole_balance == NULL)
		{
			for (rxn_ptr = &s_x[j]->rxn_s.token[0] + 1; rxn_ptr->s != NULL; rxn_ptr++)
			{
				if (redox && rxn_ptr->s->secondary)
				{
					token = rxn_ptr->s->secondary->elt->name;
				}
				else if (!redox && rxn_ptr->s->secondary)
				{
					token = rxn_ptr->s->secondary->elt->primary->elt->name;
				}
				else if (!redox && rxn_ptr->s->primary)
				{
					token = rxn_ptr->s->primary->elt->name;
				}
				else
				{
					continue;
				}
				if (strcmp(token.c_str(), total_name) == 0)
				{
					t += rxn_ptr->coef * s_x[j]->moles;
					break;
				}
				else
					// sum all sites in case total_name is a surface name without underscore surf ("Hfo_w", "Hfo")
				{
					if (rxn_ptr->s->type == SURF)
					{
						if (token.find("_") != std::string::npos)
						{
							token = token.substr(0, token.find("_"));
						}
						if (strcmp(token.c_str(), total_name) == 0)
						{
							t += rxn_ptr->coef * s_x[j]->moles;
							break;
						}
					}
				}
			}
		}
		else
		{
			for (int i = 0; s_x[j]->next_secondary[i].elt != NULL; i++)
			{
				token = s_x[j]->next_secondary[i].elt->name;
				if (strcmp(token.c_str(), total_name) == 0)
				{
					t += s_x[j]->next_secondary[i].coef * s_x[j]->moles;
					break;
				}
			}
		}
	}
	return t;
}

/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
surf_total_no_redox(const char *total_name, const char *surface_name)
/* ---------------------------------------------------------------------- */
{
/*
 *   Provides total moles in LDBLE layer
 */
	int i, j, k;
	char name[MAX_LENGTH], token[MAX_LENGTH];
	char surface_name_local[MAX_LENGTH];
	const char* cptr;

	if (use.Get_surface_ptr() == NULL)
		return (0);

/*
 *   Find surface...
 */
	for (j = 0; j < count_unknowns; j++)
	{
		if (x[j]->type != SURFACE)
			continue;
		strcpy(token, x[j]->master[0]->elt->name);
		replace("_", " ", token);
		cptr = token;
		copy_token(name, &cptr, &k);
		if (surface_name != NULL)
		{
			if (strcmp(name, surface_name) == 0)
				break;
		}
		else
		{
			break;
		}
	}
	if (j >= count_unknowns)
		return (0);
	strcpy(surface_name_local, name);
/*
 *   find total moles of each element in diffuse layer...
 */
	count_elts = 0;
	paren_count = 0;
	for (j = 0; j < (int)this->s_x.size(); j++)
	{
		if (s_x[j]->type != SURF)
			continue;
		for (i = 0; s_x[j]->next_elt[i].elt != NULL; i++)
		{
			if (s_x[j]->next_elt[i].elt->master->type != SURF) continue;

			strcpy(token, s_x[j]->next_elt[i].elt->name);
			replace("_", " ", token);
			cptr = token;
			copy_token(name, &cptr, &k);
			if (strcmp(name, surface_name_local) == 0)
			{
/*
 *   Accumulate elements in diffuse layer
 */
				add_elt_list(s_x[j]->next_elt, s_x[j]->moles);
				//fprintf(stderr, "%15s\t%e\t%s\t%s\n", s_x[j]->name, s_x[j]->moles, name, surface_name_local );
				break;
			}
		}
	}
	elt_list_combine();
/*
 *   Return totals
 */
	for (j = 0; j < count_elts; j++)
	{
		if (strcmp(elt_list[j].elt->name, total_name) == 0)
		{
			return ((LDBLE) elt_list[j].coef);
		}
	}
	return (0);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
total(const char *total_name)
/* ---------------------------------------------------------------------- */
{
	class master *master_ptr;
	LDBLE t;

	if (strcmp(total_name, "H") == 0)
	{
		return (total_h_x / mass_water_aq_x);
	}
	if (strcmp(total_name, "O") == 0)
	{
		return (total_o_x / mass_water_aq_x);
	}
	std::string noplus = total_name;
	replace(noplus, "(+", "(");
	master_ptr = master_bsearch(noplus.c_str());
	t = 0.0;
	if (master_ptr == NULL)
	{
		if (strcmp_nocase(total_name, "water") == 0)
		{
			return (mass_water_aq_x);
		}
		else if (strcmp_nocase(total_name, "charge") == 0)
		{
			return (cb_x / mass_water_aq_x);
		}
/*
        sprintf (error_string, "Cannot find definition for master species, %s.",
	         total_name);
        warning_msg (error_string);
*/
	}
/*
 *  Primary master species
 */
	else if (master_ptr->primary == TRUE)
	{
		/*
		 *  Not a redox element
		 */
		if (master_ptr->s->secondary == NULL)
		{
			t = master_ptr->total / mass_water_aq_x;
			/*
			 * Redox element, need to sum totals of all redox states
			 */
		}
		else
		{
			t = 0;
			for (size_t i = master_ptr->number + 1;
				 (i < (int)master.size() && master[i]->elt->primary == master_ptr);
				 i++)
			{
				t += master[i]->total / mass_water_aq_x;
			}
		}
	}
/*
 *  Secondary master species
 */
	else
	{
		t = master_ptr->total / mass_water_aq_x;
	}
	return (t);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
total_mole(const char *total_name)
/* ---------------------------------------------------------------------- */
{
	class master *master_ptr;
	LDBLE t;

	if (strcmp(total_name, "H") == 0)
	{
		return (total_h_x);
	}
	if (strcmp(total_name, "O") == 0)
	{
		return (total_o_x);
	}
	std::string noplus = total_name;
	replace(noplus, "(+", "(");
	master_ptr = master_bsearch(noplus.c_str());
	t = 0.0;
	if (master_ptr == NULL)
	{
		if (strcmp_nocase(total_name, "water") == 0)
		{
			return (mass_water_aq_x / gfw_water);
		}
		else if (strcmp_nocase(total_name, "charge") == 0)
		{
			return (cb_x);
		}
/*
        sprintf (error_string, "Cannot find definition for master species, %s.",
	         total_name);
        warning_msg (error_string);
*/
	}
/*
 *  Primary master species
 */
	else if (master_ptr->primary == TRUE)
	{
		/*
		 *  Not a redox element
		 */
		if (master_ptr->s->secondary == NULL)
		{
			t = master_ptr->total;
			/*
			 * Redox element, need to sum totals of all redox states
			 */
		}
		else
		{
			t = 0;
			for (size_t i = master_ptr->number + 1;
				 (i < master.size() && master[i]->elt->primary == master_ptr);
				 i++)
			{
				t += master[i]->total;
			}
		}
	}
/*
 *  Secondary master species
 */
	else
	{
		t = master_ptr->total;
	}
	return (t);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
get_edl_species(cxxSurfaceCharge & charge_ref)
/* ---------------------------------------------------------------------- */
{

	double mass_water_surface = charge_ref.Get_mass_water();
	sys.clear();
	for (int j = 0; j < (int)this->s_x.size(); j++)
	{
		if (s_x[j]->type == H2O)
		{
			size_t count_sys = sys.size();
			sys.resize(count_sys + 1);
			sys[count_sys].name = string_duplicate(s_x[j]->name);
			sys[count_sys].moles = mass_water_surface / gfw_water;
			sys_tot += sys[count_sys].moles;
			count_sys++;
		}
		else if (s_x[j]->type < H2O)
		{
			size_t count_sys = sys.size();
			sys.resize(count_sys + 1);
			double molality = under(s_x[j]->lm);
			double moles_excess = mass_water_aq_x * molality * charge_ref.Get_g_map()[s_x[j]->z].Get_g();
			double moles_surface = mass_water_surface * molality + moles_excess;
			sys[count_sys].name = string_duplicate(s_x[j]->name);
			sys[count_sys].moles = moles_surface;
			sys_tot += sys[count_sys].moles;
			count_sys++;
		}
		else
		{
			continue;
		}
	}
	return (OK);
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
edl_species(const char *surf_name, LDBLE * count, char ***names, LDBLE ** moles, LDBLE * area, LDBLE * thickness)
/* ---------------------------------------------------------------------- */
{
/*
 *   Provides total moles in system and lists of species/phases in sort order
 */
	int i;
	sys_tot = 0;
	sys.clear();
	if (!(dl_type_x == cxxSurface::NO_DL))
	{
		cxxSurface *surface_ptr = use.Get_surface_ptr();
		for (size_t i = 0; i < surface_ptr->Get_surface_charges().size(); i++)
		{
			cxxSurfaceCharge & charge_ref = surface_ptr->Get_surface_charges()[i];
			if (strcmp(charge_ref.Get_name().c_str(), surf_name) == 0)
			{	
				get_edl_species(charge_ref);
				*area = charge_ref.Get_specific_area() * charge_ref.Get_grams();
				*thickness = surface_ptr->Get_thickness();
				break;
			}
		}
	}
	/*
	 *   Sort system species
	 */
	if (sys.size() > 1)
	{
		qsort(&sys[0], sys.size(),
			  sizeof(class system_species), system_species_compare);
	}
	/*
	 * malloc space
	 */
	*names = (char **) PHRQ_malloc((sys.size() + 1) * sizeof(char *));
	if (names == NULL)
		malloc_error();
	*moles = (LDBLE *) PHRQ_malloc((sys.size() + 1) * sizeof(LDBLE));
	if (moles == NULL)
		malloc_error();

	(*names)[0] = NULL;
	(*moles)[0] = 0;
	for (i = 0; i < (int)sys.size(); i++)
	{
		(*names)[i + 1] = sys[i].name;
		(*moles)[i + 1] = sys[i].moles;
	}
	*count = (LDBLE)sys.size();

	//PHRQ_free(sys);
	sys.clear();
	return (sys_tot);
}				
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
system_total(const char *total_name, LDBLE * count, char ***names,
			 char ***types, LDBLE ** moles, int isort)
/* ---------------------------------------------------------------------- */
{
/*
 *   Provides total moles in system and lists of species/phases in sort order
 */
	int i;

	sys_tot = 0;
	sys.clear();
	if (strcmp_nocase(total_name, "elements") == 0)
	{
		system_total_elements();
	}
	else if (strcmp_nocase(total_name, "phases") == 0)
	{
		system_total_si();
	}
	else if (strcmp_nocase(total_name, "aq") == 0)
	{
		system_total_aq();
	}
	else if (strcmp_nocase(total_name, "ex") == 0)
	{
		system_total_ex();
	}
	else if (strcmp_nocase(total_name, "surf") == 0)
	{
		system_total_surf();
	}
	else if (strcmp_nocase(total_name, "s_s") == 0)
	{
		system_total_ss();
	}
	else if (strcmp_nocase(total_name, "gas") == 0)
	{
		system_total_gas();
	}
	else if (strcmp_nocase(total_name, "equi") == 0)
	{
		system_total_equi();
	}
	else if (strcmp_nocase(total_name, "kin") == 0)
	{
		system_total_kin();
	}
	else
	{
		if (strstr(total_name, "(") == NULL)
		{
			system_total_elt(total_name);
		}
		else
		{
			system_total_elt_secondary(total_name);
		}
	}
	/*
	 *   Sort system species
	 */
	if (sys.size() > 1 && isort == 0)
	{
		qsort(&sys[0], sys.size(),
			  sizeof(class system_species), system_species_compare);
	}
	else if (sys.size() > 1)
	{
		qsort(&sys[0], sys.size(),
			sizeof(class system_species), system_species_compare_name);
	}
	/*
	 * malloc space
	 */
	size_t count_sys = sys.size();
	*names = (char **) PHRQ_malloc((count_sys + 1) * sizeof(char *));
	if (names == NULL)
		malloc_error();
	*types = (char **) PHRQ_malloc((count_sys + 1) * sizeof(char *));
	if (types == NULL)
		malloc_error();
	*moles = (LDBLE *) PHRQ_malloc((count_sys + 1) * sizeof(LDBLE));
	if (moles == NULL)
		malloc_error();

	(*names)[0] = NULL;
	(*types)[0] = NULL;
	(*moles)[0] = 0;
	for (i = 0; i < (int)count_sys; i++)
	{
		(*names)[i + 1] = sys[i].name;
		(*types)[i + 1] = sys[i].type;
		(*moles)[i + 1] = sys[i].moles;
	}
	*count = (LDBLE) count_sys;
	if (strcmp_nocase(total_name, "elements") == 0)
	{
		sys_tot = 0;;
		for (i = 0; i < (int)count_sys; i++)
		{
			if (strcmp(sys[i].type, "dis") == 0 &&
				strstr(sys[i].name, "(") == NULL &&
				strcmp(sys[i].name, "H") != 0
				&& strcmp(sys[i].name, "O") != 0)
			{
				sys_tot += sys[i].moles;
			}
		}
	}
	//PHRQ_free(sys);
	sys.clear();
	return (sys_tot);
}

/* ---------------------------------------------------------------------- */
std::string Phreeqc::
kinetics_formula(std::string kin_name, cxxNameDouble &stoichiometry)
/* ---------------------------------------------------------------------- */
{
/*
 *   Returns formula of kinetic reactant
 *   Also returns arrays of elements and stoichiometry in stoichiometry
 */
	stoichiometry.clear();
	std::string formula;

	if (use.Get_kinetics_ptr() == NULL)
		return (formula);
	std::vector <cxxKineticsComp> comps = use.Get_kinetics_ptr()->Get_kinetics_comps();
	count_elts = 0;
	paren_count = 0;
	for  (size_t i=0 ; i < comps.size(); i++)
	{
			cxxKineticsComp *comp_ptr = &comps[i]; 
			if (kin_name == comp_ptr->Get_rate_name().c_str())
			{
				cxxNameDouble nd = comp_ptr->Get_namecoef();
				cxxNameDouble::iterator it = nd.begin();
				for ( ; it != nd.end(); it++)
				{
					// Try Phases
					int l; 
					class phase *phase_ptr = phase_bsearch(it->first.c_str(), &l, FALSE);
					if (phase_ptr != NULL)
					{
						add_elt_list(phase_ptr->next_elt, it->second);
					}
					else
					{
						// add formula
						std::string name = it->first;
						LDBLE coef = it->second;
						const char* cptr = &name[0];
						get_elts_in_species(&cptr, coef);
					}
				}
				formula.append(kin_name);
				//elt_list[count_elts].elt = NULL;
				elt_list_combine();
				stoichiometry = elt_list_NameDouble();
				break;
			}
	}
	return (formula);
}
/* ---------------------------------------------------------------------- */
std::string Phreeqc::
phase_formula(std::string phase_name, cxxNameDouble &stoichiometry)
/* ---------------------------------------------------------------------- */
{
/*
 *   Returns formula of mineral
 *   Also returns arrays of elements and stoichiometry in elts_arg and coef_arg
 */
	stoichiometry.clear();
	std::string formula;

	int j;
	class phase *phase_ptr = phase_bsearch(phase_name.c_str(), &j, FALSE);
	if (phase_ptr != NULL)
	{
		formula.append(phase_ptr->formula);
		cxxNameDouble nd(phase_ptr->next_elt);
		stoichiometry = nd;
	}

	return (formula);
}
/* ---------------------------------------------------------------------- */
std::string Phreeqc::
species_formula(std::string phase_name, cxxNameDouble &stoichiometry)
/* ---------------------------------------------------------------------- */
{
/*
 *   Returns formula of mineral
 *   Also returns arrays of elements and stoichiometry in elts_arg and coef_arg
 */
	stoichiometry.clear();
	std::string formula;
	formula = "none";
	class species *s_ptr = s_search(phase_name.c_str());
	if (s_ptr != NULL)
	{
		cxxNameDouble nd(s_ptr->next_elt);
		stoichiometry = nd;
		stoichiometry["charge"] = s_ptr->z;
		if (s_ptr->type == EX)
		{
			formula = "ex";
		}
		else if (s_ptr->type == SURF)
		{
			formula = "surf";
		}
		else
		{
			formula = "aq";
		}
	}
	return (formula);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
system_total_elements(void)
/* ---------------------------------------------------------------------- */
{
	int i;
	LDBLE t;
	char name[MAX_LENGTH];
	class master *master_ptr;

	/*
	 * Include H and O
	 */
	size_t count_sys = sys.size();
	sys.resize(count_sys + 1);
	sys[count_sys].name = string_duplicate("H");
	sys[count_sys].moles = total_h_x;
	sys_tot += sys[count_sys].moles;
	sys[count_sys].type = string_duplicate("dis");
	count_sys++;
	sys.resize(count_sys + 1);
	sys[count_sys].name = string_duplicate("O");
	sys[count_sys].moles = total_o_x;
	sys_tot += sys[count_sys].moles;
	sys[count_sys].type = string_duplicate("dis");
	count_sys++;;
	/*
	 * Include H(1) and O(-2)
	 */
	sys.resize(count_sys + 1);
	sys[count_sys].name = string_duplicate("H(1)");
	sys[count_sys].moles = solution_sum_secondary("H(1)");
	sys_tot += sys[count_sys].moles;
	sys[count_sys].type = string_duplicate("dis");
	count_sys++;
	sys.resize(count_sys + 1);
	sys[count_sys].name = string_duplicate("O(-2)");
	sys[count_sys].moles = solution_sum_secondary("O(-2)");
	sys_tot += sys[count_sys].moles;
	sys[count_sys].type = string_duplicate("dis");
	count_sys++;
	for (i = 0; i < (int)master.size(); i++)
	{
		master_ptr = master[i];
		if (master_ptr->primary == TRUE && master_ptr->total_primary <= 0)
			continue;
		if (master_ptr->in == FALSE
			&& (master_ptr->primary == FALSE
				|| master_ptr->total_primary == 0))
			continue;
		/*
		 *  H and O
		 */
		if (master_ptr->s == s_hplus)
		{
			continue;
		}
		else if (master_ptr->s == s_h2o)
		{
			continue;
		}
		if (master_ptr->primary == TRUE)
		{
			if (master_ptr->total_primary > 0)
			{
				t = master_ptr->total_primary;
				/*
				 *  Not a redox element
				 */
			}
			else if (master_ptr->s->secondary == NULL)
			{
				t = master_ptr->total;
				/*
				 * Redox element, need to sum totals of all redox states
				 */
			}
			else
			{
				t = 0;
				for (size_t j = master_ptr->number + 1;
					 master[j]->elt->primary == master_ptr; j++)
				{
					t += master[j]->total;
				}
			}
			/*
			 *  Secondary master species
			 */
		}
		else
		{
			t = master_ptr->total;
		}
		strcpy(name, master[i]->elt->name);			
		count_sys = sys.size();
		sys.resize(count_sys + 1);
		sys[count_sys].name = string_duplicate(name);
		sys[count_sys].moles = t;
		sys_tot += sys[count_sys].moles;
		if (master[i]->s->type <= SOLID)
		{
			sys[count_sys].type = string_duplicate("dis");
		}
		else if (master[i]->s->type == EX)
		{
			sys[count_sys].type = string_duplicate("ex");
		}
		else if (master[i]->s->type == SURF || master[i]->s->type == SURF_PSI)
		{
			sys[count_sys].type = string_duplicate("surf");
		}
	}
	return (OK);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
system_total_si(void)
/* ---------------------------------------------------------------------- */
{
	int i;
	LDBLE si, iap;
	class rxn_token *rxn_ptr;
	char name[MAX_LENGTH];

	sys_tot = -999.9;
	for (i = 0; i < (int)phases.size(); i++)
	{
		if (phases[i]->in == FALSE || phases[i]->type != SOLID)
			continue;
/*
 *   Print saturation index
 */
		iap = 0.0;
		for (rxn_ptr = &phases[i]->rxn_x.token[0] + 1; rxn_ptr->s != NULL;
			 rxn_ptr++)
		{
			iap += rxn_ptr->s->la * rxn_ptr->coef;
		}
		si = -phases[i]->lk + iap;
		strcpy(name, phases[i]->name);
		size_t count_sys = sys.size();
		sys.resize(count_sys + 1);
		sys[count_sys].name = string_duplicate(name);
		sys[count_sys].moles = si;
		if (si > sys_tot)
			sys_tot = si;
		sys[count_sys].type = string_duplicate("phase");
	}
	return (OK);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
system_total_aq(void)
/* ---------------------------------------------------------------------- */
{
/*
 *   Provides total moles in system and lists of species/phases in sort order
 */
	int i;
/*
 *   find total moles in aq, surface, and exchange
 */
	for (i = 0; i < (int)this->s_x.size(); i++)
	{
		//if (s_x[i]->type != AQ)
		if (s_x[i]->type > HPLUS)
			continue;
		size_t count_sys = sys.size();
		sys.resize(count_sys + 1);
		sys[count_sys].name = string_duplicate(s_x[i]->name);
		sys[count_sys].moles = s_x[i]->moles;
		sys_tot += sys[count_sys].moles;
		sys[count_sys].type = string_duplicate("aq");
	}
	return (OK);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
system_total_ex(void)
/* ---------------------------------------------------------------------- */
{
/*
 *   Provides total moles in system and lists of species/phases in sort order
 */
	int i;
/*
 *   find total moles in aq, surface, and exchange
 */
	for (i = 0; i < (int)this->s_x.size(); i++)
	{
		if (s_x[i]->type != EX)
			continue;
		if (s_x[i]->primary != NULL)
			continue;
		size_t count_sys = sys.size();
		sys.resize(count_sys + 1);
		sys[count_sys].name = string_duplicate(s_x[i]->name);
		sys[count_sys].moles = s_x[i]->moles;
		sys_tot += sys[count_sys].moles;
		sys[count_sys].type = string_duplicate("ex");
	}
	return (OK);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
system_total_surf(void)
/* ---------------------------------------------------------------------- */
{
/*
 *   Provides total moles in system and lists of species/phases in sort order
 */
	int i;
/*
 *   find total moles in aq, surface, and exchange
 */
	for (i = 0; i < (int)this->s_x.size(); i++)
	{
		if (s_x[i]->type != SURF)
			continue;
		size_t count_sys = sys.size();
		sys.resize(count_sys + 1);
		sys[count_sys].name = string_duplicate(s_x[i]->name);
		sys[count_sys].moles = s_x[i]->moles;
		sys_tot += sys[count_sys].moles;
		sys[count_sys].type = string_duplicate("surf");
	}
	return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
system_total_gas(void)
/* ---------------------------------------------------------------------- */
{
/*
 *   Provides total moles in system and lists of species/phases in sort order
 */
	int i;

/*
 *   find total in gas phase
 */
	if (use.Get_gas_phase_ptr() == NULL)
		return (OK);
	cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr();
	for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++)
	{
		class phase *phase_ptr = phase_bsearch(gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str(), 
			&i, FALSE);
		assert(phase_ptr);
		size_t count_sys = sys.size();
		sys.resize(count_sys + 1);
		sys[count_sys].name = string_duplicate(phase_ptr->name);
		sys[count_sys].moles = phase_ptr->moles_x;
		sys_tot += sys[count_sys].moles;
		sys[count_sys].type = string_duplicate("gas");
	}
	return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
system_total_equi(void)
/* ---------------------------------------------------------------------- */
{
/*
 *  Equilibrium phases
 */
	if (use.Get_pp_assemblage_ptr() == NULL)
		return (OK);
	std::map <std::string, cxxPPassemblageComp > comps = use.Get_pp_assemblage_ptr()->Get_pp_assemblage_comps();
	std::map <std::string, cxxPPassemblageComp >::iterator it = comps.begin();
	for  ( ; it != comps.end(); it++)
	{
			cxxPPassemblageComp *comp_ptr = &(it->second);
			int l;
			class phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE);
			size_t count_sys = sys.size();
			sys.resize(count_sys + 1);
			sys[count_sys].name = string_duplicate(phase_ptr->name);
			sys[count_sys].moles = equi_phase(sys[count_sys].name);
			sys_tot += sys[count_sys].moles;
			sys[count_sys].type = string_duplicate("equi");
	}
	return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
system_total_kin(void)
/* ---------------------------------------------------------------------- */
{
/*
 *  Equilibrium phases
 */
	if (use.Get_kinetics_ptr() == NULL)
		return (OK);
	std::vector <cxxKineticsComp> comps = use.Get_kinetics_ptr()->Get_kinetics_comps();
	for  (size_t i=0 ; i < comps.size(); i++)
	{
			cxxKineticsComp *comp_ptr = &comps[i];
			size_t count_sys = sys.size();
			sys.resize(count_sys + 1);
			sys[count_sys].name = string_duplicate(comp_ptr->Get_rate_name().c_str());
			sys[count_sys].moles = comp_ptr->Get_m();
			sys_tot += sys[count_sys].moles;
			sys[count_sys].type = string_duplicate("kin");
	}
	return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
system_total_ss(void)
/* ---------------------------------------------------------------------- */
{
/*
 *   Provides total moles in system and lists of species/phases in sort order
 */

/*
 *  Solid solutions
 */
	if (use.Get_ss_assemblage_ptr() == NULL)
		return (OK);
	std::vector<cxxSS *> ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize();
	for (size_t k = 0; k < ss_ptrs.size(); k++)
	{
		cxxSS *ss_ptr = ss_ptrs[k];
		for (size_t i = 0; i < ss_ptr->Get_ss_comps().size(); i++)
		{
			cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[i]);
			int l;
			class phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE);
			size_t count_sys = sys.size();
			sys.resize(count_sys + 1);
			sys[count_sys].name = string_duplicate(phase_ptr->name);
			sys[count_sys].moles = comp_ptr->Get_moles();
			sys_tot += sys[count_sys].moles;
			sys[count_sys].type = string_duplicate("s_s");
		}
	}
	return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
system_total_elt(const char *total_name)
/* ---------------------------------------------------------------------- */
{
/*
 *   Provides total moles in system and lists of species/phases in sort order
 */
	int i, j, k;
	LDBLE molality, moles_excess, moles_surface, mass_water_surface;
	char name[MAX_LENGTH];

/*
 *   find total moles in aq, surface, and exchange
 */
	for (i = 0; i < (int)this->s_x.size(); i++)
	{
		count_elts = 0;
		paren_count = 0;
		add_elt_list(s_x[i]->next_elt, s_x[i]->moles);

		elt_list_combine();
		/*
		 *   Look for element
		 */
		for (j = 0; j < count_elts; j++)
		{
			if (strcmp(elt_list[j].elt->name, total_name) == 0)
			{
				size_t count_sys = sys.size();
				sys.resize(count_sys + 1);
				sys[count_sys].name = string_duplicate(s_x[i]->name);
				sys[count_sys].moles = elt_list[j].coef;
				sys_tot += sys[count_sys].moles;
				if (s_x[i]->type == AQ)
				{
					sys[count_sys].type = string_duplicate("aq");
				}
				else if (s_x[i]->type == EX)
				{
					sys[count_sys].type = string_duplicate("ex");
					/* subtract again the dummy moles of primary exchange species... */
					if (s_x[i]->primary != NULL)
					{
						sys_tot -= elt_list[j].coef;
					}
				}
				else if (s_x[i]->type == SURF)
				{
					sys[count_sys].type = string_duplicate("surf");
				}
				else if (s_x[i]->type == HPLUS)
				{
					sys[count_sys].type = string_duplicate("aq");
					/* sys[count_sys].moles = total_h_x; */
				}
				else if (s_x[i]->type == H2O)
				{
					sys[count_sys].type = string_duplicate("aq");
					/* sys[count_sys].moles = total_o_x; */
				}
				else
				{
					error_msg("System_total", STOP);
				}
				break;
			}
		}
	}
	if (use.Get_surface_ptr() != NULL && dl_type_x != cxxSurface::NO_DL)
	{
		/*
		 *   Find position of component in surface charge data
		 */
		i = -1;
		for (k = 0; k < count_unknowns; k++)
		{
			if (x[k]->type != SURFACE_CB)
				continue;
			cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[k]->surface_charge);
			i++;
			/*
			 *   Loop through all surface components, calculate each H2O surface (diffuse layer),
			 *   H2O aq, and H2O bulk (diffuse layers plus aqueous).
			 */
			mass_water_surface = charge_ptr->Get_mass_water();
			count_elts = 0;
			paren_count = 0;
			for (j = 0; j < (int)this->s_x.size(); j++)
			{
				if (s_x[j]->type > HPLUS)
					continue;
				molality = under(s_x[j]->lm);
				moles_excess =
					mass_water_aq_x * molality *
					(charge_ptr->Get_g_map()[s_x[j]->z].Get_g() * s_x[j]->erm_ddl +
					 mass_water_surface / mass_water_aq_x * (s_x[j]->erm_ddl -
															 1));
				moles_surface = mass_water_surface * molality + moles_excess;
				/*
				 *   Accumulate elements in diffuse layer
				 */
				add_elt_list(s_x[j]->next_elt, moles_surface);
			}
			elt_list_combine();
			/*
			 *   Print totals
			 */
			for (j = 0; j < count_elts; j++)
			{
				if (strcmp(elt_list[j].elt->name, total_name) == 0)
				{
					size_t count_sys = sys.size();
					sys.resize(count_sys + 1);
					strcpy(name, x[k]->master[0]->elt->name);
					replace("_psi", "", name);
					sys[count_sys].name = string_duplicate(name);
					sys[count_sys].moles = elt_list[j].coef;
					sys_tot += sys[count_sys].moles;
					sys[count_sys].type = string_duplicate("diff");
					break;
				}
			}
		}
	}
/*
 *   find total moles in mineral phases
 */
	if (use.Get_pp_assemblage_in() == TRUE && use.Get_pp_assemblage_ptr() != NULL)
	{
		for (i = 0; i < count_unknowns; i++)
		{
			if (x[i]->type != PP)
				continue;
			//std::map<std::string, cxxPPassemblageComp>::iterator it;
			//it =  pp_assemblage_ptr->Get_pp_assemblage_comps().find(x[i]->pp_assemblage_comp_name);
			cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp * ) x[i]->pp_assemblage_comp_ptr;
			//if (it->second.Get_add_formula().size() > 0)
			if (comp_ptr->Get_add_formula().size() > 0)
				continue;
			count_elts = 0;
			paren_count = 0;
			int j;
			//class phase * phase_ptr = phase_bsearch(x[i]->pp_assemblage_comp_name, &j, FALSE);
			class phase * phase_ptr = x[i]->phase;
			add_elt_list(phase_ptr->next_elt, x[i]->moles);
			elt_list_combine();
			for (j = 0; j < count_elts; j++)
			{
				if (strcmp(elt_list[j].elt->name, total_name) == 0)
				{
					size_t count_sys = sys.size();
					sys.resize(count_sys + 1);
					sys[count_sys].name = string_duplicate(phase_ptr->name);
					sys[count_sys].moles = elt_list[j].coef;
					sys_tot += sys[count_sys].moles;
					sys[count_sys].type = string_duplicate("equi");
					break;
				}
			}
		}
	}
/*
 *  Solid solutions
 */
	if (use.Get_ss_assemblage_ptr() != NULL)
	{
		std::vector<cxxSS *> ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize();
		for (size_t k = 0; k < ss_ptrs.size(); k++)
		{
			cxxSS *ss_ptr = ss_ptrs[k];
			if (ss_ptr->Get_ss_in())
			{
				for (size_t i = 0; i < ss_ptr->Get_ss_comps().size(); i++)
				{
					cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[i]);
					int l;
					class phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE);
					count_elts = 0;
					paren_count = 0;
					add_elt_list(phase_ptr->next_elt,
								 comp_ptr->Get_moles());
					elt_list_combine();
					for (j = 0; j < count_elts; j++)
					{
						if (strcmp(elt_list[j].elt->name, total_name) == 0)
						{
							size_t count_sys = sys.size();
							sys.resize(count_sys + 1);
							sys[count_sys].name =
								string_duplicate(phase_ptr->name);
							sys[count_sys].moles = elt_list[j].coef;
							sys_tot += sys[count_sys].moles;
							sys[count_sys].type = string_duplicate("s_s");
							break;
						}
					}
				}
			}
		}
	}
/*
 *   find total in gas phase
 */
	if (use.Get_gas_phase_ptr() != NULL)
	{
		cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr();
		for (size_t i = 0; i < gas_phase_ptr->Get_gas_comps().size(); i++)
		{
			class phase *phase_ptr = 
				phase_bsearch(gas_phase_ptr->Get_gas_comps()[i].Get_phase_name().c_str(), &k, FALSE);
			assert(phase_ptr);
			if (phase_ptr->in == TRUE)
			{
				count_elts = 0;
				paren_count = 0;
				add_elt_list(phase_ptr->next_elt, phase_ptr->moles_x);
				elt_list_combine();
				/*
				 *   Look for element
				 */
				for (j = 0; j < count_elts; j++)
				{
					if (strcmp(elt_list[j].elt->name, total_name) == 0)
					{
						size_t count_sys = sys.size();
						sys.resize(count_sys + 1);
						sys[count_sys].name = string_duplicate(phase_ptr->name);
						sys[count_sys].moles = elt_list[j].coef;
						sys_tot += sys[count_sys].moles;
						sys[count_sys].type = string_duplicate("gas");
						break;
					}
				}
			}
		}
	}
	return (OK);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
system_total_elt_secondary(const char *total_name)
/* ---------------------------------------------------------------------- */
{
/*
 *   Provides total moles in system and lists of species/phases in sort order
 */
	int i, j, k, l;
	LDBLE molality, moles_excess, moles_surface, mass_water_surface, sum,
		coef;
	char name[MAX_LENGTH];
/*
 *   find total moles in aq, surface, and exchange
 */
	for (i = 0; i < (int)this->s_x.size(); i++)
	{
		count_elts = 0;
		paren_count = 0;
		if (s_x[i]->next_secondary.size() != 0)
		{
			add_elt_list(s_x[i]->next_secondary, s_x[i]->moles);
		}
		else
		{
			add_elt_list(s_x[i]->next_sys_total, s_x[i]->moles);
		}
		elt_list_combine();
		/*
		 *   Look for element
		 */
		for (j = 0; j < count_elts; j++)
		{
			if (strcmp(elt_list[j].elt->name, total_name) == 0)
			{
				size_t count_sys = sys.size();
				sys.resize(count_sys + 1);
				sys[count_sys].name = string_duplicate(s_x[i]->name);
				sys[count_sys].moles = elt_list[j].coef;
				sys_tot += sys[count_sys].moles;
				if (s_x[i]->type == AQ)
				{
					sys[count_sys].type = string_duplicate("aq");
				}
				else if (s_x[i]->type == EX)
				{
					sys[count_sys].type = string_duplicate("ex");
				}
				else if (s_x[i]->type == SURF)
				{
					sys[count_sys].type = string_duplicate("surf");
				}
				else if (s_x[i]->type == HPLUS)
				{
					sys[count_sys].type = string_duplicate("aq");
					/* sys[count_sys].moles = total_h_x; */
				}
				else if (s_x[i]->type == H2O)
				{
					sys[count_sys].type = string_duplicate("aq");
					/* sys[count_sys].moles = total_o_x; */
				}
				else
				{
					error_msg("System_total", STOP);
				}
				break;
			}
		}
	}
	if (use.Get_surface_ptr() != NULL && dl_type_x != cxxSurface::NO_DL)
	{
		/*
		 *   Find position of component in surface charge data
		 */
		i = -1;
		for (k = 0; k < count_unknowns; k++)
		{
			if (x[k]->type != SURFACE_CB)
				continue;
			cxxSurfaceCharge *charge_ptr = use.Get_surface_ptr()->Find_charge(x[k]->surface_charge);
			i++;
			/*
			 *   Loop through all surface components, calculate each H2O surface (diffuse layer),
			 *   H2O aq, and H2O bulk (diffuse layers plus aqueous).
			 */
			mass_water_surface = charge_ptr->Get_mass_water();
			sum = 0;
			for (j = 0; j < (int)this->s_x.size(); j++)
			{
				count_elts = 0;
				paren_count = 0;
				if (s_x[i]->next_secondary.size() != 0)
				{
					add_elt_list(s_x[i]->next_secondary, 1);
				}
				else
				{
					add_elt_list(s_x[i]->next_sys_total, 1);
				}
				for (l = 0; l < count_elts; l++)
				{
					if (strcmp(elt_list[l].elt->name, total_name) == 0)
					{
						coef = elt_list[l].coef;
						if (s_x[j]->type > H2O)
							continue;
						molality = under(s_x[j]->lm);
						moles_excess =
							mass_water_aq_x * molality *
							charge_ptr->Get_g_map()[s_x[j]->z].Get_g();
						moles_surface =
							mass_water_surface * molality + moles_excess;
						sum += moles_surface * coef;
						break;
					}
				}
				if (l >= count_elts)
					continue;
				strcpy(name, x[k]->master[0]->elt->name);
				replace("_psi", "", name);
				size_t count_sys = sys.size();
				sys.resize(count_sys + 1);
				sys[count_sys].name = string_duplicate(name);
				sys[count_sys].moles = sum;
				sys_tot += sys[count_sys].moles;
				sys[count_sys].type = string_duplicate("diff");
				break;
			}
		}
	}
/*
 *   find total moles in mineral phases
 */
	if (use.Get_pp_assemblage_in() == TRUE && use.Get_pp_assemblage_ptr() != NULL)
	{
		for (i = 0; i < count_unknowns; i++)
		{
			if (x[i]->type != PP)
				continue;
			//std::map<std::string, cxxPPassemblageComp>::iterator it;
			//it =  pp_assemblage_ptr->Get_pp_assemblage_comps().find(x[i]->pp_assemblage_comp_name);
			cxxPPassemblageComp * comp_ptr = (cxxPPassemblageComp * ) x[i]->pp_assemblage_comp_ptr;
			//if (it->second.Get_add_formula().size() > 0)
			if (comp_ptr->Get_add_formula().size() > 0)
				continue;
			count_elts = 0;
			paren_count = 0;
			int j;
			//class phase * phase_ptr = phase_bsearch(x[i]->pp_assemblage_comp_name, &j, FALSE);
			class phase * phase_ptr = x[i]->phase;
			add_elt_list(phase_ptr->next_sys_total,	 x[i]->moles);
			elt_list_combine();
			for (j = 0; j < count_elts; j++)
			{
				if (strcmp(elt_list[j].elt->name, total_name) == 0)
				{
					size_t count_sys = sys.size();
					sys.resize(count_sys + 1);
					sys[count_sys].name =
						string_duplicate(phase_ptr->name);
					sys[count_sys].moles = elt_list[j].coef;
					sys_tot += sys[count_sys].moles;
					sys[count_sys].type = string_duplicate("equi");
					break;
				}
			}
		}
	}
/*
 *  Solid solutions
 */
	if (use.Get_ss_assemblage_ptr() != NULL)
	{
		std::vector<cxxSS *> ss_ptrs = use.Get_ss_assemblage_ptr()->Vectorize();
		for (size_t i = 0; i < ss_ptrs.size(); i++)
		{
			cxxSS *ss_ptr = ss_ptrs[i];
			if (ss_ptr->Get_ss_in())
			{
				for (size_t k = 0; k < ss_ptr->Get_ss_comps().size(); k++)
				{
					cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[k]);
					int l;
					class phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE);
					count_elts = 0;
					paren_count = 0;
					add_elt_list(phase_ptr->next_sys_total,
								 comp_ptr->Get_moles());
					elt_list_combine();
					for (j = 0; j < count_elts; j++)
					{
						if (strcmp(elt_list[j].elt->name, total_name) == 0)
						{
							size_t count_sys = sys.size();
							sys.resize(count_sys + 1);
							sys[count_sys].name =
								string_duplicate(phase_ptr->name);
							sys[count_sys].moles = elt_list[j].coef;
							sys_tot += sys[count_sys].moles;
							sys[count_sys].type = string_duplicate("s_s");
							break;
						}
					}
				}
			}
		}
	}
/*
 *   find total in gas phase
 */
	if (use.Get_gas_phase_ptr() != NULL)
	{
		cxxGasPhase *gas_phase_ptr = use.Get_gas_phase_ptr();
		for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++)	
		{
			class phase *phase_ptr = 
				phase_bsearch(gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str(), &i, FALSE);
			assert(phase_ptr);
			if (phase_ptr->in == TRUE)
			{
				count_elts = 0;
				paren_count = 0;
				add_elt_list(phase_ptr->next_sys_total,
							 phase_ptr->moles_x);

				elt_list_combine();
				/*
				 *   Look for element
				 */
				for (size_t j1 = 0; j1 < (size_t) count_elts; j1++)
				{
					if (strcmp(elt_list[j1].elt->name, total_name) == 0)
					{
						size_t count_sys = sys.size();
						sys.resize(count_sys + 1);
						sys[count_sys].name =
							string_duplicate(phase_ptr->name);
						sys[count_sys].moles = elt_list[j1].coef;
						sys_tot += sys[count_sys].moles;
						sys[count_sys].type = string_duplicate("gas");
						break;
					}
				}
			}
		}
	}
	return (OK);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
solution_number(void)
/* ---------------------------------------------------------------------- */
{
	Phreeqc * PhreeqcPtr = this;
	int soln_no = -999;
	if (PhreeqcPtr->state == TRANSPORT)
	{
		soln_no = PhreeqcPtr->cell_no;
	}
	else if (PhreeqcPtr->state == PHAST)
	{
		soln_no = PhreeqcPtr->cell_no;
	}
	else if (PhreeqcPtr->state == ADVECTION)
	{
		soln_no = PhreeqcPtr->cell_no;
	}
	else if (PhreeqcPtr->state < REACTION)
	{
		soln_no = PhreeqcPtr->use.Get_solution_ptr()->Get_n_user();
	}
	else
	{
		if (PhreeqcPtr->use.Get_mix_in())
		{
			soln_no = PhreeqcPtr->use.Get_n_mix_user();
		}
		else
		{
			soln_no = PhreeqcPtr->use.Get_n_solution_user();
		}
	}
	return soln_no;
}
/* ---------------------------------------------------------------------- */
LDBLE Phreeqc::
solution_sum_secondary(const char *total_name)
/* ---------------------------------------------------------------------- */
{
/*
 *   Provides total moles in system and lists of species/phases in sort order
 */
	int i, j;
	LDBLE sum;
/*
 *   find total moles in aq, surface, and exchange
 */
	sum = 0;
	for (i = 0; i < (int)this->s_x.size(); i++)
	{
		if (s_x[i]->type > H2O)
			continue;
		count_elts = 0;
		paren_count = 0;
		if (s_x[i]->next_secondary.size() != 0)
		{
			add_elt_list(s_x[i]->next_secondary, s_x[i]->moles);
		}
		else
		{
			add_elt_list(s_x[i]->next_sys_total, s_x[i]->moles);
		}
		elt_list_combine();
		/*
		 *   Look for element
		 */
		for (j = 0; j < count_elts; j++)
		{
			if (strcmp(elt_list[j].elt->name, total_name) == 0)
			{
				sum += elt_list[j].coef;
				break;
			}
		}
	}
	return (sum);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
system_species_compare(const void *ptr1, const void *ptr2)
/* ---------------------------------------------------------------------- */
{
	const class system_species *a, *b;

	a = (const class system_species *) ptr1;
	b = (const class system_species *) ptr2;
	if (a->moles < b->moles)
		return (1);
	if (a->moles > b->moles)
		return (-1);
	return (0);
}
int Phreeqc::
system_species_compare_name(const void* ptr1, const void* ptr2)
/* ---------------------------------------------------------------------- */
{
	const class system_species* a, * b;

	a = (const class system_species*)ptr1;
	b = (const class system_species*)ptr2;
	return (strncmp(a->name, b->name, MAX_LENGTH));
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
system_total_solids(cxxExchange *exchange_ptr,
					cxxPPassemblage *pp_assemblage_ptr,
					cxxGasPhase *gas_phase_ptr,
					cxxSSassemblage *ss_assemblage_ptr,
					cxxSurface *surface_ptr)
/* ---------------------------------------------------------------------- */
{
/*
 *   Provides total moles in solid phases
 */
	count_elts = 0;
	paren_count = 0;
/*
 *   find total moles in exchanger
 */
	if (exchange_ptr != NULL)
	{
		for (size_t i = 0; i < exchange_ptr->Get_exchange_comps().size(); i++)
		{
			add_elt_list(exchange_ptr->Get_exchange_comps()[i].Get_totals(), 1.0);
		}
	}
	if (surface_ptr != NULL)
	{
		for (size_t i = 0; i < surface_ptr->Get_surface_comps().size(); i++)
		{
			add_elt_list(surface_ptr->Get_surface_comps()[i].Get_totals(), 1.0);
		}
	}
	if (ss_assemblage_ptr != NULL)
	{
		std::vector<cxxSS *> ss_ptrs = ss_assemblage_ptr->Vectorize();
		for (size_t i = 0; i < ss_ptrs.size(); i++)
		{
			cxxSS *ss_ptr = ss_ptrs[i];
			for (size_t j = 0; j < ss_ptr->Get_ss_comps().size(); j++)
			{
				cxxSScomp *comp_ptr = &(ss_ptr->Get_ss_comps()[j]);
				int l;
				class phase *phase_ptr = phase_bsearch(comp_ptr->Get_name().c_str(), &l, FALSE);
				add_elt_list(phase_ptr->next_elt,
							 comp_ptr->Get_moles());
			}
		}
	}
	if (gas_phase_ptr != NULL)
	{
		for (size_t j = 0; j < gas_phase_ptr->Get_gas_comps().size(); j++)
		{
			int i;
			class phase *phase_ptr = 
				phase_bsearch(gas_phase_ptr->Get_gas_comps()[j].Get_phase_name().c_str(), &i, FALSE);
			add_elt_list(phase_ptr->next_elt, gas_phase_ptr->Get_gas_comps()[j].Get_moles());
		}
	}
	if (pp_assemblage_ptr != NULL)
	{
		std::map<std::string, cxxPPassemblageComp>::iterator it;
		it =  pp_assemblage_ptr->Get_pp_assemblage_comps().begin();
		for ( ; it != pp_assemblage_ptr->Get_pp_assemblage_comps().end(); it++)
		{
			int j;
			class phase * phase_ptr = phase_bsearch(it->first.c_str(), &j, FALSE);
			add_elt_list(phase_ptr->next_elt,
						 it->second.Get_moles());
		}
	}
	elt_list_combine();
	return (OK);
}

LDBLE Phreeqc::
iso_value(const char *total_name)
{
	int j;
	char token[MAX_LENGTH];
	char my_total_name[MAX_LENGTH];
	strcpy(token, "");
	strcpy(my_total_name, total_name);
	while (replace(" ","_",my_total_name));
	for (j = 0; j < (int)isotope_ratio.size(); j++)
	{
		if (isotope_ratio[j]->ratio == MISSING)
			continue;
		if (strcmp(my_total_name, isotope_ratio[j]->name) != 0)
			continue;
		return (isotope_ratio[j]->converted_ratio);
	}
	strcpy(my_total_name, total_name);
	while (replace("[","",my_total_name));
	while (replace("]","",my_total_name));
	strcat(token,"R(");
	strcat(token,my_total_name);
	strcat(token,")");
	for (j = 0; j < (int)isotope_ratio.size(); j++)
	{
		if (isotope_ratio[j]->ratio == MISSING)
			continue;
		if (strcmp(token, isotope_ratio[j]->name) != 0)
			continue;
		return (isotope_ratio[j]->converted_ratio);
	}
	return -1000.;
}

char * Phreeqc::
iso_unit(const char *total_name)
{
	int j;
	char token[MAX_LENGTH], unit[MAX_LENGTH];
	class master_isotope *master_isotope_ptr;
	char my_total_name[MAX_LENGTH];
	strcpy(token, "");
	strcpy(my_total_name, total_name);
	while (replace(" ","_",my_total_name));
	strcpy(unit, "unknown");
	for (j = 0; j < (int)isotope_ratio.size(); j++)
	{
		if (isotope_ratio[j]->ratio == MISSING)
			continue;
		if (strcmp(my_total_name, isotope_ratio[j]->name) != 0)
			continue;
		master_isotope_ptr = master_isotope_search(isotope_ratio[j]->isotope_name);
		if (master_isotope_ptr != NULL)
		{
			strcpy(unit, master_isotope_ptr->units);
		}
		return string_duplicate(unit);
	}
	strcpy(my_total_name, total_name);
	while (replace("[","",my_total_name));
	while (replace("]","",my_total_name));
	strcat(token,"R(");
	strcat(token,my_total_name);
	strcat(token,")");
	for (j = 0; j < (int)isotope_ratio.size(); j++)
	{
		if (isotope_ratio[j]->ratio == MISSING)
			continue;
		if (strcmp(token, isotope_ratio[j]->name) != 0)
			continue;
		master_isotope_ptr = master_isotope_search(isotope_ratio[j]->isotope_name);
		if (master_isotope_ptr != NULL)
		{
			strcpy(unit, master_isotope_ptr->units);
		}
		return string_duplicate(unit);
	}
	return string_duplicate(unit);
}

int Phreeqc::
basic_compile(const char *commands, void **lnbase, void **vbase, void **lpbase)
{
	return this->basic_interpreter->basic_compile(commands, lnbase, vbase, lpbase);
}

int Phreeqc::
basic_run(char *commands, void *lnbase, void *vbase, void *lpbase)
{
	return this->basic_interpreter->basic_run(commands, lnbase, vbase, lpbase);
}

void Phreeqc::
basic_free(void)
{
	delete this->basic_interpreter;
	this->basic_interpreter = NULL;
}

#if defined(SWIG) || defined(SWIG_IPHREEQC)

#include "BasicCallback.h"

double Phreeqc::
basic_callback(double x1, double x2, const char * str)
{
	if (this->basicCallback)
	{
		return this->basicCallback->Callback(x1, x2, str);
	}
	return 0.0;
}

#else  /* defined(SWIG) || defined(SWIG_IPHREEQC) */

#ifdef IPHREEQC_NO_FORTRAN_MODULE
double Phreeqc::
basic_callback(double x1, double x2, char * str)
#else
double Phreeqc::
basic_callback(double x1, double x2, const char * str)
#endif
{
	double local_x1 = x1;
	double local_x2 = x2;

	if (basic_callback_ptr != NULL)
	{
		return (*basic_callback_ptr) (x1, x2, (const char *) str, basic_callback_cookie);
	}
	if (basic_fortran_callback_ptr != NULL)
	{
#ifdef IPHREEQC_NO_FORTRAN_MODULE
		return (*basic_fortran_callback_ptr) (&local_x1, &local_x2, str, (int) strlen(str));
#else
		return (*basic_fortran_callback_ptr) (&local_x1, &local_x2, str, (int) strlen(str));
#endif
	}
	return 0;
}

void 
Phreeqc::register_basic_callback(double (*fcn)(double x1, double x2, const char *str, void *cookie), void *cookie1)
{
	this->basic_callback_ptr = fcn;
	this->basic_callback_cookie = cookie1;
}
#ifdef IPHREEQC_NO_FORTRAN_MODULE
void 
Phreeqc::register_fortran_basic_callback(double ( *fcn)(double *x1, double *x2, char *str, size_t l))
{
	this->basic_fortran_callback_ptr = fcn;
}
#else

void 
Phreeqc::register_fortran_basic_callback(double ( *fcn)(double *x1, double *x2, const char *str, int l))
{
	this->basic_fortran_callback_ptr = fcn;
}
#endif

#endif  /* defined(SWIG) || defined(SWIG_IPHREEQC) */
