Skip to content

Commit

Permalink
Create new, working PhysicalCalculationService
Browse files Browse the repository at this point in the history
Verify method to convert between density & ppm/ppb is successful
Add initial specs for density conversions
Include significant digit helper methods to correctly output new concentrations
  • Loading branch information
jackkoppa committed Mar 18, 2018
1 parent c314bc6 commit e110ec6
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 44 deletions.
25 changes: 24 additions & 1 deletion src/app/core/calculation/calculation.helper.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe('CalculationHelper', () => {
[0.0034, 2],
[120.5e12, 4],
[1120.5e+12, 5],
[120.52e-5, 5],
[120.52e-5, 5],
[Math.PI, 16]
]
testCases.forEach(testCase => {
Expand Down Expand Up @@ -47,4 +47,27 @@ describe('CalculationHelper', () => {
});
})
});

describe('setSignificantDigits', () => {

const testCases: [number, number, number][] = [
//inputValue significantDigits outputValue
[0, 1, 0],
[2000, 1, 2000],
[1234.567, 3, 1230],
[123e5, 5, 123.00e5],
[12300.456, 6, 12300.5],
[12, 1, 10],
]
testCases.forEach(testCase => {
const [inputValue, significantDigits, outputValue] = testCase;

describe(`with an input value of ${inputValue}, setting to ${significantDigits} significant digits`, () => {
const result = CalculationHelper.setSignificantDigits(inputValue, significantDigits);
it(`should output ${outputValue}`, () => {
expect(result).toBe(outputValue);
});
});
});
});
});
11 changes: 9 additions & 2 deletions src/app/core/calculation/calculation.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,17 @@ export class CalculationHelper {
if (Math.abs(value) > max) throw new Error(`${value} is greater than the max JavaScript float, ` +
`and significant digits cannot be accurately calculated`);
value = Math.abs(+String(value).replace(".", "")); //remove decimal and make positive
if (value == 0)
return 0;
if (value == 0) return 0;
while (value != 0 && value % 10 == 0) value /= 10; //kill the 0s at the end of n

return Math.floor(Math.log(value) / log10) + 1; //get number of digits
}

static setSignificantDigits(value: number, significantDigits: number) {
const log10 = Math.log(10);

if (value == 0) return 0;
var multiple = Math.pow(10, significantDigits - Math.floor(Math.log(value) / log10) - 1);
return Math.round(value * multiple) / multiple;
}
}
80 changes: 40 additions & 40 deletions src/app/core/calculation/physical/physical-calculation.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,62 +7,62 @@ import { MOLECULAR_WEIGHTS } from './molecular-weights.constant';
import { NTP } from './ntp.constant';
import { CalculationHelper } from '../calculation.helper';
import { MessageSeverity } from '../calculation-response.models';
import { PARAMETER_INDEX_MAP } from '../indices/parameters/parameter-index-map.constant';

@Injectable()
export class PhysicalCalculationService {
public attemptUnitConversion(args: CalculationArguments): CalculationArguments {
if (this.equivalentUnits(args))
return args;
const givenUnit = args.unit;
const indexUnit = args.index.unit;
const concentration = args.concentration;
const parameter = args.parameter;
let newConcentration: number;
// TODO: refactor ugly switch statement to use... object?
switch (givenUnit) {
case 'ppm':
if (indexUnit === 'ppb')
newConcentration = 1000 * concentration;
if (indexUnit === 'µg/m³')
newConcentration = this.convertToDensity(1000 * concentration, parameter);
break;
case 'ppb':
if (indexUnit === 'ppm')
newConcentration = concentration / 1000;
if (indexUnit === 'µg/m³')
newConcentration = this.convertToDensity(concentration, parameter);
break;
case 'µg/m³':
if (indexUnit === 'ppm')
newConcentration = this.convertToVolumeRatio(concentration, parameter) * 1000;
if (indexUnit === 'ppb')
newConcentration = this.convertToVolumeRatio(concentration, parameter);
break;
default:
throw new Error(`Invalid unit given for measurement: ${givenUnit}`);
try {
newConcentration = this.getNewConcentration(args);
}
if (newConcentration == null) {
throw new Error(`Unable to convert ${givenUnit} to index unit of ${indexUnit}`);
} else {
args.concentration = CalculationHelper.truncateAtDecimal(newConcentration, args.index.decimalPlaces);
args.unit = indexUnit;
catch (err) {
throw new Error(`Failed to convert given unit of ${args.unit} to ${args.index.unit}`);
}
args.concentration = newConcentration;
args.unit = args.index.unit;
return args;
}

private equivalentUnits(args: CalculationArguments): boolean {
return args.unit.toLowerCase() === args.index.unit.toLowerCase();
}

private convertToDensity(ppb: number, parameter: Parameter): number {
const molecularWeight: number = MOLECULAR_WEIGHTS[parameter];
return molecularWeight &&
(ppb * molecularWeight * NTP.pressureInAtm) / (NTP.gasConstantForKAndAtm * NTP.temperatureInK);
private getNewConcentration(args: CalculationArguments): number {
const conversionMap: ConversionMap = {
'ppm': {
'ppb': 1000 * args.concentration,
'µg/m³': this.getMicrogramsByPartsPerMillion(args.concentration, args.parameter)
},
'ppb': {
'ppm': 0.001 * args.concentration,
'µg/m³': this.getMicrogramsByPartsPerMillion(args.concentration / 1000, args.parameter)
},
'µg/m³': {
'ppm': this.getPartsPerMillionByMicrograms(args.concentration, args.parameter),
'ppb': this.getPartsPerMillionByMicrograms(args.concentration, args.parameter) * 1000
}
}
const newConcentration = conversionMap[args.unit][args.index.unit];
const significantDigits = CalculationHelper.getSignificantDigitCount(args.concentration);
return CalculationHelper.setSignificantDigits(newConcentration, significantDigits);
}

private convertToVolumeRatio(density: number, parameter: Parameter): number {
const molecularWeight: number = MOLECULAR_WEIGHTS[parameter];
return molecularWeight &&
(molecularWeight * NTP.pressureInAtm) / (density * 1000 * NTP.gasConstantForKAndAtm * NTP.temperatureInK)
private getMicrogramsByPartsPerMillion(partsPerMillion: number, parameter: Parameter): number {
const moles = (NTP.pressureInAtm * partsPerMillion / 1000000) / (NTP.gasConstantForKAndAtm * NTP.temperatureInK);
return moles * MOLECULAR_WEIGHTS[parameter] * 1000000;
}

private getPartsPerMillionByMicrograms(micrograms: number, parameter: Parameter): number {
const moles = (micrograms * 1000000) / MOLECULAR_WEIGHTS[parameter];
return (moles * NTP.gasConstantForKAndAtm * NTP.temperatureInK) / (NTP.pressureInAtm * 1000000);
}
}

type ConversionMap = {
[M in MeasurementUnit]: {
[U in MeasurementUnit]?: number;
};
}
1 change: 0 additions & 1 deletion src/sass/modules/onboarding/_onboarding.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ $max-parallax-ratio: 1.25;
&.skyline-foreground {
background-image: url('../assets/backgrounds/skyline-foreground.svg');
height: $skyline-foreground-height;

z-index: map-get($z-index-levels, 'foreground-1');
}
&.skyline-midground {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ originalOrder,parameter,testType,concentration,unit,expectedConcentration,expect
1,so2,converting from ppm to ppb,0,ppm,0,ppb
2,so2,converting from ppm to ppb,10,ppm,10000,ppb
3,so2,converting from ppm to ppb,0.5,ppm,500,ppb
4,so2,converting from µg/m³ to ppb,201.5,µg/m³,75.65,ppb
5,so2,converting from µg/m³ to ppb,401.5,µg/m³,150.7,ppb
6,so2,converting from µg/m³ to ppb,801.5,µg/m³,300.9,ppb

0 comments on commit e110ec6

Please sign in to comment.