Skip to content

Commit

Permalink
Implemented missing gas blender test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
jirkapok committed Mar 6, 2024
1 parent 24b6abb commit 5dffeab
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 70 deletions.
2 changes: 2 additions & 0 deletions projects/planner/src/app/shared/gas-blender.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ export class GasBlenderService {
he: topMetric.gas.fHe
}
};

// TODO add exception handling in case we are unable to create target mix.
this.results = GasBlender.mix(request);
}
}
120 changes: 65 additions & 55 deletions projects/scuba-physics/src/lib/gasBlender.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@ describe('Gas Blender', () => {
expect(result.addO2).withContext('add O2').toBeCloseTo(expectedO2, 6);
expect(result.addHe).withContext('add He').toBeCloseTo(expectedHe, 6);

const finalPpO2 = (request.source.o2 * request.source.pressure + request.topMix.o2 * result.addTop + result.addO2)
const sourcePressure = request.source.pressure - result.removeFromSource;
const finalPpO2 = (request.source.o2 * sourcePressure + request.topMix.o2 * result.addTop + result.addO2)
/ request.target.pressure;
expect(request.target.o2).withContext('final pp O2').toBeCloseTo(finalPpO2, 6);
const finalPpHe = (request.source.he * request.source.pressure + request.topMix.he * result.addTop + result.addHe)
const finalPpHe = (request.source.he * sourcePressure + request.topMix.he * result.addTop + result.addHe)
/ request.target.pressure;
expect(request.target.he).withContext('final pp He').toBeCloseTo(finalPpHe, 6);

const total = request.source.pressure + result.addTop + result.addO2 + result.addHe;
const total = sourcePressure + result.addTop + result.addO2 + result.addHe;
expect(request.target.pressure).withContext('Sum pressures').toBeCloseTo(total, 6);
expect(result.removeFromSource).toBeCloseTo(expecedRemove, 6);
};
Expand Down Expand Up @@ -140,9 +141,7 @@ describe('Gas Blender', () => {
request.topMix.o2 = 0.32;
request.target.o2 = 0.21;

const mixProcess = GasBlender.mix(request);
expect(mixProcess.addO2).toBeCloseTo(-32.352941, 6);
expect(mixProcess.addTop).toBeCloseTo(232.352941, 6);
expect(() => GasBlender.mix(request)).toThrow();
});
});

Expand Down Expand Up @@ -177,9 +176,7 @@ describe('Gas Blender', () => {
request.topMix.o2 = 0.32;
request.target.o2 = 0.21;

const mixProcess = GasBlender.mix(request);
expect(mixProcess.addO2).toBeCloseTo(-24.264706, 6);
expect(mixProcess.addTop).toBeCloseTo(174.264706, 6);
expect(() => GasBlender.mix(request)).toThrow();
});
});
});
Expand Down Expand Up @@ -241,7 +238,7 @@ describe('Gas Blender', () => {
request.topMix.o2 = 0.25;
request.topMix.he = 0.25;

assertResult(request, 148, -1, 53);
expect(() => GasBlender.mix(request)).toThrow();
});

it('Can`t create 21/35 using 17/45', () => {
Expand All @@ -251,7 +248,7 @@ describe('Gas Blender', () => {
request.topMix.o2 = 0.17;
request.topMix.he = 0.45;

assertResult(request, 231.578947, 2.631579, -34.210526);
expect(() => GasBlender.mix(request)).toThrow();
});
});

Expand Down Expand Up @@ -322,7 +319,7 @@ describe('Gas Blender', () => {
request.topMix.o2 = 0.18;
request.topMix.he = 0.45;

assertResult(request, 216.216216, -6.418919, -59.797297);
expect(() => GasBlender.mix(request)).toThrow();
});

it('Can`t create 21/35 from 25/25 using 17/45', () => {
Expand All @@ -334,7 +331,7 @@ describe('Gas Blender', () => {
request.topMix.o2 = 0.17;
request.topMix.he = 0.45;

assertResult(request, 165.789474, 1.315789, -17.105263);
expect(() => GasBlender.mix(request)).toThrow();
});
});
});
Expand Down Expand Up @@ -363,7 +360,7 @@ describe('Gas Blender', () => {
assertResult(request, 0, 5, 145);
});

it('Can`t create heliox from mix with nitrox in empty tank', () => {
it('Can`t create trimix from mix with nitrox in empty tank', () => {
const request = createNonEmptyRequest();
request.source.o2 = 0.10;
request.source.he = 0.70;
Expand All @@ -372,81 +369,94 @@ describe('Gas Blender', () => {
request.topMix.o2 = 0.25;
request.topMix.he = 0.25;

assertResult(request, -20, 20, 150);
assertResult(request, 0, 20, 180, 50);
});
});

// no need to test 0 bar remove, since it is part of all other tests
// test not only removed amount, but also the final result
// TODO Finish Gas blender test cases
// Add test case - remove from source to remove nitrogen
xdescribe('Remove from source tank', () => {
it('Trimix 25/25 to Ean32 needs to remove everything', () => {
// using non standard mixtures to only test possibilities
describe('Remove from source tank', () => {
it('Ean50 from Ean32 needs to remove nitrogen', () => {
const request = createNonEmptyRequest();
request.source.o2 = 0.25;
request.source.he = 0.25;
request.target.o2 = 0.32;
request.source.o2 = 0.32;
request.source.pressure = 200;
request.target.o2 = 0.5;
request.target.he = 0;
request.topMix.o2 = 0.21;
request.topMix.he = 0;

assertResult(request, 0, 20, 200, 50);
assertResult(request, 0, 52.941176, 0, 52.941176);
});

it('Oxygen to Ean32 needs to remove oxygen', () => {
it('Trimix 21/25 from 21/35 needs to remove helium', () => {
const request = createNonEmptyRequest();
request.source.o2 = 1;
request.target.o2 = 0.32;
request.target.he = 0;
request.topMix.o2 = 0.21;
request.source.he = 0.35;
request.source.pressure = 200;
request.target.he = 0.25;
request.topMix.he = 0;

assertResult(request, 0, 20, 180, 50);
assertResult(request, 57.142857, 0, 0, 57.142857);
});

// using non standard mixtures to only test possibilities
it('Trimix 25/35 to 15/30 needs to remove oxygen', () => {
it('Ean32 from Ean50 needs to remove oxygen', () => {
const request = createNonEmptyRequest();
request.source.o2 = 1;
request.source.o2 = 0.5;
request.source.pressure = 200;
request.target.o2 = 0.32;
request.target.he = 0;
request.topMix.o2 = 0.21;
request.topMix.he = 0;

assertResult(request, 0, 20, 180, 50);
assertResult(request, 124.137931, 0, 0, 124.137931);
});

it('Trimix 21/35 to 18/25 needs to remove helium', () => {
it('Trimix 15/30 from 25/35 needs to remove everything', () => {
const request = createNonEmptyRequest();
request.source.o2 = 1;
request.target.o2 = 0.32;
request.target.he = 0;
request.topMix.o2 = 0.21;
request.topMix.he = 0;
request.source.o2 = 0.25;
request.source.he = 0.35;
request.source.pressure = 100;
request.target.o2 = 0.15;
request.target.he = 0.30;

assertResult(request, 0, 20, 180, 50);
assertResult(request, 136.563877, 0, 58.14978, 94.713656);
});

// no need to care about too much nitrogen in top mix, since we compensate it by adding O2 and He

it('Unable to mix, because top mix contains more oxygen, than needed', () => {
const request = createNonEmptyRequest();
request.source.o2 = 1;
request.target.o2 = 0.32;
request.target.he = 0;
request.topMix.o2 = 0.21;
request.topMix.he = 0;
request.source.o2 = 0.5;
request.target.o2 = 0.21;
request.topMix.o2 = 0.32;

assertResult(request, 0, 20, 180, 50);
expect(() => GasBlender.mix(request)).toThrow();
});

it('Unable to mix, because top mix contains more helium, than needed', () => {
const request = createNonEmptyRequest();
request.source.o2 = 1;
request.target.o2 = 0.32;
request.target.he = 0;
request.topMix.o2 = 0.21;
request.topMix.he = 0;
request.source.he = 0.35;
request.target.he = 0.21;
request.topMix.he = 0.45;

assertResult(request, 0, 20, 180, 50);
expect(() => GasBlender.mix(request)).toThrow();
});

it('Unable to mix, because top mix contains more oxygen and helium, than needed', () => {
const request = createNonEmptyRequest();
request.source.he = 0.25;
request.target.o2 = 0.10;
request.target.he = 0.25;
request.topMix.o2 = 0.18;
request.topMix.he = 0.45;

expect(() => GasBlender.mix(request)).toThrow();
});

it('Unable to mix, because top mix contains more oxygen and helium, source tank is empty', () => {
const request = createNonEmptyRequest();
request.source.pressure = 0;
request.topMix.o2 = 0.18;
request.topMix.he = 0.45;

expect(() => GasBlender.mix(request)).toThrow();
});
});
});
79 changes: 64 additions & 15 deletions projects/scuba-physics/src/lib/gasBlender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,26 @@ export interface MixRequest {

/** Gas mix blending math */
export class GasBlender {
/**
* Calculates final pressure combining two tanks A and B with different volume and start pressure using ideal gas law.
*
* @returns final pressure in both tanks in bars
*/
public static redundancies(tankA: TankFill, tankB: TankFill): number {
GasBlender.validateTankFill(tankA, 'tankA');
GasBlender.validateTankFill(tankB, 'tankB');

if (tankA.size === 0 && tankB.size === 0) {
return 0;
}

const combinedSize = tankA.size + tankB.size;
const tankVolumeA = Tank.volume(tankA);
const tankVolumeB = Tank.volume(tankB);
const result = (tankVolumeA + tankVolumeB) / combinedSize;
return result;
}

/**
* Math describing to create required amount of mixture from current tank content using o2, he and topping mix.
* The formula expects ideal gas law.
Expand All @@ -55,14 +75,30 @@ export class GasBlender {
const currentfN2 = GasBlender.n2(request.source);
const currentN2 = currentfN2 * request.source.pressure;
const addN2 = finalN2 - currentN2;
// Even the top mix contains more nitrogen than target, we are still able to mix
// by adding less top mix and more He and O2
if(addN2 < 0) {
const removeSource = -(addN2 / currentfN2);
return GasBlender.mixByRemoving(request, removeSource);
}

const topfN2 = GasBlender.n2(request.topMix);
const addTop = addN2 / topfN2;
const targetHe = request.target.pressure * request.target.he;
const sourceHe = request.source.pressure * request.source.he;
const topHe = addTop * request.topMix.he;

const addHe = targetHe - sourceHe - topHe;

if(addHe < 0) {
const removeSource = -(addHe / request.source.he);
return GasBlender.mixByRemoving(request, removeSource);
}

const addO2 = request.target.pressure - request.source.pressure - addHe - addTop;
if(addO2 < 0) {
const removeSource = -(addO2 / request.source.o2);
return GasBlender.mixByRemoving(request, removeSource);
}

return {
addO2: addO2,
Expand All @@ -72,23 +108,17 @@ export class GasBlender {
};
}

/**
* Calculates final pressure combining two tanks A and B with different volume and start pressure using ideal gas law.
*
* @returns final pressure in both tanks in bars
*/
public static redundancies(tankA: TankFill, tankB: TankFill): number {
GasBlender.validateTankFill(tankA, 'tankA');
GasBlender.validateTankFill(tankB, 'tankB');
private static mixByRemoving(request: MixRequest, removeSource: number) {
const newRequest = GasBlender.copyRequest(request);

if (tankA.size === 0 && tankB.size === 0) {
return 0;
if(removeSource > request.source.pressure) {
throw new Error('Unable to mix required gas because target contains less he or oxygen than top mix.');
}

const combinedSize = tankA.size + tankB.size;
const tankVolumeA = Tank.volume(tankA);
const tankVolumeB = Tank.volume(tankB);
const result = (tankVolumeA + tankVolumeB) / combinedSize;
newRequest.source.pressure -= removeSource;
const result = GasBlender.mix(newRequest);
// aggregate the removed pressure from all the recursive calls
result.removeFromSource += removeSource;
return result;
}

Expand Down Expand Up @@ -120,4 +150,23 @@ export class GasBlender {
throw new Error(`${tankName} Start pressure needs to be positive number.`);
}
}

private static copyRequest(request: MixRequest): MixRequest {
return {
source: GasBlender.copyTankMix(request.source),
target: GasBlender.copyTankMix(request.target),
topMix: {
o2: request.topMix.o2,
he: request.topMix.he
}
};
}

private static copyTankMix(source: TankMix): TankMix {
return {
o2: source.o2,
he: source.he,
pressure: source.pressure
};
}
}

0 comments on commit 5dffeab

Please sign in to comment.