All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.qudtlib.tools.contribute.support.ContributionHelper Maven / Gradle / Ivy

There is a newer version: 6.8.0
Show newest version
package io.github.qudtlib.tools.contribute.support;

import io.github.qudtlib.Qudt;
import io.github.qudtlib.model.*;
import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

public class ContributionHelper {
    public static Unit.Definition derivedUnitDefinition(
            FactorUnits factorUnits, String nonstandardLocalname) {
        String localname = null;
        Unit.Definition def = null;
        Unit existingUnit = null;
        if (nonstandardLocalname != null) {
            localname = nonstandardLocalname;
            existingUnit = Qudt.unitFromLocalname(localname).orElse(null);
        } else {
            localname = factorUnits.getLocalname();
            existingUnit = findUnitByLocalName(factorUnits);
        }
        if (existingUnit == null) {
            def = Unit.definition(QudtNamespaces.unit.makeIriInNamespace(localname));
            FactorUnits base = getBaseFactorUnits(factorUnits).withoutScaleFactor();
            if (base.equals(factorUnits)) {
                def.conversionMultiplier(findMultiplier(factorUnits));
            } else {
                List units =
                        Qudt.unitsFromFactorUnits(DerivedUnitSearchMode.ALL, base.getFactorUnits());
                units = filterPossibleBasesByQkIfOnlyOneFactor(factorUnits, units);
                if (units.isEmpty()) {
                    throw new RuntimeException(
                            String.format(
                                    "Cannot create unit %s: the factor units %s define a scaled unit, but there is no QUDT unit for the base factor units %s. Add a unit for these factors first.",
                                    localname,
                                    factorUnits.toString(),
                                    base.getFactorUnits().toString()));
                }
                Unit scalingOf = units.stream().findFirst().get();
                def.conversionMultiplier(
                        factorUnits
                                .conversionFactor(scalingOf)
                                .multiply(scalingOf.getConversionMultiplier().get()));
                def.scalingOf(scalingOf);
            }
            def.setFactorUnits(factorUnits).dimensionVectorIri(factorUnits.getDimensionVectorIri());
        } else {
            def = Unit.definition(existingUnit.getIri());
            final Unit.Definition finalDef = def;
            existingUnit
                    .getFactorUnits()
                    .getFactorUnits()
                    .forEach(fu -> finalDef.addFactorUnit(fu));
            FactorUnits base = getBaseFactorUnits(factorUnits).withoutScaleFactor();
            if (existingUnit.getScalingOf().isPresent()) {
                finalDef.scalingOf(existingUnit.getScalingOf().get());
                finalDef.conversionMultiplier(
                        factorUnits
                                .conversionFactor(existingUnit.getScalingOf().get())
                                .multiply(
                                        existingUnit
                                                .getScalingOf()
                                                .get()
                                                .getConversionMultiplier()
                                                .get()));
                existingUnit.getConversionOffset().ifPresent(co -> finalDef.conversionOffset(co));
            } else {
                List baseUnits =
                        Qudt.unitsFromFactorUnits(DerivedUnitSearchMode.ALL, base.getFactorUnits());
                baseUnits = filterPossibleBasesByQkIfOnlyOneFactor(factorUnits, baseUnits);
                Optional newBase = baseUnits.stream().findFirst();
                if (newBase.isEmpty() || newBase.get().equals(existingUnit)) {
                    finalDef.conversionMultiplier(findMultiplier(factorUnits));
                } else {
                    finalDef.scalingOf(newBase.get());
                    finalDef.conversionMultiplier(
                            factorUnits
                                    .conversionFactor(newBase.get())
                                    .multiply(newBase.get().getConversionMultiplier().get()));
                }
            }
            existingUnit.getUcumCode().ifPresent(ucum -> finalDef.ucumCode(ucum));

            def.setFactorUnits(factorUnits).dimensionVectorIri(factorUnits.getDimensionVectorIri());
        }
        def.symbol(factorUnits.getSymbol().orElse(null))
                .ucumCode(factorUnits.getUcumCode().orElse(null))
                .addLabels(LabelCombiner.forFactorUnits(factorUnits));
        return def;
    }

    private static List filterPossibleBasesByQkIfOnlyOneFactor(
            FactorUnits factorUnits, List baseUnits) {
        if (factorUnits.getFactorUnits().size() == 1) {
            // make sure the base we found shares the quantity kinds of the base we provided
            baseUnits =
                    baseUnits.stream()
                            .filter(
                                    bu ->
                                            factorUnits
                                                    .getFactorUnits()
                                                    .get(0)
                                                    .getUnit()
                                                    .getQuantityKinds()
                                                    .stream()
                                                    .allMatch(
                                                            qk ->
                                                                    bu.getQuantityKinds()
                                                                            .contains(qk)))
                            .collect(Collectors.toList());
        }
        return baseUnits;
    }

    private static BigDecimal findMultiplier(FactorUnits factorUnits) {
        String dimensionVectorIri = factorUnits.getDimensionVectorIri();
        Set possibleBases =
                Qudt.allUnits().stream()
                        .filter(
                                u ->
                                        u.getDimensionVectorIri()
                                                .map(dv -> dv.equals(dimensionVectorIri))
                                                .orElse(false))
                        .filter(
                                u ->
                                        factorUnits
                                                .streamLocalnamePossibilities()
                                                .noneMatch(u.getIriLocalname()::equals))
                        .filter(
                                u ->
                                        u.getConversionMultiplier()
                                                .map(cm -> cm.compareTo(BigDecimal.ONE) == 0)
                                                .orElse(false))
                        .collect(Collectors.toSet());
        if (possibleBases.isEmpty()) {
            return BigDecimal.ONE;
        }
        for (Unit base : possibleBases) {
            try {
                return factorUnits.conversionFactor(base);
            } catch (Exception e) {
            }
        }
        throw new RuntimeException(
                String.format(
                        "Unable to calculate conversion factor from these factor units %s to any of these bases %s",
                        factorUnits.toString(),
                        possibleBases.stream()
                                .map(Unit::getIriAbbreviated)
                                .collect(Collectors.joining(", "))));
    }

    private static Unit findUnitByLocalName(FactorUnits factorUnits) {
        return factorUnits
                .streamLocalnamePossibilities()
                .map(
                        localNameCandidate ->
                                Qudt.unitFromLocalname(localNameCandidate)
                                        .orElseGet(
                                                () ->
                                                        Qudt.currencyFromLocalname(
                                                                        localNameCandidate)
                                                                .orElse(null)))
                .filter(Objects::nonNull)
                .findFirst()
                .orElse(null);
    }

    /** Retuns the FactorUnits object resulting from unscaling each factor unit. */
    static FactorUnits getBaseFactorUnits(FactorUnits factorUnits) {
        return new FactorUnits(
                factorUnits.getFactorUnits().stream()
                        .map(
                                fu ->
                                        new FactorUnit(
                                                Qudt.unscale(fu.getUnit(), true, false),
                                                fu.getExponent()))
                        .collect(Collectors.toList()));
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy