From fec327e5ff3676542e1c5d29289e38972b577520 Mon Sep 17 00:00:00 2001 From: Patrick Austin Date: Sat, 30 Apr 2022 01:58:52 +0100 Subject: [PATCH 1/8] Add scripts and basic tests for unit conversion #19 --- .../java/org/icatproject/utils/IcatUnits.java | 77 +++++++++++++++++++ .../org/icatproject/utils/TestIcatUnits.java | 45 +++++++++++ 2 files changed, 122 insertions(+) create mode 100644 src/main/java/org/icatproject/utils/IcatUnits.java create mode 100644 src/test/java/org/icatproject/utils/TestIcatUnits.java diff --git a/src/main/java/org/icatproject/utils/IcatUnits.java b/src/main/java/org/icatproject/utils/IcatUnits.java new file mode 100644 index 0000000..285cc0b --- /dev/null +++ b/src/main/java/org/icatproject/utils/IcatUnits.java @@ -0,0 +1,77 @@ +package org.icatproject.utils; + +import javax.measure.IncommensurableException; +import javax.measure.UnconvertibleException; +import javax.measure.Unit; +import javax.measure.UnitConverter; +import javax.measure.format.MeasurementParseException; + +import tech.units.indriya.format.SimpleUnitFormat; + +/** + * Utility to perform conversions to SI (System) units. + */ +public class IcatUnits { + + /** + * Holds the SI units and value for a quantity. If the units provided at construction could not be parsed, then these will be null. + */ + public class SystemValue { + public String units = null; + public Double value = null; + + /** + * Converts value units into an SI quantity. + * + * @param value Quantity in (potentially) non-SI units. + * @param units Units of the provided value. + */ + public SystemValue(Double value, String units) { + try { + Unit unit = unitFormat.parse(units); + Unit systemUnit = unit.getSystemUnit(); + this.units = systemUnit.getName(); + if (value == null) { + return; + } + UnitConverter converter = unit.getConverterToAny(systemUnit); + this.value = converter.convert(value.doubleValue()); + } catch (MeasurementParseException | UnconvertibleException | IncommensurableException e) { + // If the units can't be parsed, or the value converted, then just return + return; + } + } + } + + private static final SimpleUnitFormat unitFormat = SimpleUnitFormat.getInstance(); + + /** + * Creates instance with any aliasing. + */ + public IcatUnits() { + } + + /** + * In addition to the standard names and prefixes, allows aliasing other terms + * for a unit. Note that Unit should be refered to by the symbol specified in + * the + * Indriya + * documentation, but the aliases can be any string. However, OoM prefixes + * will not be applied to aliases. For example, "mK" would be understood as + * 0.001K, but if "Kelvin" is aliased than "mKelvin" will not be understood. + * + * @param aliasOptions String with the format ": , + * : ..." + */ + public IcatUnits(String aliasOptions) { + for (String unitAliases : aliasOptions.split(",")) { + String[] splitUnitAliases = unitAliases.split(":"); + Unit unit = unitFormat.parse(splitUnitAliases[0].trim()); + for (String alias : splitUnitAliases[1].trim().split("\\s+")) { + unitFormat.alias(unit, alias); + } + } + } + +} \ No newline at end of file diff --git a/src/test/java/org/icatproject/utils/TestIcatUnits.java b/src/test/java/org/icatproject/utils/TestIcatUnits.java new file mode 100644 index 0000000..fa2705e --- /dev/null +++ b/src/test/java/org/icatproject/utils/TestIcatUnits.java @@ -0,0 +1,45 @@ +package org.icatproject.utils; + +import static org.junit.Assert.assertEquals; +import org.icatproject.utils.IcatUnits.SystemValue; +import org.junit.Test; + +public class TestIcatUnits { + @Test + public void testArguments() { + // Both null + IcatUnits icatUnits = new IcatUnits(); + SystemValue systemValue = icatUnits.new SystemValue(null, "impossible to parse"); + assertEquals(null, systemValue.units); + assertEquals(null, systemValue.value); + + // Unit parsed, value null + systemValue = icatUnits.new SystemValue(null, "K"); + assertEquals("Kelvin", systemValue.units); + assertEquals(null, systemValue.value); + + // Unit parsed, value converted + systemValue = icatUnits.new SystemValue(1., "GK"); + assertEquals("Kelvin", systemValue.units); + assertEquals(new Double(1e9), systemValue.value); + } + + @Test + public void testAliasing() { + IcatUnits icatUnits = new IcatUnits(); + SystemValue systemValue = icatUnits.new SystemValue(null, "celsius"); + assertEquals(null, systemValue.units); + systemValue = icatUnits.new SystemValue(null, "degC"); + assertEquals(null, systemValue.units); + systemValue = icatUnits.new SystemValue(null, "kelvin"); + assertEquals(null, systemValue.units); + + icatUnits = new IcatUnits("\u2103: celsius degC, K: kelvin"); + systemValue = icatUnits.new SystemValue(null, "celsius"); + assertEquals("Kelvin", systemValue.units); + systemValue = icatUnits.new SystemValue(null, "degC"); + assertEquals("Kelvin", systemValue.units); + systemValue = icatUnits.new SystemValue(null, "kelvin"); + assertEquals("Kelvin", systemValue.units); + } +} \ No newline at end of file From d231d3f77817c50b37200ddee9798d13afdfe265 Mon Sep 17 00:00:00 2001 From: Patrick Austin Date: Sat, 30 Apr 2022 01:59:57 +0100 Subject: [PATCH 2/8] Add unit conversion dependencies #19 --- pom.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pom.xml b/pom.xml index 805d0a6..ff70e5c 100644 --- a/pom.xml +++ b/pom.xml @@ -63,6 +63,18 @@ test + + javax.measure + unit-api + 2.1.3 + + + + tech.units + indriya + 2.1.3 + + From 8e54346d4f8ee2824ee6d1daafc0ad0b7d83a008 Mon Sep 17 00:00:00 2001 From: Patrick Austin Date: Fri, 10 Jun 2022 20:20:20 +0100 Subject: [PATCH 3/8] Prevent exception for empty aliasing string #19 --- src/main/java/org/icatproject/utils/IcatUnits.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/icatproject/utils/IcatUnits.java b/src/main/java/org/icatproject/utils/IcatUnits.java index 285cc0b..485cdf9 100644 --- a/src/main/java/org/icatproject/utils/IcatUnits.java +++ b/src/main/java/org/icatproject/utils/IcatUnits.java @@ -65,11 +65,13 @@ public IcatUnits() { * : ..." */ public IcatUnits(String aliasOptions) { - for (String unitAliases : aliasOptions.split(",")) { - String[] splitUnitAliases = unitAliases.split(":"); - Unit unit = unitFormat.parse(splitUnitAliases[0].trim()); - for (String alias : splitUnitAliases[1].trim().split("\\s+")) { - unitFormat.alias(unit, alias); + if (!aliasOptions.equals("")) { + for (String unitAliases : aliasOptions.split(",")) { + String[] splitUnitAliases = unitAliases.split(":"); + Unit unit = unitFormat.parse(splitUnitAliases[0].trim()); + for (String alias : splitUnitAliases[1].trim().split("\\s+")) { + unitFormat.alias(unit, alias); + } } } } From 32be44539da5fbee3f399bb03822a2f3316e8493 Mon Sep 17 00:00:00 2001 From: Patrick Austin Date: Mon, 20 Jun 2022 17:02:40 +0100 Subject: [PATCH 4/8] Allow numeric factors for conversion #19 --- .../java/org/icatproject/utils/IcatUnits.java | 24 +++++++++++++------ .../org/icatproject/utils/TestIcatUnits.java | 9 ++++--- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/icatproject/utils/IcatUnits.java b/src/main/java/org/icatproject/utils/IcatUnits.java index 485cdf9..5ee729a 100644 --- a/src/main/java/org/icatproject/utils/IcatUnits.java +++ b/src/main/java/org/icatproject/utils/IcatUnits.java @@ -14,7 +14,8 @@ public class IcatUnits { /** - * Holds the SI units and value for a quantity. If the units provided at construction could not be parsed, then these will be null. + * Holds the SI units and value for a quantity. If the units provided at + * construction could not be parsed, then these will be null. */ public class SystemValue { public String units = null; @@ -53,7 +54,7 @@ public IcatUnits() { /** * In addition to the standard names and prefixes, allows aliasing other terms - * for a unit. Note that Unit should be refered to by the symbol specified in + * for a unit. Note that Unit should be referred to by the symbol specified in * the * Indriya @@ -61,16 +62,25 @@ public IcatUnits() { * will not be applied to aliases. For example, "mK" would be understood as * 0.001K, but if "Kelvin" is aliased than "mKelvin" will not be understood. * - * @param aliasOptions String with the format ": , - * : ..." + * If needed, a conversion factor can be provided alongside an alias, for + * example to alias "eV" as a unit of energy, a factor of 1.602176634e-19 should + * be applied to convert to the SI unit J. + * + * @param aliasOptions String with the format + * symbolA: aliasA1, aliasA2 factorA2; symbolB: aliasB1 ... */ public IcatUnits(String aliasOptions) { if (!aliasOptions.equals("")) { - for (String unitAliases : aliasOptions.split(",")) { + for (String unitAliases : aliasOptions.split(";")) { String[] splitUnitAliases = unitAliases.split(":"); Unit unit = unitFormat.parse(splitUnitAliases[0].trim()); - for (String alias : splitUnitAliases[1].trim().split("\\s+")) { - unitFormat.alias(unit, alias); + for (String alias : splitUnitAliases[1].trim().split(",")) { + String[] aliasSplit = alias.trim().split("\\s+"); + if (aliasSplit.length == 2) { + unitFormat.alias(unit.multiply(new Double(aliasSplit[1])), aliasSplit[0]); + } else { + unitFormat.alias(unit, aliasSplit[0]); + } } } } diff --git a/src/test/java/org/icatproject/utils/TestIcatUnits.java b/src/test/java/org/icatproject/utils/TestIcatUnits.java index fa2705e..3384573 100644 --- a/src/test/java/org/icatproject/utils/TestIcatUnits.java +++ b/src/test/java/org/icatproject/utils/TestIcatUnits.java @@ -33,13 +33,16 @@ public void testAliasing() { assertEquals(null, systemValue.units); systemValue = icatUnits.new SystemValue(null, "kelvin"); assertEquals(null, systemValue.units); + systemValue = icatUnits.new SystemValue(null, "eV"); + assertEquals(null, systemValue.units); - icatUnits = new IcatUnits("\u2103: celsius degC, K: kelvin"); + icatUnits = new IcatUnits("\u2103: celsius, degC; K: kelvin; J: eV 1.602176634e-19"); systemValue = icatUnits.new SystemValue(null, "celsius"); assertEquals("Kelvin", systemValue.units); systemValue = icatUnits.new SystemValue(null, "degC"); assertEquals("Kelvin", systemValue.units); - systemValue = icatUnits.new SystemValue(null, "kelvin"); - assertEquals("Kelvin", systemValue.units); + systemValue = icatUnits.new SystemValue(1., "eV"); + assertEquals("Joule", systemValue.units); + assertEquals(new Double(1.602176634e-19), systemValue.value); } } \ No newline at end of file From c471cb0e2c931e64c330314dd0bf537600753473 Mon Sep 17 00:00:00 2001 From: Patrick Austin Date: Fri, 9 Sep 2022 04:59:54 +0100 Subject: [PATCH 5/8] Update version #19 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ff70e5c..3a024d4 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 org.icatproject - 4.16.2-SNAPSHOT + 4.17.0-SNAPSHOT icat.utils jar ICAT Utils From f97f29ed985bc83c94d8aabc38b9e66c585e0691 Mon Sep 17 00:00:00 2001 From: Patrick Austin Date: Fri, 21 Oct 2022 16:02:13 +0100 Subject: [PATCH 6/8] Use assertNull in tests #19 --- .../org/icatproject/utils/TestIcatUnits.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/test/java/org/icatproject/utils/TestIcatUnits.java b/src/test/java/org/icatproject/utils/TestIcatUnits.java index 3384573..cff8070 100644 --- a/src/test/java/org/icatproject/utils/TestIcatUnits.java +++ b/src/test/java/org/icatproject/utils/TestIcatUnits.java @@ -1,6 +1,8 @@ package org.icatproject.utils; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + import org.icatproject.utils.IcatUnits.SystemValue; import org.junit.Test; @@ -10,13 +12,13 @@ public void testArguments() { // Both null IcatUnits icatUnits = new IcatUnits(); SystemValue systemValue = icatUnits.new SystemValue(null, "impossible to parse"); - assertEquals(null, systemValue.units); - assertEquals(null, systemValue.value); + assertNull(systemValue.units); + assertNull(systemValue.value); // Unit parsed, value null systemValue = icatUnits.new SystemValue(null, "K"); assertEquals("Kelvin", systemValue.units); - assertEquals(null, systemValue.value); + assertNull(systemValue.value); // Unit parsed, value converted systemValue = icatUnits.new SystemValue(1., "GK"); @@ -28,15 +30,16 @@ public void testArguments() { public void testAliasing() { IcatUnits icatUnits = new IcatUnits(); SystemValue systemValue = icatUnits.new SystemValue(null, "celsius"); - assertEquals(null, systemValue.units); + assertNull(systemValue.units); systemValue = icatUnits.new SystemValue(null, "degC"); - assertEquals(null, systemValue.units); + assertNull(systemValue.units); systemValue = icatUnits.new SystemValue(null, "kelvin"); - assertEquals(null, systemValue.units); + assertNull(systemValue.units); systemValue = icatUnits.new SystemValue(null, "eV"); - assertEquals(null, systemValue.units); + assertNull(systemValue.units); - icatUnits = new IcatUnits("\u2103: celsius, degC; K: kelvin; J: eV 1.602176634e-19"); + new IcatUnits("\u2103: celsius, degC; K: kelvin; J: eV 1.602176634e-19"); + new IcatUnits(); systemValue = icatUnits.new SystemValue(null, "celsius"); assertEquals("Kelvin", systemValue.units); systemValue = icatUnits.new SystemValue(null, "degC"); From 835e782ba2a99825026efe63e6a24598d94bf567 Mon Sep 17 00:00:00 2001 From: Patrick Austin Date: Fri, 8 Sep 2023 16:02:43 +0000 Subject: [PATCH 7/8] 4.17.0 release notes --- src/site/xhtml/release-notes.xhtml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/site/xhtml/release-notes.xhtml b/src/site/xhtml/release-notes.xhtml index 27da45a..2f4a975 100644 --- a/src/site/xhtml/release-notes.xhtml +++ b/src/site/xhtml/release-notes.xhtml @@ -4,6 +4,10 @@

Release notes

+ +

4.17.0

+

Adds support for unit conversion, to be used in icat.server and icat.lucene as part of search improvements.

+

4.16.1

Python 3 compatibility

4.16.0

From e18701f92e626015a1e8b9ece13903d87004f54b Mon Sep 17 00:00:00 2001 From: Patrick Austin Date: Fri, 22 Mar 2024 11:47:34 +0000 Subject: [PATCH 8/8] Refactor IcatUnits for clarity and to use getNewInstance --- .../java/org/icatproject/utils/IcatUnits.java | 57 +++++++++++-------- .../org/icatproject/utils/TestIcatUnits.java | 56 +++++++++--------- 2 files changed, 60 insertions(+), 53 deletions(-) diff --git a/src/main/java/org/icatproject/utils/IcatUnits.java b/src/main/java/org/icatproject/utils/IcatUnits.java index 5ee729a..4e0b76e 100644 --- a/src/main/java/org/icatproject/utils/IcatUnits.java +++ b/src/main/java/org/icatproject/utils/IcatUnits.java @@ -14,37 +14,25 @@ public class IcatUnits { /** - * Holds the SI units and value for a quantity. If the units provided at - * construction could not be parsed, then these will be null. + * Holds the numerical value and SI units for a quantity. */ - public class SystemValue { - public String units = null; - public Double value = null; + public static class Value { + public String units; + public double numericalValue; /** - * Converts value units into an SI quantity. + * Instantiate with a numericalValue and units. * - * @param value Quantity in (potentially) non-SI units. - * @param units Units of the provided value. + * @param numericalValue Numerical value of the quantity. + * @param units Units of the provided value. */ - public SystemValue(Double value, String units) { - try { - Unit unit = unitFormat.parse(units); - Unit systemUnit = unit.getSystemUnit(); - this.units = systemUnit.getName(); - if (value == null) { - return; - } - UnitConverter converter = unit.getConverterToAny(systemUnit); - this.value = converter.convert(value.doubleValue()); - } catch (MeasurementParseException | UnconvertibleException | IncommensurableException e) { - // If the units can't be parsed, or the value converted, then just return - return; - } + public Value(double numericalValue, String units) { + this.numericalValue = numericalValue; + this.units = units; } } - private static final SimpleUnitFormat unitFormat = SimpleUnitFormat.getInstance(); + private final SimpleUnitFormat unitFormat = SimpleUnitFormat.getNewInstance(); /** * Creates instance with any aliasing. @@ -77,7 +65,7 @@ public IcatUnits(String aliasOptions) { for (String alias : splitUnitAliases[1].trim().split(",")) { String[] aliasSplit = alias.trim().split("\\s+"); if (aliasSplit.length == 2) { - unitFormat.alias(unit.multiply(new Double(aliasSplit[1])), aliasSplit[0]); + unitFormat.alias(unit.multiply(Double.parseDouble(aliasSplit[1])), aliasSplit[0]); } else { unitFormat.alias(unit, aliasSplit[0]); } @@ -86,4 +74,25 @@ public IcatUnits(String aliasOptions) { } } + /** + * Converts a value into SI units. + * + * @param numericalValue Numerical value of a quantity associated with a unit. + * @param units Units of the quantity. + * @return Either an instance of Value, or null if the units could not be converted. + */ + public Value convertValueToSiUnits(double numericalValue, String units) { + try { + Unit unit = unitFormat.parse(units); + Unit systemUnit = unit.getSystemUnit(); + String convertedUnits = systemUnit.getName(); + UnitConverter converter = unit.getConverterToAny(systemUnit); + double convertedValue = converter.convert(numericalValue); + return new Value(convertedValue, convertedUnits); + } catch (MeasurementParseException | UnconvertibleException | IncommensurableException e) { + // If the units can't be parsed, or the value converted, then just return null + return null; + } + } + } \ No newline at end of file diff --git a/src/test/java/org/icatproject/utils/TestIcatUnits.java b/src/test/java/org/icatproject/utils/TestIcatUnits.java index cff8070..55af560 100644 --- a/src/test/java/org/icatproject/utils/TestIcatUnits.java +++ b/src/test/java/org/icatproject/utils/TestIcatUnits.java @@ -3,49 +3,47 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -import org.icatproject.utils.IcatUnits.SystemValue; +import org.icatproject.utils.IcatUnits.Value; import org.junit.Test; public class TestIcatUnits { @Test public void testArguments() { - // Both null + // Units cannot be parsed null IcatUnits icatUnits = new IcatUnits(); - SystemValue systemValue = icatUnits.new SystemValue(null, "impossible to parse"); - assertNull(systemValue.units); - assertNull(systemValue.value); + Value value = icatUnits.convertValueToSiUnits(1., "impossible to parse"); + assertNull(value); - // Unit parsed, value null - systemValue = icatUnits.new SystemValue(null, "K"); - assertEquals("Kelvin", systemValue.units); - assertNull(systemValue.value); + // Unit parsed, value unchanged + value = icatUnits.convertValueToSiUnits(1., "K"); + assertEquals("Kelvin", value.units); + assertEquals(1., value.numericalValue, 0.); // Unit parsed, value converted - systemValue = icatUnits.new SystemValue(1., "GK"); - assertEquals("Kelvin", systemValue.units); - assertEquals(new Double(1e9), systemValue.value); + value = icatUnits.convertValueToSiUnits(1., "GK"); + assertEquals("Kelvin", value.units); + assertEquals(1e9, value.numericalValue, 0.); } @Test public void testAliasing() { IcatUnits icatUnits = new IcatUnits(); - SystemValue systemValue = icatUnits.new SystemValue(null, "celsius"); - assertNull(systemValue.units); - systemValue = icatUnits.new SystemValue(null, "degC"); - assertNull(systemValue.units); - systemValue = icatUnits.new SystemValue(null, "kelvin"); - assertNull(systemValue.units); - systemValue = icatUnits.new SystemValue(null, "eV"); - assertNull(systemValue.units); + Value value = icatUnits.convertValueToSiUnits(1., "celsius"); + assertNull(value); + value = icatUnits.convertValueToSiUnits(1., "degC"); + assertNull(value); + value = icatUnits.convertValueToSiUnits(1., "kelvin"); + assertNull(value); + value = icatUnits.convertValueToSiUnits(1., "eV"); + assertNull(value); - new IcatUnits("\u2103: celsius, degC; K: kelvin; J: eV 1.602176634e-19"); - new IcatUnits(); - systemValue = icatUnits.new SystemValue(null, "celsius"); - assertEquals("Kelvin", systemValue.units); - systemValue = icatUnits.new SystemValue(null, "degC"); - assertEquals("Kelvin", systemValue.units); - systemValue = icatUnits.new SystemValue(1., "eV"); - assertEquals("Joule", systemValue.units); - assertEquals(new Double(1.602176634e-19), systemValue.value); + icatUnits = new IcatUnits("\u2103: celsius, degC; K: kelvin; J: eV 1.602176634e-19"); + value = icatUnits.convertValueToSiUnits(1., "celsius"); + assertEquals("Kelvin", value.units); + value = icatUnits.convertValueToSiUnits(1., "degC"); + assertEquals("Kelvin", value.units); + value = icatUnits.convertValueToSiUnits(1., "eV"); + assertEquals("Joule", value.units); + assertEquals(1.602176634e-19, value.numericalValue, 0.); } } \ No newline at end of file