/*************************************************************************
* File	:	detail.cpp	
* Description:	This file contains functions implementing
*		AGA Report No.8 1994 - Detail Method, plus new features
*		required for AGA Report No. 10
*		Contains the functions:
*		Detail(), ~Detail(), compositionchange(), Run(), table(),
*		paramdl(), chardl(), braket(), bvir(), temp(), ddetail(),
*		pdetail(), zdetail(), relativedensity(), dZdT(), d2ZdT2(),
*		dZdD()	
* Version	:	ver 1.7	2002.11.17
* Author	:	W.B. Peterson
*Revisions:
*Copyright (c) 2002  American Gas Association
**************************************************************************/

#include "aga10.h" #include "detail.h" #include <math.h>

/**************************************************************************
*	Function	:	Detail::Detail()
*	Arguments	:	void
*	Returns	:	
*	Purpose	:	default constructor; includes initialization of
*			history-sensitive variables & data tables 4 and 6
*	Revisions	:	
**************************************************************************/
Detail::Detail(void)

{

//initialize history-sensitive variables dOldMixID = 0.0 ; // mixture ID from previous calc

dOldPb = 0.0 ; // base pressure from previous calc dOldTb = 0.0 ; // base temperature from previous calc dOldPf = 0.0 ; // flowing pressure from previous calc dOldTf = 0.0 ; // flowing temperature from previous calc

//initialize gas component array used within this class for (int i=0 ;i<NUMBEROFCOMPONENTS ;i++) dXi[i] = 0 ;


// function table() populates tables of static constants table() ;

}// Detail::Detail()


/**************************************************************************
*	Function	:	Detail::~Detail()
*	Arguments	:	
*	Returns	:	
*	Purpose	:	default destructor
*	Revisions	:	
**************************************************************************/
Detail::~Detail()
{
}// Detail::~Detail()


/**************************************************************************
*	Function	:	Detail::compositionchange()
*	Arguments	:	AGA10STRUCT *
*	Returns	:	void
*	Purpose	:	Compares new composition to old by creating a semi-unique
*			numerical ID. It is possible but very unlikely that 2
*			sequential & different compositions will produce the same ID
*	Revisions	:	
**************************************************************************/

bool Detail::compositionchange(AGA10STRUCT *ptAGA10)

{
double dMixID = 0.0 ; int i ;

// generate the numerical ID for the composition

for (i=0 ; i<NUMBEROFCOMPONENTS ; i++) dMixID += ((i+2) * ptAGA10->adMixture[i]) ;

// update the history variable, if different from previous if (dMixID != dOldMixID)

{
dOldMixID = dMixID ; return true ;




}

else
{
return false;
}

}// Detail::compositionchange()


/**************************************************************************

*	Function	:	Detail::Run()
*	Arguments	:	AGA10STRUCT *
*	Returns	:	void
*	Purpose	:	public method to coordinate and run the full calc sequence
*	Revisions	:	
**************************************************************************/

void Detail::Run(AGA10STRUCT *ptAGA10)

{
int i ;

// Check for gas composition change

ptAGA10->bForceUpdate = (ptAGA10->bForceUpdate || compositionchange(ptAGA10)) ;

// assign component IDs and values if (ptAGA10->bForceUpdate)

{

iNCC = -1 ;
for (i=0 ;i<NUMBEROFCOMPONENTS ;i++)
{

if (ptAGA10->adMixture[i] > 0.0)

{
iNCC = iNCC + 1 ; aiCID[iNCC] = i ;
dXi[iNCC] = ptAGA10->adMixture[i] ;
}

}

iNCC =	iNCC +1 ;

//calculate composition dependent quantities; ported from original
//FORTRAN functions paramdl() and chardl()



paramdl() ; chardl(ptAGA10) ;
}

//evaluate T & P dependent parms at base pressure and temperature,

//but only if necessary

if ((fabs(ptAGA10->dPb - dOldPb) > P_CHG_TOL)|| (fabs(ptAGA10->dTb - dOldTb) > T_CHG_TOL)|| (ptAGA10->bForceUpdate))

{
dP = ptAGA10->dPb * 1.0e-6 ; // AGA 8 uses MPa internally dT = ptAGA10->dTb ;

//calculate temperature dependent parms temp() ;

//determine molar density ddetail(ptAGA10) ; ptAGA10->dDb = dRho ;

//determine compressibility

ptAGA10->dZb = zdetail(dRho) ; // calculate mass density

dRhoTP = (dP * ptAGA10->dMrx) / (ptAGA10->dZb * RGASKJ * dT) ;

//calculate relative density relativedensity(ptAGA10) ;

//copy density to data structure member ptAGA10->dRhob = dRhoTP ;

//update history and clear the ForceUpdate flag dOldTb = ptAGA10->dTb ;
dOldPb = ptAGA10->dPb ; ptAGA10->bForceUpdate = true ;

}


//repeat the process using flowing conditions

//begin by loading P & T from data structure
//AGA 8 uses MPa internally; converted from Pa here dP = ptAGA10->dPf * 1.0e-6 ;
dT = ptAGA10->dTf ;

//check whether to calculate temperature dependent parms

if ((fabs(ptAGA10->dTf - dOldTf) > T_CHG_TOL)||(ptAGA10->bForceUpdate))



{

//if temperature has changed, we must follow through temp() ;

//force ForceUpdate flag to true
ptAGA10->bForceUpdate = true ;

}

// check whether to calculate other parms

if ((fabs(ptAGA10->dPf - dOldPf) > P_CHG_TOL)||(ptAGA10->bForceUpdate))
{

//determine molar density ddetail(ptAGA10) ; ptAGA10->dDf = dRho ;

//determine compressibility ptAGA10->dZf = zdetail(dRho) ;

//calculate mass density

dRhoTP = (dP * ptAGA10->dMrx) / (ptAGA10->dZf * RGASKJ * dT) ;
//copy density to data structure member ptAGA10->dRhof = dRhoTP ;

//update history

dOldTf = ptAGA10->dTf ; dOldPf = ptAGA10->dPf ;
}

//calculate legacy factor Fpv

//NOTE: as implemented here, Fpv is not constrained to 14.73 psi and 60F if ((ptAGA10->dZb > 0.0) && (ptAGA10->dZf > 0.0))
{
ptAGA10->dFpv = sqrt(ptAGA10->dZb / ptAGA10->dZf) ;
}

else
//if either Zb or Zf is zero at this point, we have a serious unexpected problem
{
ptAGA10->dFpv = ptAGA10->dZb = ptAGA10->dZf = 0.0 ; ptAGA10->lStatus = GENERAL_CALCULATION_FAILURE ;
}

//we are now up to date; toggle off the update flag

ptAGA10->bForceUpdate = false ;
}// Detail::Run()





/**************************************************************************

*	Function	:	Detail::table()
*	Arguments	:	void
*	Returns	:	void
*	Purpose	:	builds tables of constants
*	Revisions	:	
**************************************************************************/

//Tables 4 and 6 are filled only during object initialization.

//
//component ID's, mapped to each species supported by AGA Report#8
//	1	- methane	8	- hydrogen	15	- n-hexane
//	2	- nitrogen	9	- carbon monoxide	16	- n-heptane
//	3	- carbon dioxide	10	- oxygen	17	- n-octane
//	4	- ethane	11	- i-butane	18	- n-nonane
//	5	- propane	12	- n-butane	19	- n-decane
//	6	- water	13	- i-pentane	20	- helium
//	7	- hydrogen sulfide	14	- n-pentane	21	- argon

void Detail::table(void)

{
int j, k ;

// 58 constants from table 4 - column A(n) adAn[0] = 0.153832600 ;

adAn[1] = 1.341953000 ; adAn[2] = -2.998583000 ; adAn[3] = -0.048312280 ; adAn[4] = 0.375796500 ; adAn[5] = -1.589575000 ; adAn[6] = -0.053588470 ; adAn[7] = 0.886594630 ; adAn[8] = -0.710237040 ; adAn[9] = -1.471722000 ; adAn[10] = 1.321850350 ; adAn[11] = -0.786659250 ; adAn[12] = 2.29129E-09 ;



adAn[13] = 0.157672400 ; adAn[14] = -0.436386400 ; adAn[15] = -0.044081590 ; adAn[16] = -0.003433888 ; adAn[17] = 0.032059050 ; adAn[18] = 0.024873550 ; adAn[19] = 0.073322790 ; adAn[20] = -0.001600573 ; adAn[21] = 0.642470600 ; adAn[22] = -0.416260100 ; adAn[23] = -0.066899570 ; adAn[24] = 0.279179500 ; adAn[25] = -0.696605100 ; adAn[26] = -0.002860589 ; adAn[27] = -0.008098836 ; adAn[28] = 3.150547000 ; adAn[29] = 0.007224479 ; adAn[30] = -0.705752900 ; adAn[31] = 0.534979200 ; adAn[32] = -0.079314910 ; adAn[33] = -1.418465000 ; adAn[34] = -5.99905E-17 ; adAn[35] = 0.105840200 ; adAn[36] = 0.034317290 ; adAn[37] = -0.007022847 ; adAn[38] = 0.024955870 ; adAn[39] = 0.042968180 ; adAn[40] = 0.746545300 ; adAn[41] = -0.291961300 ; adAn[42] = 7.294616000 ; adAn[43] = -9.936757000 ; adAn[44] = -0.005399808 ; adAn[45] = -0.243256700 ; adAn[46] = 0.049870160 ; adAn[47] = 0.003733797 ; adAn[48] = 1.874951000 ; adAn[49] = 0.002168144 ; adAn[50] = -0.658716400 ; adAn[51] = 0.000205518 ; adAn[52] = 0.009776195 ;






adAn[53] = -0.020487080 ; adAn[54] = 0.015573220 ; adAn[55] = 0.006862415 ; adAn[56] = -0.001226752 ; adAn[57] = 0.002850908 ;

// 58 constants from table 4 - column Un adUn[0] = 0.0 ;

adUn[1] = 0.5 ; adUn[2] = 1.0 ; adUn[3] = 3.5 ; adUn[4] = -0.5 ; adUn[5] = 4.5 ; adUn[6] = 0.5 ; adUn[7] = 7.5 ; adUn[8] = 9.5 ; adUn[9] = 6.0 ; adUn[10] = 12.0; adUn[11] = 12.5; adUn[12] = -6.0; adUn[13] = 2.0 ; adUn[14] = 3.0 ; adUn[15] = 2.0 ; adUn[16] = 2.0 ; adUn[17] = 11.0; adUn[18] = -0.5 ; adUn[19] = 0.5 ; adUn[20] = 0.0 ; adUn[21] = 4.0 ; adUn[22] = 6.0 ; adUn[23] = 21.0; adUn[24] = 23.0; adUn[25] = 22.0; adUn[26] = -1.0 ; adUn[27] = -0.5 ; adUn[28] = 7.0 ; adUn[29] = -1.0 ; adUn[30] = 6.0 ; adUn[31] = 4.0 ; adUn[32] = 1.0 ;






adUn[33] = 9.0 ; adUn[34] = -13.0; adUn[35] = 21.0; adUn[36] = 8.0 ; adUn[37] = -0.5 ; adUn[38] = 0.0 ; adUn[39] = 2.0 ; adUn[40] = 7.0 ; adUn[41] = 9.0 ; adUn[42] = 22.0; adUn[43] = 23.0; adUn[44] = 1.0 ; adUn[45] = 9.0 ; adUn[46] = 3.0 ; adUn[47] = 8.0 ; adUn[48] = 23.0; adUn[49] = 1.5 ; adUn[50] = 5.0 ; adUn[51] = -0.5 ; adUn[52] = 4.0 ; adUn[53] = 7.0 ; adUn[54] = 3.0 ; adUn[55] = 0.0 ; adUn[56] = 1.0 ; adUn[57] = 0.0 ;


//Most of the tables are filled with 1.0 or 0.0

//It is up to us to set non-zero values

for (j=0 ; j < NUMBEROFCOMPONENTS ; j++)

{
for (k=j ; k < NUMBEROFCOMPONENTS ; k++)
{

adTable6Eij[j][k] = 1.0 ; adTable6Uij[j][k] = 1.0 ; adTable6Kij[j][k] = 1.0 ; adTable6Gij[j][k] = 1.0 ;

}
}




//Lnsert the 132 items of non-zero and non-1.0 data

//This looks more cumbersome than it is, considering table 6 has 1764 members adTable6Eij[0][1] = 0.971640 ;

adTable6Eij[0][2] = 0.960644 ; adTable6Eij[0][4] = 0.994635 ; adTable6Eij[0][5] = 0.708218 ; adTable6Eij[0][6] = 0.931484 ; adTable6Eij[0][7] = 1.170520 ; adTable6Eij[0][8] = 0.990126 ; adTable6Eij[0][10] = 1.019530 ; adTable6Eij[0][11] = 0.989844 ; adTable6Eij[0][12] = 1.002350 ; adTable6Eij[0][13] = 0.999268 ; adTable6Eij[0][14] = 1.107274 ; adTable6Eij[0][15] = 0.880880 ; adTable6Eij[0][16] = 0.880973 ; adTable6Eij[0][17] = 0.881067 ; adTable6Eij[0][18] = 0.881161 ; adTable6Eij[1][2] = 1.022740 ; adTable6Eij[1][3] = 0.970120 ; adTable6Eij[1][4] = 0.945939 ; adTable6Eij[1][5] = 0.746954 ; adTable6Eij[1][6] = 0.902271 ; adTable6Eij[1][7] = 1.086320 ; adTable6Eij[1][8] = 1.005710 ; adTable6Eij[1][9] = 1.021000 ; adTable6Eij[1][10] = 0.946914 ; adTable6Eij[1][11] = 0.973384 ; adTable6Eij[1][12] = 0.959340 ; adTable6Eij[1][13] = 0.945520 ; adTable6Eij[2][3] = 0.925053 ; adTable6Eij[2][4] = 0.960237 ; adTable6Eij[2][5] = 0.849408 ; adTable6Eij[2][6] = 0.955052 ; adTable6Eij[2][7] = 1.281790 ; adTable6Eij[2][8] = 1.500000 ; adTable6Eij[2][10] = 0.906849 ; adTable6Eij[2][11] = 0.897362 ; adTable6Eij[2][12] = 0.726255 ;




adTable6Eij[2][13] = 0.859764 ; adTable6Eij[2][14] = 0.855134 ; adTable6Eij[2][15] = 0.831229 ; adTable6Eij[2][16] = 0.808310 ; adTable6Eij[2][17] = 0.786323 ; adTable6Eij[2][18] = 0.765171 ; adTable6Eij[3][4] = 1.022560 ; adTable6Eij[3][5] = 0.693168 ; adTable6Eij[3][6] = 0.946871 ; adTable6Eij[3][7] = 1.164460 ; adTable6Eij[3][11] = 1.013060 ; adTable6Eij[3][13] = 1.005320 ; adTable6Eij[4][7] = 1.034787 ; adTable6Eij[4][11] = 1.004900 ; adTable6Eij[6][14] = 1.008692 ; adTable6Eij[6][15] = 1.010126 ; adTable6Eij[6][16] = 1.011501 ; adTable6Eij[6][17] = 1.012821 ; adTable6Eij[6][18] = 1.014089 ; adTable6Eij[7][8] = 1.100000 ; adTable6Eij[7][10] = 1.300000 ; adTable6Eij[7][11] = 1.300000 ; adTable6Uij[0][1] = 0.886106 ; adTable6Uij[0][2] = 0.963827 ; adTable6Uij[0][4] = 0.990877 ; adTable6Uij[0][6] = 0.736833 ; adTable6Uij[0][7] = 1.156390 ; adTable6Uij[0][11] = 0.992291 ; adTable6Uij[0][13] = 1.003670 ; adTable6Uij[0][14] = 1.302576 ; adTable6Uij[0][15] = 1.191904 ; adTable6Uij[0][16] = 1.205769 ; adTable6Uij[0][17] = 1.219634 ; adTable6Uij[0][18] = 1.233498 ; adTable6Uij[1][2] = 0.835058 ; adTable6Uij[1][3] = 0.816431 ; adTable6Uij[1][4] = 0.915502 ; adTable6Uij[1][6] = 0.993476 ; adTable6Uij[1][7] = 0.408838 ; adTable6Uij[1][11] = 0.993556 ;




adTable6Uij[2][3] = 0.969870 ; adTable6Uij[2][6] = 1.045290 ; adTable6Uij[2][8] = 0.900000 ; adTable6Uij[2][14] = 1.066638 ; adTable6Uij[2][15] = 1.077634 ; adTable6Uij[2][16] = 1.088178 ; adTable6Uij[2][17] = 1.098291 ; adTable6Uij[2][18] = 1.108021 ; adTable6Uij[3][4] = 1.065173 ; adTable6Uij[3][6] = 0.971926 ; adTable6Uij[3][7] = 1.616660 ; adTable6Uij[3][10] = 1.250000 ; adTable6Uij[3][11] = 1.250000 ; adTable6Uij[3][12] = 1.250000 ; adTable6Uij[3][13] = 1.250000 ; adTable6Uij[6][14] = 1.028973 ; adTable6Uij[6][15] = 1.033754 ; adTable6Uij[6][16] = 1.038338 ; adTable6Uij[6][17] = 1.042735 ; adTable6Uij[6][18] = 1.046966 ; adTable6Kij[0][1] = 1.003630 ; adTable6Kij[0][2] = 0.995933 ; adTable6Kij[0][4] = 1.007619 ; adTable6Kij[0][6] = 1.000080 ; adTable6Kij[0][7] = 1.023260 ; adTable6Kij[0][11] = 0.997596 ; adTable6Kij[0][13] = 1.002529 ; adTable6Kij[0][14] = 0.982962 ; adTable6Kij[0][15] = 0.983565 ; adTable6Kij[0][16] = 0.982707 ; adTable6Kij[0][17] = 0.981849 ; adTable6Kij[0][18] = 0.980991 ; adTable6Kij[1][2] = 0.982361 ; adTable6Kij[1][3] = 1.007960 ; adTable6Kij[1][6] = 0.942596 ; adTable6Kij[1][7] = 1.032270 ; adTable6Kij[2][3] = 1.008510 ; adTable6Kij[2][6] = 1.007790 ; adTable6Kij[2][14] = 0.910183 ; adTable6Kij[2][15] = 0.895362 ;




adTable6Kij[2][16] = 0.881152 ; adTable6Kij[2][17] = 0.867520 ; adTable6Kij[2][18] = 0.854406 ; adTable6Kij[3][4] = 0.986893 ; adTable6Kij[3][6] = 0.999969 ; adTable6Kij[3][7] = 1.020340 ; adTable6Kij[6][14] = 0.968130 ; adTable6Kij[6][15] = 0.962870 ; adTable6Kij[6][16] = 0.957828 ; adTable6Kij[6][17] = 0.952441 ; adTable6Kij[6][18] = 0.948338 ; adTable6Gij[0][2] = 0.807653 ; adTable6Gij[0][7] = 1.957310 ; adTable6Gij[1][2] = 0.982746 ; adTable6Gij[2][3] = 0.370296 ; adTable6Gij[2][5] = 1.673090 ;
}// Detail::table()



/**************************************************************************
*	Function	:	Detail::paramdl()
*	Arguments	:	void
*	Returns	:	void
*	Purpose	:	sets up characterization & binary interaction parameters
*	Revisions	:	
**************************************************************************/

void Detail::paramdl(void)

{

int j, k ;

// table 5 parameters; declared locally to this function

const double adTable5Mri[NUMBEROFCOMPONENTS] = {16.0430, 28.0135, 44.0100, 30.0700, 44.0970, 18.0153, 34.0820, 2.0159, 28.0100, 31.9988, 58.1230, 58.1230, 72.1500, 72.1500, 86.1770, 100.2040,114.2310,128.2580,142.2850,4.0026, 39.9480} ;




const double adTable5Ei[NUMBEROFCOMPONENTS] = {151.318300, 99.737780, 241.960600, 244.166700, 298.118300, 514.015600, 296.355000, 26.957940, 105.534800, 122.766700, 324.068900, 337.638900, 365.599900, 370.682300, 402.636293, 427.722630, 450.325022, 470.840891, 489.558373, 2.610111, 119.629900} ;

const double adTable5Ki[NUMBEROFCOMPONENTS] = {0.4619255, 0.4479153, 0.4557489, 0.5279209, 0.5837490, 0.3825868, 0.4618263, 0.3514916, 0.4533894, 0.4186954, 0.6406937, 0.6341423, 0.6738577, 0.6798307, 0.7175118, 0.7525189, 0.7849550, 0.8152731, 0.8437826, 0.3589888, 0.4216551} ;

const double adTable5Gi[NUMBEROFCOMPONENTS] = {0.000000,0.027815,0.189065,0.079300,0.141239, 0.332500,0.088500,0.034369,0.038953,0.021000, 0.256692,0.281835,0.332267,0.366911,0.289731, 0.337542,0.383381,0.427354,0.469659,0.000000, 0.000000} ;


//most of the table 5 parameters are zero for (j=0 ; j < NUMBEROFCOMPONENTS ; j++)
{
adTable5Qi[j] = 0.0 ; adTable5Fi[j] = 0.0 ; adTable5Si[j] = 0.0 ; adTable5Wi[j] = 0.0 ;

}

//a small number of exceptions adTable5Qi[2] = 0.690000 ; adTable5Qi[5] = 1.067750 ; adTable5Qi[6] = 0.633276 ;

adTable5Fi[7] = 1.0000 ;




adTable5Si[5] = 1.5822 ; adTable5Si[6] = 0.3900 ;

adTable5Wi[5] = 1.0000 ;

// setup characterization parameters for non-zero components for (j=iNCC-1 ; j >= 0 ; j--)

{
dMri[j] = adTable5Mri[aiCID[j]] ; dKi[j] = adTable5Ki[aiCID[j]] ;
}

for (j=0 ; j < iNCC ; j++)

{
dGi[j] = adTable5Gi[aiCID[j]] ; dEi[j] = adTable5Ei[aiCID[j]] ;
}

for (j=0 ; j < iNCC ; j++)

{

dQi[j] = adTable5Qi[aiCID[j]] ; dFi[j] = 0.0 ;

if (aiCID[j] == 7) dFi[j] = adTable5Fi[7] ; dSi[j] = adTable5Si[aiCID[j]] ;

dWi[j] = adTable5Wi[aiCID[j]] ;

}

// Binary interaction parameters for arrays: eij, kij, wij, uij for (j=0 ; j < iNCC ; j++)
{
for (k=j ; k < iNCC ; k++)
{

dUij[j][k] = adTable6Uij[aiCID[j]][aiCID[k]] ; dKij[j][k] = adTable6Kij[aiCID[j]][aiCID[k]] ; dEij[j][k] = adTable6Eij[aiCID[j]][aiCID[k]] ; dGij[j][k] = adTable6Gij[aiCID[j]][aiCID[k]] ;

}
}

}// Detail::paramdl()




/**************************************************************************
*	Function	:	Detail::chardl()
*	Arguments	:	AGA10STRUCT *
*	Returns	:	void
*	Purpose	:	computes composition-dependent quantities
*	Revisions	:	
**************************************************************************/

void Detail::chardl(AGA10STRUCT *ptAGA10)

{

//variables local to function int i,j ;

double tmfrac, k5p0, k2p5, u5p0, u2p5, q1p0 ;
double Xij, Eij, Gij, e0p5, e2p0, e3p0, e3p5, e4p5, e6p0 ; double e7p5,e9p5,e12p0,e12p5 ;
double e11p0, s3 ;

//normalize mole fractions and calculate molar mass tmfrac = 0.0 ;

for (j=0 ; j < iNCC ; j++)

{
tmfrac = tmfrac + dXi[j] ;
}

for (j=0 ; j < iNCC ; j++)

{
dXi[j] = dXi[j]/tmfrac ;

}

// reset virial coefficients

for (j=0 ; j < 18 ; j++)

{

adBcoef[j] = 0.0 ;
}

// initialize a key subset of the local variables k5p0 = 0.0 ;




k2p5 = 0.0 ; u5p0 = 0.0 ; u2p5 = 0.0 ; dW = 0.0 ; q1p0 = 0.0 ; dF = 0.0 ;

// calculate gas molecular weight ptAGA10->dMrx = 0.0 ;

for (j=0 ; j < iNCC ; j++)

{
ptAGA10->dMrx = ptAGA10->dMrx + dXi[j] * dMri[j] ;
}

// calculate the composition-dependent quantities, applying a nested loop for (i=0 ; i < iNCC ; i++)

{
k2p5	= k2p5	+ dXi[i] * dKi[i] * dKi[i] * sqrt(dKi[i]) ;
u2p5	= u2p5	+ dXi[i] * dEi[i] * dEi[i] * sqrt(dEi[i]) ;
dW	= dW	+ dXi[i] * dGi[i] ;
q1p0	= q1p0	+ dXi[i] * dQi[i] ;
dF	= dF	+ dXi[i] * dXi[i] * dFi[i] ;

for (j=i ; j < iNCC ; j++)

{

if (i != j) Xij = 2.0 * dXi[i] * dXi[j] ; else Xij = dXi[i] * dXi[j] ;

// proceed while skipping interaction terms which equal 1.0 if (dKij[i][j] != 1.0)

k5p0 += Xij * (pow(dKij[i][j],5.0) - 1.0) * pow((pow(dKi[i],5.0) * pow(dKi[j],5.0)),0.5) ;

if (dUij[i][j] != 1.0)

u5p0 += Xij * (pow(dUij[i][j],5.0) - 1.0) * pow((pow(dEi[i],5.0) * pow(dEi[j],5.0)),0.5) ;

if (dGij[i][j] != 1.0)

dW += Xij * (dGij[i][j] - 1.0) * ((dGi[i] + dGi[j]) / 2.0) ;




// calculate terms required for second virial coefficient, B Eij = dEij[i][j] * sqrt(dEi[i] * dEi[j]) ;

Gij = dGij[i][j] * (dGi[i] + dGi[j]) / 2.0 ;

e0p5 = sqrt(Eij) ;

e2p0 = Eij * Eij ; e3p0 = Eij * e2p0 ; e3p5 = e3p0 * e0p5 ; e4p5 = Eij * e3p5 ; e6p0 = e3p0 * e3p0 ;
e11p0= e4p5 * e4p5 * e2p0 ; e7p5 = e4p5 * Eij * e2p0 ; e9p5 = e7p5 * e2p0 ;

e12p0= e11p0 * Eij ; e12p5= e12p0 * e0p5 ;

s3 = Xij * pow((pow(dKi[i], 3.0) * pow(dKi[j],3)), 0.5) ;

adBcoef[0] = adBcoef[0] + s3 ; adBcoef[1] = adBcoef[1] + s3 * e0p5 ; adBcoef[2] = adBcoef[2] + s3 * Eij ; adBcoef[3] = adBcoef[3] + s3 * e3p5 ;

adBcoef[4] = adBcoef[4] + s3 * Gij / e0p5 ; adBcoef[5] = adBcoef[5] + s3 * Gij * e4p5 ;
adBcoef[6] = adBcoef[6] + s3 * dQi[i] * dQi[j] * e0p5 ; adBcoef[7] = adBcoef[7] + s3 * dSi[i] * dSi[j] * e7p5 ; adBcoef[8] = adBcoef[8] + s3 * dSi[i] * dSi[j] * e9p5 ; adBcoef[9] = adBcoef[9] + s3 * dWi[i] * dWi[j] * e6p0 ; adBcoef[10] = adBcoef[10]+ s3 * dWi[i] * dWi[j] * e12p0 ; adBcoef[11] = adBcoef[11]+ s3 * dWi[i] * dWi[j] * e12p5 ; adBcoef[12] = adBcoef[12] + s3 * dFi[i] * dFi[j] / e6p0 ; adBcoef[13] = adBcoef[13] + s3 * e2p0 ;

adBcoef[14] = adBcoef[14] + s3 * e3p0 ;

adBcoef[15] = adBcoef[15] + s3 * dQi[i] * dQi[j] * e2p0 ; adBcoef[16] = adBcoef[16] + s3 * e2p0 ;

adBcoef[17] = adBcoef[17] + s3 * e11p0 ;
}

}




//grab the first 18 constants from table 4, completing Bnij for (i=0 ; i < 18 ; i++) adBcoef[i] *= adAn[i] ;

//final products of chardl are mixture size parameter K, energy parameter U,

//and quadrupole parameter Q

dKp3 = pow((k5p0 + k2p5 * k2p5), 0.6) ; dU = pow((u5p0 + u2p5 * u2p5), 0.2) ; dQp2 = q1p0 * q1p0 ;

}// Detail::chardl()



/**************************************************************************

*	Function	:	Detail::bvir()
*	Arguments	:	void	
*	Returns		:	void
*	Purpose		:	computes 2nd virial coefficient & partial derivs thereof
*	Revisions	:		
**************************************************************************/

void Detail::bvir(void)

{
//variables local to function

double t0p5, t2p0, t3p0, t3p5, t4p5, t6p0, t11p0 ; double t7p5, t9p5, t12p0, t12p5 ;

double t1p5, t4p0 ; double Bx[18] ; int i ;

//reset B and partial devivatives to 0.0 dB = ddBdT = dd2BdT2 = 0.0 ;

//pre-calculate powers of T

t0p5 = sqrt(dT) ; t2p0 = dT * dT ; t3p0 = dT * t2p0 ; t3p5 = t3p0 * t0p5 ; t4p5 = dT * t3p5 ; t6p0 = t3p0 * t3p0 ;




t11p0 = t4p5 * t4p5 * t2p0 ; t7p5 = t6p0 * dT * t0p5 ; t9p5 = t7p5 * t2p0 ;

t12p0 = t9p5 * t0p5 * t2p0 ; t12p5 = t12p0 * t0p5 ;

t1p5 = dT * t0p5 ; t4p0 = t2p0 * t2p0 ;

//coefficients for B Bx[0] = adBcoef[0] ;
Bx[1] = adBcoef[1] / t0p5 ; Bx[2] = adBcoef[2] / dT ; Bx[3] = adBcoef[3] / t3p5 ; Bx[4] = adBcoef[4] * t0p5 ; Bx[5] = adBcoef[5] / t4p5 ; Bx[6] = adBcoef[6] / t0p5 ; Bx[7] = adBcoef[7] / t7p5 ; Bx[8] = adBcoef[8] / t9p5 ; Bx[9] = adBcoef[9] / t6p0 ; Bx[10] = adBcoef[10] / t12p0 ; Bx[11] = adBcoef[11] / t12p5 ; Bx[12] = adBcoef[12] * t6p0 ; Bx[13] = adBcoef[13] / t2p0 ; Bx[14] = adBcoef[14] / t3p0 ; Bx[15] = adBcoef[15] / t2p0 ; Bx[16] = adBcoef[16] / t2p0 ; Bx[17] = adBcoef[17] / t11p0 ;

//sum up the pieces for second virial coefficient, B for (i= 0; i < 18; i++)
{
dB += Bx[i] ;
}

//calculate terms for first derivative of B, wrt T for (i= 0; i < 18; i++)

{
if (adUn[i]) Bx[i] *= adUn[i] ;

}




//sum up the pieces of first derivative of B

//note div by dT; changes exponent of T
for (i= 0; i < 18; i++)
{

if (adUn[i])

ddBdT += Bx[i] / dT ;
}

//sign change here ddBdT = -ddBdT ;

//calculate terms for second derivative of B, wrt T for (i= 0; i < 18; i++)

{
if (adUn[i] && adUn[i] != -1.0) Bx[i] *= (adUn[i] + 1.0) ;
}

//sum up the pieces of second derivative of B

//note division by dT, thereby changing the exponent of T

//loop will ignore Bx[0] which is = 0.0
for (i= 0; i < 18; i++)
{

if (adUn[i] && adUn[i] != -1.0) dd2BdT2 += Bx[i] / t2p0 ;
}

}// Detail::bvir()




/**************************************************************************
*	Function	:	Detail::temp()
*	Arguments	:	void
*	Returns	:	void
*	Purpose	:	computes temperature-dependent quantities
*	Revisions	:	
**************************************************************************/

void Detail::temp(void)

{

//Note: this function was ported from the AGA Report No.8 FORTRAN listing,
//retaining as much of the original content as possible

//variables local to function

double tr0p5, tr1p5, tr2p0, tr3p0, tr4p0, tr5p0, tr6p0 ; double tr7p0, tr8p0, tr9p0, tr11p0, tr13p0, tr21p0 ; double tr22p0, tr23p0, tr ;

/*	calculate second virial coefficient B	*/

bvir() ;

//calculate adFn(12) through adFn(57)

//adFn(0)-adFn(11) do not contribute to csm terms
tr	= dT / (dU) ;

tr0p5	= sqrt(tr) ;

tr1p5	= tr * tr0p5 ;
tr2p0	= tr * tr ;
tr3p0	= tr * tr2p0 ;

tr4p0	= tr * tr3p0 ;

tr5p0	= tr * tr4p0 ;
tr6p0	= tr * tr5p0 ;
tr7p0	= tr * tr6p0 ;
tr8p0	= tr * tr7p0 ;

tr9p0	= tr * tr8p0 ;

tr11p0 = tr6p0 * tr5p0 ;
tr13p0 = tr6p0 * tr7p0 ;
tr21p0 = tr9p0 * tr9p0 * tr3p0 ;
tr22p0 = tr * tr21p0 ;

tr23p0 = tr * tr22p0 ;




adFn[12] = adAn[12] * dF * tr6p0 ; adFn[13] = adAn[13] / tr2p0 ; adFn[14] = adAn[14] / tr3p0 ; adFn[15] = adAn[15] * dQp2 / tr2p0 ; adFn[16] = adAn[16] / tr2p0 ; adFn[17] = adAn[17] / tr11p0 ; adFn[18] = adAn[18] * tr0p5 ; adFn[19] = adAn[19] / tr0p5 ; adFn[20] = adAn[20] ;

adFn[21] = adAn[21] / tr4p0 ; adFn[22] = adAn[22] / tr6p0 ; adFn[23] = adAn[23] / tr21p0 ; adFn[24] = adAn[24] * dW / tr23p0 ; adFn[25] = adAn[25] * dQp2 / tr22p0 ; adFn[26] = adAn[26] * dF * tr ; adFn[27] = adAn[27] * dQp2 * tr0p5 ; adFn[28] = adAn[28] * dW / tr7p0 ; adFn[29] = adAn[29] * dF * tr ; adFn[30] = adAn[30] / tr6p0 ; adFn[31] = adAn[31] * dW / tr4p0 ; adFn[32] = adAn[32] * dW / tr ; adFn[33] = adAn[33] * dW / tr9p0 ; adFn[34] = adAn[34] * dF * tr13p0 ; adFn[35] = adAn[35] / tr21p0 ; adFn[36] = adAn[36] * dQp2 / tr8p0 ; adFn[37] = adAn[37] * tr0p5 ; adFn[38] = adAn[38] ;

adFn[39] = adAn[39] / tr2p0 ; adFn[40] = adAn[40] / tr7p0 ; adFn[41] = adAn[41] * dQp2 / tr9p0 ; adFn[42] = adAn[42] / tr22p0 ; adFn[43] = adAn[43] / tr23p0 ; adFn[44] = adAn[44] / tr ;

adFn[45] = adAn[45] / tr9p0 ; adFn[46] = adAn[46] * dQp2 / tr3p0 ; adFn[47] = adAn[47] / tr8p0 ; adFn[48] = adAn[48] * dQp2 / tr23p0 ; adFn[49] = adAn[49] / tr1p5 ; adFn[50] = adAn[50] * dW / tr5p0 ;




adFn[51] = adAn[51] * dQp2 * tr0p5 ; adFn[52] = adAn[52] / tr4p0 ; adFn[53] = adAn[53] * dW / tr7p0 ; adFn[54] = adAn[54] / tr3p0 ; adFn[55] = adAn[55] * dW ;

adFn[56] = adAn[56] / tr ; adFn[57] = adAn[57] * dQp2 ;
}// Detail::temp()



/**************************************************************************
*	Function	:	Detail::ddetail()
*	Arguments	:	AGA10STRUCT *
*	Returns	:	void
*	Purpose	:	calculates density
*	Revisions	:	
**************************************************************************/

//Note: this function was ported from the AGA Report No.8 FORTRAN listing,

//retaining as much of the original content as possible

void Detail::ddetail(AGA10STRUCT *ptAGA10)

{
int imax, i ;

double epsp, epsr, epsmin ; double x1, x2, x3, y1, y2, y3 ;
double delx, delprv, delmin, delbis, xnumer, xdenom, sgndel ; double y2my3, y3my1, y1my2, boundn ;

//initialize convergence tolerances imax = 150 ;

epsp = 1.e-6 ; epsr = 1.e-6 ; epsmin = 1.e-7 ; dRho =0.0 ;

//call subroutine braket to bracket density solution braket(ptAGA10) ;


//check value of "lStatus" returned from subroutine braket if (ptAGA10->lStatus == MAX_NUM_OF_ITERATIONS_EXCEEDED ||
ptAGA10->lStatus == NEGATIVE_DENSITY_DERIVATIVE)
{
return ;
}

//set up to start Brent's method

//x is the independent variable, y the dependent variable
//delx is the current iteration change in x
//delprv is the previous iteration change in x

x1 = dRhoL ;
x2 = dRhoH ;
y1 = dPRhoL - dP ;

y2 = dPRhoH - dP ; delx = x1 - x2 ; delprv = delx ;

//solution is bracketed between x1 and x2

//a third point x3 is introduced for quadratic interpolation x3 = x1 ;
y3 = y1 ;

for (i=0 ; i < imax ; i++)

{
//y3 must be opposite in sign from y2 so solution between x2,x3 if (y2 * y3 > 0.0)
{
x3 = x1 ;
y3 = y1 ;

delx = x1 - x2 ; delprv = delx ;

}

//y2 must be value of y closest to y=0.0, then x2new=x2old+delx

if (fabs(y3) < fabs(y2))

{
x1 = x2 ;

x2 = x3 ;




x3 = x1 ;

y1 = y2 ;
y2 = y3 ;
y3 = y1 ;
}

//delmin is minimum allowed step size for unconverged iteration delmin = epsmin * fabs(x2) ;
//if procedure is not converging or if delprv is less than delmin

//use bisection instead
//delbis = 0.5d0*(x3 - x2) is the bisection delx

delbis = 0.5 * (x3 - x2) ;

// tests to select numerical method for current iteration

if (fabs(delprv) < delmin || fabs(y1) < fabs(y2))

{
// use bisection delx = delbis ; delprv = delbis ;
}
else

{

if (x3 != x1)
{

// use inverse quadratic interpolation y2my3 = y2 - y3 ;

y3my1 = y3 - y1 ; y1my2 = y1 - y2 ;

xdenom = -(y1my2) * (y2my3) * (y3my1) ; xnumer = x1 * y2 * y3 * (y2my3)

+x2 * y3 * y1 * (y3my1)

+x3 * y1 * y2 * (y1my2) - x2 * xdenom ;

}
else

{

// use inverse linear interpolation xnumer = (x2-x1)*y2 ;
xdenom = y1-y2 ;
}

// before calculating delx check delx=xnumer/xdenom is not out of bounds if (2.0 * fabs(xnumer) < fabs(delprv * xdenom))

{
// procedure converging, use interpolation delprv = delx ;

delx = xnumer / xdenom ;

}
else
{

// procedure diverging, use bisection delx = delbis ;
delprv = delbis ;
}
}

// check for convergence

if ((fabs(y2) < epsp * dP) && (fabs(delx) < epsr * fabs(x2)))
{

dRho = x2 + delx ; return ;

}

//when unconverged, abs(delx) must be greater than delmin

//minimum allowed magnitude of change in x2 is 1.0000009*delmin
//sgndel, the sign of change in x2 is sign of delbis

if (fabs(delx) < delmin)
{

sgndel = delbis / fabs(delbis) ; delx = 1.0000009 * sgndel * delmin ; delprv = delx ;

}

//final check to insure that new x2 is in range of old x2 and x3

//boundn is negative if new x2 is in range of old x2 and x3 boundn = delx * (x2 + delx - x3) ;


if (boundn > 0.0)

{

// procedure stepping out of bounds, use bisection delx = delbis ;

delprv = delbis ;
}

//relable variables for next iteration

//x1new = x2old, y1new=y2old
x1 = x2 ;

y1 = y2 ;

// next iteration values for x2, y2 x2 = x2 + delx ;

pdetail(x2) ;
y2 = dPCalc - dP ;

}

// ddetail: maximum number of iterations exceeded ptAGA10->lStatus=MAX_NUM_OF_ITERATIONS_EXCEEDED ; dRho = x2 ;

}// Detail::ddetail()



/**************************************************************************
*	Function	:	Detail::braket()
*	Arguments	:	AGA10STRUCT *
*	Returns	:	void
*	Purpose	:	brackets density solution
*	Revisions	:	
**************************************************************************/

//Note: this function was ported from the AGA Report No.8 FORTRAN listing,

//retaining as much of the original content as possible

void Detail::braket(AGA10STRUCT *ptAGA10)

{
//variables local to function int imax, it ;

double del, rhomax, videal ; double rho1, rho2, p1, p2 ;

//initialize

imax = 200 ; rho1 = 0.0 ; p1 = 0.0 ;
rhomax = 1.0 / dKp3 ;

if (dT > 1.2593 * dU) rhomax = 20.0 * rhomax ; videal = RGASKJ * dT / dP ;

if (fabs(dB) < (0.167 * videal))

{
rho2 = 0.95 / (videal + dB) ;

}

else
{
rho2 = 1.15 / videal ;
}

del = rho2 / 20.0 ;

// start iterative density search loop for (it = 0; it < imax ; it++)

{




if (rho2 > rhomax && ptAGA10->lStatus != MAX_DENSITY_IN_BRAKET_EXCEEDED)

{

// density in braket exceeds maximum allowable density ptAGA10->lStatus = MAX_DENSITY_IN_BRAKET_EXCEEDED ;

del = 0.01 * (rhomax - rho1) + (dP / (RGASKJ * dT)) / 20.0 ; rho2 = rho1 + del ;
continue ;
}

//calculate pressure p2 at density rho2 pdetail(rho2) ;
p2 = dPCalc ;

//test value of p2 relative to p and relative to p1 if (p2 > dP)

{

//the density root is bracketed (p1<p and p2>p) dRhoL = rho1 ;

dPRhoL = p1 ; dRhoH = rho2 ;

dPRhoH = p2 ; ptAGA10->lStatus = NORMAL ; return;

}

else if (p2 > p1)

{
if (ptAGA10->lStatus == MAX_DENSITY_IN_BRAKET_EXCEEDED) del *= 2.0 ; rho1 = rho2 ;

p1 = p2 ;

rho2 = rho1 + del ; continue ;

}
else

{

//lStatus= NEGATIVE_DENSITY_DERIVATIVEindicates that
//pressure has a negative density derivative, since p2 is less than
//some previous pressure

ptAGA10->lStatus = NEGATIVE_DENSITY_DERIVATIVE; dRho = rho1;




return;

}

}

// maximum number of iterations exceeded if we fall through the bottom ptAGA10->lStatus = MAX_NUM_OF_ITERATIONS_EXCEEDED ;

dRho = rho2 ; return ;

}// Detail::braket()


/**************************************************************************
*	Function	:	Detail::pdetail()
*	Arguments	:	double
*	Returns	:	void
*	Purpose	:	calculates pressure, given D and T. Calls zdetail()
*	Revisions	:	
**************************************************************************/

void Detail::pdetail(double dD)

{
dPCalc = zdetail(dD) * dD * RGASKJ * dT ;
}// Detail::pdetail()



/**************************************************************************
*	Function	:	Detail::zdetail()
*	Arguments	:	double
*	Returns	:	void
*	Purpose	:	calculates compressibility
*	Revisions	:	
**************************************************************************/

double Detail::zdetail(double d)

{
// variables local to function

double D1, D2, D3, D4, D5, D6, D7, D8, D9, exp1, exp2, exp3, exp4 ;

// powers of reduced density D1 = dKp3 * d ;

D2 = D1 * D1 ;

D3 = D2 * D1 ;
D4 = D3 * D1 ;
D5 = D4 * D1 ;
D6 = D5 * D1 ;

D7 = D6 * D1 ;

D8 = D7 * D1 ;
D9 = D8 * D1 ;

exp1 = exp(-D1) ; exp2 = exp(-D2) ; exp3 = exp(-D3) ; exp4 = exp(-D4) ;

// the following expression for Z was adopted from FORTRAN example in AGA8 dZ = 1.0 + dB * d

+adFn[12] * D1 * (exp3 - 1.0 - 3.0 * D3 * exp3)
+(adFn[13] + adFn[14] + adFn[15]) * D1 * (exp2 - 1.0 - 2.0 * D2 * exp2)
+(adFn[16] + adFn[17]) * D1 * (exp4 - 1.0 - 4.0 * D4 * exp4)
+(adFn[18] + adFn[19]) * D2 * 2.0

+(adFn[20] + adFn[21] + adFn[22]) * D2 * (2.0 - 2.0 * D2) * exp2
+(adFn[23] + adFn[24] + adFn[25]) * D2 * (2.0 - 4.0 * D4) * exp4
+adFn[26] * D2 * (2.0 - 4.0 * D4) * exp4
+adFn[27] * D3 * 3.0
+(adFn[28] + adFn[29]) * D3 * (3.0 - D1) * exp1




+(adFn[30] + adFn[31]) * D3 * (3.0 - 2.0 * D2) * exp2

+(adFn[32] + adFn[33]) * D3 * (3.0 - 3.0 * D3) * exp3
+(adFn[34] + adFn[35] + adFn[36]) * D3 * (3.0 - 4.0 * D4) * exp4
+(adFn[37] + adFn[38]) * D4 * 4.0
+(adFn[39] + adFn[40] + adFn[41]) * D4 * (4.0 - 2.0 * D2) * exp2
+(adFn[42] + adFn[43]) * D4 * (4.0 - 4.0 * D4) * exp4

+adFn[44] * D5 * 5.0
+(adFn[45] + adFn[46]) * D5 * (5.0 - 2.0 * D2) * exp2
+(adFn[47] + adFn[48]) * D5 * (5.0 - 4.0 * D4) * exp4
+adFn[49] * D6 * 6.0
+adFn[50] * D6 * (6.0 - 2.0 * D2) * exp2

+adFn[51] * D7 * 7.0
+adFn[52] * D7 * (7.0 - 2.0 * D2) * exp2
+adFn[53] * D8 * (8.0 - D1) * exp1
+(adFn[54] + adFn[55]) * D8 * (8.0 - 2.0 * D2) * exp2
+(adFn[56] + adFn[57]) * D9 * (9.0 - 2.0 * D2) * exp2 ;

return dZ ;

}// Detail::zdetail()



/**************************************************************************
*	Function	:	Detail::dZdT()
*	Arguments	:	double
*	Returns	:	double
*	Purpose	:	calculates the first partial derivative of Z wrt T
*	Revisions	:	
**************************************************************************/

double Detail::dZdT(double d)

{
//variables local to function double tmp ;

int i ;

double D1, D2, D3, D4, D5, D6, D7, D8, D9, exp1, exp2, exp3, exp4 ;

//set up powers of reduced density

D1 = dKp3 * d ;
D2 = D1 * D1 ;




D3 = D2 * D1 ;

D4 = D3 * D1 ;
D5 = D4 * D1 ;
D6 = D5 * D1 ;
D7 = D6 * D1 ;

D8 = D7 * D1 ;

D9 = D8 * D1 ;

exp1 = exp(-D1) ; exp2 = exp(-D2) ; exp3 = exp(-D3) ; exp4 = exp(-D4) ;

// create terms uC*T^-(un+1) from coefficients we've already computed (An[n]) for (i=12; i < 58; i++)

{

if (adUn[i] && adFn[i])
{
fx[i] = (adFn[i] * adUn[i] * D1) / dT;
}

else

{
fx[i] = 0.0 ;
}
}

//initial part of equation ddZdT = d * ddBdT ;

//n=13 evaluates to zero except for hydrogen, for whom fn = 1 if (dF) ddZdT += fx[12] - (fx[12] * (1.0 - 3.0 * D3) * exp3) ;

tmp = (1.0 - 2.0 * D2) * exp2 ; ddZdT += (fx[13] - (fx[13] * tmp)) ; ddZdT += fx[14] - (fx[14] * tmp) ; ddZdT += fx[15] - (fx[15] * tmp) ;

tmp = (1.0 - 4.0 * D4) * exp4 ; ddZdT += fx[16] - (fx[16] * tmp) ; ddZdT += fx[17] - (fx[17] * tmp) ;




ddZdT = ddZdT - (fx[18] + fx[19]) * D1 * 2.0

-(fx[21] + fx[22]) * D1 * (2.0 - 2.0 * D2) * exp2
-(fx[23] + fx[24] + fx[25]) * D1 * (2.0 - 4.0 * D4) * exp4
-fx[26] * D1 * (2.0 - 4.0 * D4) * exp4
-fx[27] * D2 * 3.0

-(fx[28] + fx[29]) * D2 * (3.0 - D1) * exp1
-(fx[30] + fx[31]) * D2 * (3.0 - 2.0 * D2) * exp2
-(fx[32] + fx[33]) * D2 * (3.0 - 3.0 * D3) * exp3
-(fx[34] + fx[35] + fx[36]) * D2 * (3.0 - 4.0 * D4) * exp4
-fx[37] * D3 * 4.0

-(fx[39] + fx[40] + fx[41]) * D3 * (4.0 - 2.0 * D2) * exp2
-(fx[42] + fx[43]) * D3 * (4.0 - 4.0 * D4) * exp4
-fx[44] * D4 * 5.0
-(fx[45] + fx[46]) * D4 * (5.0 - 2.0 * D2) * exp2
-(fx[47] + fx[48]) * D4 * (5.0 - 4.0 * D4) * exp4

-fx[49] * D5 * 6.0
-fx[50] * D5 * (6.0 - 2.0 * D2) * exp2
-fx[51] * D6 * 7.0
-fx[52] * D6 * (7.0 - 2.0 * D2) * exp2
-fx[53] * D7 * (8.0 - D1) * exp1

-fx[54] * D7 * (8.0 - 2.0 * D2) * exp2
-fx[56] * D8 * (9.0 - 2.0 * D2) * exp2 ;

return ddZdT ;

}// Detail::dDdT()



/**************************************************************************
*	Function	:	Detail::d2ZdT2()
*	Arguments	:	double
*	Returns	:	double
*	Purpose	:	calculates the second partial derivative of Z wrt T
*	Revisions	:	
**************************************************************************/

double Detail::d2ZdT2(double d)

{
//variables local to function double tmp ;
int i ;
double D1, D2, D3, D4, D5, D6, D7, D8, D9, exp1, exp2, exp3, exp4 ;

//set up powers of reduced density

D1 = dKp3 * d ;
D2 = D1 * D1 ;
D3 = D2 * D1 ;
D4 = D3 * D1 ;

D5 = D4 * D1 ;

D6 = D5 * D1 ;
D7 = D6 * D1 ;
D8 = D7 * D1 ;

D9 = D8 * D1 ; exp1 = exp(-D1) ; exp2 = exp(-D2) ; exp3 = exp(-D3) ; exp4 = exp(-D4) ;

// create terms uC*T^-(un+1) from coefficients we've already computed (An[n]) for (i=12; i < 58; i++)

{
if (adUn[i] && adFn[i])

{

fx[i] = (adFn[i] * D1 * adUn[i] * (adUn[i] + 1.0)) / (dT * dT) ;
}
else
{

fx[i] = 0.0 ;




}

}

//initial part of equation dd2ZdT2 = d * dd2BdT2 ;

//n=13 evaluates to zero except for hydrogen, for whom fn = 1 if (dF) dd2ZdT2 += fx[12] - (fx[12] * (1.0 - 3.0 * D3) * exp3) ;

tmp = (1.0 - 2.0 * D2) * exp2 ; dd2ZdT2 += -fx[13] + (fx[13] * tmp) ; dd2ZdT2 += -fx[14] + (fx[14] * tmp) ; dd2ZdT2 += -fx[15] + (fx[15] * tmp) ;

tmp = (1.0 - 4.0 * D4) * exp4 ; dd2ZdT2 += -fx[16] + (fx[16] * tmp) ; dd2ZdT2 += -fx[17] + (fx[17] * tmp) ;

dd2ZdT2 = dd2ZdT2 + (fx[18] + fx[19]) * D1 * 2.0

+(fx[21] + fx[22]) * D1 * (2.0 - 2.0 * D2) * exp2
+(fx[23] + fx[24] + fx[25]) * D1 * (2.0 - 4.0 * D4) * exp4

+fx[26] * D1 * (2.0 - 4.0 * D4) * exp4
+fx[27] * D2 * 3.0
+(fx[28] + fx[29]) * D2 * (3.0 - D1) * exp1
+(fx[30] + fx[31]) * D2 * (3.0 - 2.0 * D2) * exp2
+(fx[32] + fx[33]) * D2 * (3.0 - 3.0 * D3) * exp3

+(fx[34] + fx[35] + fx[36]) * D2 * (3.0 - 4.0 * D4) * exp4
+fx[37] * D3 * 4.0
+(fx[39] + fx[40] + fx[41]) * D3 * (4.0 - 2.0 * D2) * exp2
+(fx[42] + fx[43]) * D3 * (4.0 - 4.0 * D4) * exp4

+fx[44] * D4 * 5.0
+(fx[45] + fx[46]) * D4 * (5.0 - 2.0 * D2) * exp2
+(fx[47] + fx[48]) * D4 * (5.0 - 4.0 * D4) * exp4
+fx[49] * D5 * 6.0
+fx[50] * D5 * (6.0 - 2.0 * D2) * exp2

+fx[51] * D6 * 7.0
+fx[52] * D6 * (7.0 - 2.0 * D2) * exp2
+fx[53] * D7 * (8.0 - D1) * exp1
+fx[54] * D7 * (8.0 - 2.0 * D2) * exp2
+fx[56] * D8 * (9.0 - 2.0 * D2) * exp2 ;




return dd2ZdT2 ;

}// Detail::d2ZdT2()


/**************************************************************************
*	Function	:	Detail::dZdD()
*	Arguments	:	double
*	Returns	:	double
*	Purpose	:	calculates the first partial derivative of Z wrt D
*	Revisions	:	
**************************************************************************/

//For efficiency and continuity with AGA 8 code example, each term

//is evaluated individually rather than through looping through tables.

//Temporary storage is used to hold portions of complex equations and
//to facilitate debugging. Additional speed optimization is possible.

double Detail::dZdD(double d)

{

double temp, temp1, temp2, temp3; int i ;

double D1, D2, D3, D4, D5, D6, D7, D8, D9, exp1, exp2, exp3, exp4 ;

// set up powers of reduced density D1 = dKp3 * d ;

D2 = D1 * D1 ;
D3 = D2 * D1 ;

D4 = D3 * D1 ;

D5 = D4 * D1 ;
D6 = D5 * D1 ;
D7 = D6 * D1 ;
D8 = D7 * D1 ;

D9 = D8 * D1 ; exp1 = exp(-D1) ; exp2 = exp(-D2) ; exp3 = exp(-D3) ; exp4 = exp(-D4) ;



//create terms uC*T^-(un+1) from coefficients we've already computed (An[n]) for (i=12; i < 58; i++)

{
fx[i] = adFn[i] ;
}

//initial part of equation

ddZdD = dB / dKp3 ;

//evaluate all remaining terms, simplifying where possible

//n=13 evaluates to zero except for hydrogen, for whom fn = 1 if (dF)
{
temp1 = -9.0 * D3 * exp3 ;
temp2 = (1.0 - 3.0 * D3) * exp3 ; temp3 = -temp2 * 3.0 * D6;
temp = temp1 + temp2 + temp3 ; ddZdD += -fx[12] + fx[12] * temp ;

}
//n =  14..16

temp1 = -4.0 * D2 * exp2 ;

temp2 = (1.0 - 2.0 * D2) * exp2 ; temp3 = -temp2 * 2.0 * D2;

temp = temp1 + temp2 + temp3 ; ddZdD += -fx[13] + fx[13] * temp ; ddZdD += -fx[14] + fx[14] * temp ; ddZdD += -fx[15] + fx[15] * temp ;

// n =	17..18		
temp1 =	-16.0 * D4	* exp4 ;
temp2 =	(1.0 - 4.0	*	D4) * exp4 ;
temp3 =	-temp2 * 4.0	* D4 ;
temp = temp1 + temp2 + temp3 ; ddZdD += -fx[16] + fx[16] * temp ; ddZdD += -fx[17] + fx[17] * temp ;

// n = 19..20 temp = 4.0 * D1 ;

ddZdD += fx[18] * temp ; ddZdD += fx[19] * temp ;




// n =	21..23			
temp1 =	-4.0	*	D3	* exp2 ;
temp2 =	(2.0	-	2.0 * D2)	* 2.0 * D1 * exp2 ;
temp3 =	-temp2	* D2;	
temp = temp1 +	temp2 + temp3 ;
ddZdD += fx[20]	* temp ;	
ddZdD += fx[21]	* temp ;	
ddZdD += fx[22] * temp ;	
// n =	24..27			
temp1 =	-16.0 *	D5 * exp4 ;
temp2 =	(2.0	-	4.0 * D4)	* 2.0 * D1 * exp4 ;
temp3 =	-temp2	* 2.0 * D4 ;
temp = temp1 +	temp2 + temp3 ;
ddZdD += fx[23]	* temp ;	
ddZdD += fx[24]	* temp ;	
ddZdD += fx[25]	* temp ;	
ddZdD += fx[26] * temp ;	
// n =	28				
temp = 9.0 *	D2	;		
ddZdD += fx[27] * temp ;	
// n =	29..30			
temp = -D3 * exp1 + (3.0	- D1) * 3.0 * D2 * exp1 ;
temp -=	(3.0	-	D1) * D3 *	exp1 ;
ddZdD += fx[28] * temp ;	
ddZdD += fx[29] * temp ;	
// n =	31..32			
temp1 =	-4.0	*	D4 * exp2 ;
temp2 =	(3.0	-	2.0 * D2)	* 3.0 * D2 * exp2 ;
temp3 =	-(3.0 - 2.0 * D2) * 2.0 * D4 * exp2 ;
temp = temp1 + temp2 + temp3 ;
ddZdD += fx[30] * temp ;	
ddZdD += fx[31] * temp ;	
// n =	33..34			
temp1 =	-9.0	*	D5 * exp3 ;




temp2 = (3.0 - 3.0 * D3) * 3.0 * D2 * exp3 ; temp3 = -(3.0 - 3.0 * D3) * 3.0 * D5 * exp3 ; temp = temp1 + temp2 + temp3 ;

ddZdD += fx[32] * temp ; ddZdD += fx[33] * temp ;

// n =	35..37

temp1 = -16.0 * D6 * exp4 ;
temp2 = (3.0 - 4.0 * D4) * 3.0 * D2 * exp4 ; temp3 = -(3.0 - 4.0 * D4) * D6 * 4.0 * exp4 ; temp = temp1 + temp2 + temp3 ;

ddZdD += fx[34] * temp ; ddZdD += fx[35] * temp ; ddZdD += fx[36] * temp ;

//n = 38..39 temp = 16.0 * D3 ;
ddZdD += fx[37] * temp ; ddZdD += fx[38] * temp ;

//n =  40..42

temp1 = -4.0 * D5 * exp2 ;
temp2 = (4.0 - 2.0 * D2) * 4.0 * D3 * exp2 ; temp3 = -(4.0 - 2.0 * D2) * 2.0 * D5 * exp2 ; temp = temp1 + temp2 + temp3 ;

ddZdD += fx[39] * temp ; ddZdD += fx[40] * temp ; ddZdD += fx[41] * temp ;

// n =	43..44

temp = -16.0 * D7 * exp4 + (4.0 - 4.0 * D4) * 4.0 * D3 * exp4 ; temp -= (4.0 - 4.0 * D4) * D7 * 4.0 * exp4 ;

ddZdD += fx[42] * temp ; ddZdD += fx[43] * temp ;

// n =	45

temp = 25.0 * D4 ; ddZdD += fx[44] * temp ;

// n =	46..47




temp = -4.0 * D6 * exp2 + (5.0 - 2.0 * D2) * 5.0 * D4 * exp2 ; temp -= (5.0 - 2.0 * D2) * D6 * 2.0 * exp2 ;

ddZdD += fx[45] * temp ; ddZdD += fx[46] * temp ;

// n =	48..49

temp = -16.0 * D8 * exp4 + (5.0 - 4.0 * D4) * 5.0 * D4 * exp4 ; temp -= (5.0 - 4.0 * D4) * D8 * 4.0 * exp4 ;

ddZdD += fx[47] * temp ; ddZdD += fx[48] * temp ;

// n =	50

temp = 36.0 * D5 ; ddZdD += fx[49] * temp ;

// n =	51

temp = -4.0 * D7 * exp2 + (6.0 - 2.0 * D2) * 6.0 * D5 * exp2 ; temp -= (6.0 - 2.0 * D2) * D7 * 2.0 * exp2 ;

ddZdD += fx[50] * temp ;

// n =	52

temp = 49.0 * D6 ; ddZdD += fx[51] * temp ;

// n =	53

temp = -4.0 * D8 * exp2 + (7.0 - 2.0 * D2) * 7.0 * D6 * exp2 ; temp -= (7.0 - 2.0 * D2) * D8 * 2.0 * exp2 ;
ddZdD += fx[52] * temp ;

// n =	54

temp = -1.0 * D8 * exp1 + (8.0 - D1) * 8.0 * D7 * exp1 ; temp -= (8.0 - D1) * D8 * exp1 ;

ddZdD += fx[53] * temp ;

// n =	55..56

temp = -4.0 * D1 * D8 * exp2 + (8.0 - 2.0 * D2) * 8.0 * D7 * exp2 ; temp -= (8.0 - 2.0 * D2) * D8 * 2.0 * D1 * exp2 ;

ddZdD += fx[54] * temp ; ddZdD += fx[55] * temp ;





// n =	57..58

temp = -4.0 * D2 * D8 * exp2 + (9.0 - 2.0 * D2) * 9.0 * D8 * exp2 ; temp -= (9.0 - 2.0 * D2) * D2 * D8 * 2.0 * exp2 ;

ddZdD += fx[56] * temp ; ddZdD += fx[57] * temp ;

ddZdD *= dKp3 ;

return ddZdD ;

}// Detail::dZdD()




/**************************************************************************
*	Function	:	Detail::relativedensity()
*	Arguments	:	AGA10STRUCT *
*	Returns	:	void
*	Purpose	:	calculates relative density via methods listed in AGA 8
*	Revisions	:	
**************************************************************************/

void Detail::relativedensity(AGA10STRUCT *ptAGA10)

{
double dBX, dZa ;

const double dMWair = 28.96256 ;

// calculate second virial coefficient for air

dBX = -0.12527 + 5.91e-4 * ptAGA10->dTb - 6.62e-7 * ptAGA10->dTb * ptAGA10->dTb ;

// calculate compressibility of air

dZa = 1.0 + (dBX * dP) / (RGASKJ * ptAGA10->dTb) ;

// calculate ideal gas and real gas relative densities ptAGA10->dRD_Ideal = ptAGA10->dMrx / dMWair ; ptAGA10->dRD_Real = ptAGA10->dRD_Ideal * (dZa / ptAGA10->dZb) ;
}// Detail::relativedensity()