subsurface/subsurface-core/gas-model.c
Linus Torvalds 055316e4a9 gas model: add polynomials for Z factors of oxygen/nitrogen/helium
.. and use a linear mix of them for arbitrary gas mixes.

For the special case of air, we continue to use the air-specific
polynomial.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-03-02 18:09:41 -08:00

164 lines
4.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* gas-model.c */
/* gas compressibility model */
#include <stdio.h>
#include <stdlib.h>
#include "dive.h"
/*
* This gives an interative solution of hte Redlich-Kwong equation for the compressibility factor
* according to https://en.wikipedia.org/wiki/RedlichKwong_equation_of_state
* in terms of the reduced temperature T/T_crit and pressure p/p_crit.
*
* Iterate this three times for good results in our pressur range.
*
*/
static double redlich_kwong_equation(double t_red, double p_red, double z_init)
{
return (1.0/(1.0 - 0.08664*p_red/(t_red * z_init)) -
0.42748/(sqrt(t_red * t_red * t_red) * ((t_red*z_init/p_red + 0.08664))));
}
/*
* At high pressures air becomes less compressible, and
* does not follow the ideal gas law any more.
*/
#define STANDARD_TEMPERATURE 293.0
static double redlich_kwong_compressibility_factor(struct gasmix *gas, double bar)
{
/* Critical points according to https://en.wikipedia.org/wiki/Critical_point_(thermodynamics) */
double tcn2 = 126.2;
double tco2 = 154.6;
double tche = 5.19;
double pcn2 = 33.9;
double pco2 = 50.5;
double pche = 2.27;
double tc, pc;
tc = (tco2 * get_o2(gas) + tche * get_he(gas) + tcn2 * (1000 - get_o2(gas) - get_he(gas))) / 1000.0;
pc = (pco2 * get_o2(gas) + pche * get_he(gas) + pcn2 * (1000 - get_o2(gas) - get_he(gas))) / 1000.0;
return (redlich_kwong_equation(STANDARD_TEMPERATURE/tc, bar/pc,
redlich_kwong_equation(STANDARD_TEMPERATURE/tc, bar/pc,
redlich_kwong_equation(STANDARD_TEMPERATURE/tc, bar/pc,1.0))));
}
/*
* Generic quintic polynomial
*/
static double quintic(double bar, const double coefficient[])
{
double x0 = 1.0,
x1 = bar,
x2 = x1*x1,
x3 = x2*x1,
x4 = x2*x2,
x5 = x2*x3;
return x0 * coefficient[0] +
x1 * coefficient[1] +
x2 * coefficient[2] +
x3 * coefficient[3] +
x4 * coefficient[4] +
x5 * coefficient[5];
}
/*
* These are the quintic coefficients by Lubomir I. Ivanov that have
* been optimized for the least-square error to the air
* compressibility factor table (at 300K) taken from Wikipedia:
*
* bar z_factor
* --- ------
* 1: 0.9999
* 5: 0.9987
* 10: 0.9974
* 20: 0.9950
* 40: 0.9917
* 60: 0.9901
* 80: 0.9903
* 100: 0.9930
* 150: 1.0074
* 200: 1.0326
* 250: 1.0669
* 300: 1.1089
* 400: 1.2073
* 500: 1.3163
*/
static const double air_coefficients[6] = {
+1.0002556612420115,
-0.0003115084635183305,
+0.00000227808965401253,
+1.91596422989e-9,
-8.78421542e-12,
+6.77746e-15
};
/*
* Quintic least-square coefficients for O2/N2/He based on tables at
*
* http://ww.baue.org/library/zfactor_table.php
*
* converted to bar and also done by Lubomir.
*/
static const double o2_coefficients[6] = {
+1.0002231211532653,
-0.0007471497056767194,
+0.00000200444854807816,
+2.91501995188e-9,
-4.48294663e-12,
-6.11529e-15
};
static const double n2_coefficients[6] = {
+1.0001898816185364,
-0.00030793319362077315,
+0.00000327557417347714,
-1.93872574476e-9,
-2.7732353e-12,
-2.8921e-16
};
static const double he_coefficients[6] = {
+0.9998700261301693,
+0.0005452130351730479,
-2.7853712233619e-7,
+5.5935404211e-10,
-1.35114572e-12,
+2.00476e-15
};
static double air_compressibility_factor(double bar) { return quintic(bar, air_coefficients); }
static double o2_compressibility_factor(double bar) { return quintic(bar, o2_coefficients); }
static double n2_compressibility_factor(double bar) { return quintic(bar, n2_coefficients); }
static double he_compressibility_factor(double bar) { return quintic(bar, he_coefficients); }
/*
* We end up using specialized functions for known gases, because
* we have special tables for them.
*
* For air we use our known-good table. For other mixes we use a
* linear interpolation of the Z factors of the individual gases.
*/
double gas_compressibility_factor(struct gasmix *gas, double bar)
{
double o2, n2, he; // Z factors
double of, nf, hf; // gas fractions ("partial pressures")
if (gasmix_is_air(gas))
return air_compressibility_factor(bar);
o2 = o2_compressibility_factor(bar);
n2 = n2_compressibility_factor(bar);
he = he_compressibility_factor(bar);
of = gas->o2.permille / 1000.0;
hf = gas->he.permille / 1000.0;
nf = 1.0 - of - nf;
return o2*of + n2*nf + he*hf;
}