@qudtlib/core
Version:
Data model for QUDTLib
164 lines (163 loc) • 6.11 kB
JavaScript
import { arrayDeduplicate, arrayEqualsIgnoreOrdering, checkInteger, compareUsingEquals, isNullish, } from "./utils.js";
import { DimensionVector } from "./dimensionVector.js";
/**
* Combines a {@link Unit} and an exponent; some Units are a combination of {@link FactorUnit}s. If
* a unit is such a 'derived unit', its {@link Unit#getFactorUnits()} method returns a non-empty Set
* of FactorUnits.
*
*/
export class FactorUnit {
constructor(unit, exponent) {
checkInteger(exponent, "exponent");
this.exponent = exponent;
this.unit = unit;
}
/**
* Perform mathematical simplification on factor units. Only simplifies units with exponents of the same sign.
*
* For example,
* ```
* N / M * M -> N per M^2
* ```
*
* @param factorUnits the factor units to simplify
* @return the simplified factor units.
*/
static contractExponents(factorUnits) {
const ret = [];
const factorUnitsByKind = factorUnits.reduce((mapping, cur) => {
const kind = cur.getKind();
const prevUnit = mapping.get(kind);
if (prevUnit) {
mapping.set(kind, FactorUnit.combine(prevUnit, cur));
}
else {
mapping.set(kind, cur);
}
return mapping;
}, new Map());
for (const fu of factorUnitsByKind.values()) {
ret.push(fu);
}
return ret;
}
static reduceExponents(factorUnits) {
const ret = [];
const exponentsByUnit = factorUnits.reduce((mapping, cur) => {
const unit = cur.unit;
const prevExponent = mapping.get(unit);
if (prevExponent) {
mapping.set(unit, prevExponent + cur.exponent);
}
else {
mapping.set(unit, cur.exponent);
}
return mapping;
}, new Map());
for (const [unit, exponent] of exponentsByUnit.entries()) {
if (Math.abs(exponent) > 0) {
ret.push(new FactorUnit(unit, exponent));
}
}
return ret;
}
pow(by) {
checkInteger(by, "by");
return new FactorUnit(this.unit, this.exponent * by);
}
getExponentCumulated(cumulatedExponent) {
checkInteger(cumulatedExponent, "cumulatedExponent");
return this.exponent * cumulatedExponent;
}
isCompatibleWith(other) {
return (this.exponent === other.exponent && this.unit.isConvertible(other.unit));
}
getConversionMultiplier(other) {
if (!this.isCompatibleWith(other)) {
throw `${this.toString()} is not compatible with ${other.toString()}`;
}
return this.unit.getConversionMultiplier(other.unit).pow(this.exponent);
}
equals(other) {
return (!!other &&
this.exponent === other.exponent &&
this.unit.equals(other.unit));
}
toString() {
return (this.unit.toString() + (this.exponent === 1 ? "" : "^" + this.exponent));
}
static combine(left, right) {
if (!left) {
return right;
}
if (!right) {
return left;
}
if (!left.unit.equals(right.unit)) {
throw `Cannot combine UnitFactors of different units (left: ${left.unit.toString()}, right:${right.unit.toString()}`;
}
return new FactorUnit(left.unit, left.exponent + right.exponent);
}
/**
* Combines unit IRI and sign of exponent in one string.
*/
getKind() {
return (this.unit.iri +
(this.exponent === 0 ? "0" : this.exponent > 0 ? "1" : "-1"));
}
getLeafFactorUnitsWithCumulativeExponents() {
const leafFactorUnits = this.unit.getLeafFactorUnitsWithCumulativeExponents();
if (!!leafFactorUnits?.length) {
return leafFactorUnits.map((fu) => fu.pow(this.exponent));
}
return [this];
}
normalize() {
return this.unit.normalize().pow(this.exponent);
}
getAllPossibleFactorUnitCombinations() {
const subResult = this.unit.getAllPossibleFactorUnitCombinations();
const result = subResult.map((fus) => fus.map((fu) => fu.pow(this.exponent)));
return arrayDeduplicate(result, (left, right) => arrayEqualsIgnoreOrdering(left, right, compareUsingEquals));
}
static getAllPossibleFactorUnitCombinations(factorUnits) {
const numFactors = factorUnits.length;
const subResults = factorUnits.map((fu) => fu.getAllPossibleFactorUnitCombinations());
const subResultLengths = subResults.map((s) => s.length);
const currentIndices = [];
currentIndices.length = numFactors;
currentIndices.fill(0);
const results = [];
// cycle through all possible combinations of results per factor unit and combine them
do {
const curResult = [];
let countUp = true;
for (let i = 0; i < numFactors; i++) {
curResult.push(...subResults[i][currentIndices[i]]);
if (countUp) {
currentIndices[i]++;
if (currentIndices[i] >= subResultLengths[i]) {
currentIndices[i] = 0;
}
else {
countUp = false;
}
}
}
results.push(FactorUnit.contractExponents(curResult));
results.push(FactorUnit.reduceExponents(curResult));
} while (!currentIndices.every((val) => val === 0));
return arrayDeduplicate(results, (left, right) => arrayEqualsIgnoreOrdering(left, right, compareUsingEquals));
}
getDimensionVector() {
if (isNullish(this.unit.dimensionVectorIri)) {
return undefined;
}
const dv = new DimensionVector(this.unit.dimensionVectorIri);
return dv.multiply(this.exponent);
}
static ofUnit(unit) {
return new FactorUnit(unit, 1);
}
}
//# sourceMappingURL=factorUnit.js.map