From another thread...
The stuff above is actually the basis of all the dissolved gas models going back to the early 20th C. Also the models in the Zoop and the like.
What makes Buhlmann ZHL16C is a specific set of value s of a and b which encode the sixteen tissue compartments. Those values control the limits and changing them I see reflected in how much no stop time (NDL) is available or how long the stops will be. There is also a minor detail of how much water vapour is assumed by different algorithms.
The first surprising, to me at least, thing about Buhlmann ZHL16 is that the a/b values are generated from the half time. Like this:
// Given a half time calculate the A and B coefficients.
// This is how Buhlmann ZHL-16A derives the M values - represented as a and b
// coefficients. Note that the B and C versions use tweaked coefficients.
static double calcA(double ht)
{
double result = 2 * std:ow(ht, -1.0 / 3);
return result;
}
static double calcB(double ht)
{
double result = 1.005 - std:ow(ht, -1.0 / 2);
return result;
}
The two later versions, B and C, are slightly tweaked.
Maybe this makes sense if you start thinking about the slower compartments needing lower limits in a constant time * risk sort of a way. Need to check with a proper deco scientist about that.
On top of these numbers there is a scheme invented by Eric Baker to make profiles more conservative. At the time deep stops were in fashion and it was felt that making this adjustment greater at depth would be better. This is where the GF thing comes in. Two numbers which are used to derive a percentage applied in that limit calculation. Sort of like this
// Actual Buhlmann
Pressure getCeilingPressure()
{
//Pambtol = (Pcomp - a) x b
// normal 100/100 version
Pressure minAmbientPressure = (compartmentPressureN2 - a) * b;
return minAmbientPressure;
}
// GF version, for the given GF at a particular depth
Pressure getCeilingPressureGF(Pressure compartmentTension, double gf)
{
// with gf
// PAMBT(I) = (PHEN2(I) - AHEN2(I)*FACTOR)/(FACTOR/BHEN2(I) - FACTOR + 1.0)
double tension = compartmentTension.value();
double minAmbient = (tension - a * gf) / ((gf / b) + 1.0 - gf);
// printf("which %3d - tension %.4f min ambient %.4f gf %.2f\n", (int)halfTime.value(), tension, minAmbient, gf);
Pressure minAmbientPressure(minAmbient);
return minAmbientPressure;
}
Next there is how to figure out the GF value for the given depth. That is not at all clear. In the original Baker work that was the first stop depth. Dive computers can’t really do that as a slow ascent will probably mean that stop is gone before you get there. I’ll not post code for that as it all a bit tedious.
Another complication is the question of whether a ceiling calculation (ie when the maximum tolerated over pressure, minAmbientPressure above, is more than surface pressure) should assume a vaguely normal ascent or not.
Another is whether ceilings count at all, or whether only stops count. And if a stop is of zero length is it still a no stop dive? Should you apply the deco ascent rate between zero length stops or the usual one?
For reference.... below are the ZHL-16 algorithm equations for calculating ambient pressure tolerance (P amb. tol.) for each compartment.
P amb. = ambient pressure in bar,
PI i.g. = inspiratory pressure of the inert gas,
F i.g. = proportion of the inert gas in the breathing gas,
0.063 bar = pressure of water vapor in the inspired breathing gas at a body temperature of 37 ° C.
PI i.g. = (P amb. - 0.063)*F i.g.
t0 = beginning of the exposure at time 0,
tE = duration of exposure in min,
½ t = half-value time in min,
Pi.g.t. = pressure of the inert gas in the tissue with a given half-value time.
Pi.g.t.(tE) = Pi.g.t(t0) + [ PI i.g. - Pi.g.t(t0) ] * [1-e^-k*tE]
or
Pi.g.t.(tE) = Pi.g.t(t0) + [ PI i.g. - Pi.g.t(t0) ] * [1-e^-0.69315*tE/½t]
and P amb. tol can be calculated from any Pi.g.t. using the a and b coefficients from a given compartment using:
P amb. tol. = (P i.g.t. - a)*b
For those interested in the math, see above, as it gives you a good idea of what is going on. I know it may look confusing to many, but I have tried to organize it in a way that flows and maybe helps explain what the algorithm is actually doing.
The stuff above is actually the basis of all the dissolved gas models going back to the early 20th C. Also the models in the Zoop and the like.
What makes Buhlmann ZHL16C is a specific set of value s of a and b which encode the sixteen tissue compartments. Those values control the limits and changing them I see reflected in how much no stop time (NDL) is available or how long the stops will be. There is also a minor detail of how much water vapour is assumed by different algorithms.
The first surprising, to me at least, thing about Buhlmann ZHL16 is that the a/b values are generated from the half time. Like this:
// Given a half time calculate the A and B coefficients.
// This is how Buhlmann ZHL-16A derives the M values - represented as a and b
// coefficients. Note that the B and C versions use tweaked coefficients.
static double calcA(double ht)
{
double result = 2 * std:ow(ht, -1.0 / 3);
return result;
}
static double calcB(double ht)
{
double result = 1.005 - std:ow(ht, -1.0 / 2);
return result;
}
The two later versions, B and C, are slightly tweaked.
Maybe this makes sense if you start thinking about the slower compartments needing lower limits in a constant time * risk sort of a way. Need to check with a proper deco scientist about that.
On top of these numbers there is a scheme invented by Eric Baker to make profiles more conservative. At the time deep stops were in fashion and it was felt that making this adjustment greater at depth would be better. This is where the GF thing comes in. Two numbers which are used to derive a percentage applied in that limit calculation. Sort of like this
// Actual Buhlmann
Pressure getCeilingPressure()
{
//Pambtol = (Pcomp - a) x b
// normal 100/100 version
Pressure minAmbientPressure = (compartmentPressureN2 - a) * b;
return minAmbientPressure;
}
// GF version, for the given GF at a particular depth
Pressure getCeilingPressureGF(Pressure compartmentTension, double gf)
{
// with gf
// PAMBT(I) = (PHEN2(I) - AHEN2(I)*FACTOR)/(FACTOR/BHEN2(I) - FACTOR + 1.0)
double tension = compartmentTension.value();
double minAmbient = (tension - a * gf) / ((gf / b) + 1.0 - gf);
// printf("which %3d - tension %.4f min ambient %.4f gf %.2f\n", (int)halfTime.value(), tension, minAmbient, gf);
Pressure minAmbientPressure(minAmbient);
return minAmbientPressure;
}
Next there is how to figure out the GF value for the given depth. That is not at all clear. In the original Baker work that was the first stop depth. Dive computers can’t really do that as a slow ascent will probably mean that stop is gone before you get there. I’ll not post code for that as it all a bit tedious.
Another complication is the question of whether a ceiling calculation (ie when the maximum tolerated over pressure, minAmbientPressure above, is more than surface pressure) should assume a vaguely normal ascent or not.
Another is whether ceilings count at all, or whether only stops count. And if a stop is of zero length is it still a no stop dive? Should you apply the deco ascent rate between zero length stops or the usual one?