Introduction to KotUniL

Mars Climate Orbiter
Mars Climate Orbiter

Amps cannot be added to volts. Centimeters can be added to inches, but very carefully. Otherwise, it will turn out like with the $125 million Mars Climate Orbiter spacecraft, which successfully flew to Mars, but mediocrely crashed about its surface.

It crashed because the developers of its software did not take into account the difference in physical units used in different parts of the system. For the same reason, before and after this expensive accident, space and aircraft exploded and fell, ships sank and people died.

These disasters and deaths could have been avoided if on-board and system software programmers used specialized libraries like KotUniL, which I want to talk about in this series of articles, in their work.

The first (this) article is actually about the library, its capabilities and simple rules for using it. Other articles in this series touch on topics that all programmers, regardless of the language they use, may find useful and interesting, although Kotlinians may find them more useful than others.

Here is the complete list of articles in the series:

  1. The magic of dimensions and the magic of Kotlin. Part One: Introduction to KotUniL

  2. The magic of dimensions and the magic of Kotlin. Part Two: Advanced Features of KotUniL (coming soon)

  3. The magic of dimensions and the magic of Kotlin. Part Three: Mixing Magic (coming soon)

How it all began

In my project, I came across the need to work with formulas using the physical quantities of the SI system, which, on the one hand, must be programmed in Kotlin, but on the other hand, must be understandable to physicists.

It is my deep conviction that libraries for working with physical quantities should, along with libraries for working with files, Internet protocols, etc. be one of the standard libraries supplied by the language developers. However, this is not true. Which leads to the fact that firms – “enterprise sharks” for big money develop them themselves.

Unfortunately, my favorite Kotlin also comes without such a library. There is no holy place, and on GitHub you can find several applicants for this role. Having studied them, I did not find any library that meets my requirements. (I hope I didn’t miss one in my search). Disappointed by this circumstance, I decided to write my own library.

Thus, I dived into the magic of physical dimensions, and emerged into the magic of type algebra.

Later in this article, I will briefly talk about the results achieved.

KotUniL

KotUniL (Kotlin Units Library) is a library of Kotlin functions and objects that generally meet the following requirements:

  1. Covers all base units SIsuch as meter, second, etc., as well as some other common non-physical units, such as currencies, percentages, etc.

  2. Can accurately work with all SI prefixes – micro, nano, kilo, etc.

  3. Allows you to write various formulas in the Kotlin language in a way that is as close as possible to how formulas are written in physics and economics.

  4. Allows you to analyze the dimension of the results of applying complex formulas.

  5. Allows you to detect most typical errors when working with SI units already at the compilation stage. Errors in the incorrect use of physical units in complex formulas are detected at runtime, but can be easily detected during normal unit testing.

  6. It is a pure library (no plugin, no parser, etc.) that does not have any dependencies on third party libraries.

Not very clear? Then let’s look at how the library works on a number of examples, starting with the simplest. (I won’t be wiser and will give here a few examples from the library documentation on GitHub).

Masha soap… aquarium

Let’s consider the first example.

Masha was wiping the glass of the aquarium from the outside, touched a vase standing nearby, as a result of which the glass of the aquarium broke and water leaked onto the floor. The aquarium before this trouble was 32 liters of water. Masha’s room is 4 meters long and 4.3 meters wide. At what height in mm. is there water in the room now, assuming it stayed there and didn’t leak out?

A Kotlin/KotUniL solution can be written in one line. For didactic purposes, we introduce two auxiliary variables s and h for the area of ​​the room and the water level in the room.

val s = 4.m * 4.3.m
val h = 32.l/s   
print(«Высота воды в комнате ${h.mm} mm."

Let’s take a closer look. The area of ​​a room is measured in square meters. Variable s as a result of multiplying meters by meters, this dimension was obtained implicitly. A liter is a thousandth of a cubic meter. Cubic meters divided by square meters results in just meters (variable h). But we want to translate their millimeters, which we do when printing.

It’s more than type safety

“And what does the spacecraft that crashed on the surface of Mars have to do with it?” – perhaps one of the readers will ask.

The thing is that if we set the knowledge of the variables of our calculations not just with numbers, but at the same time indicate physical (and not only) dimensions, errors when trying to manipulate the wrong dimensions will be detected either already at the compilation stage or at the very first unit test, “running” according to the wrong formula:

//val x = 1.m + 2 ошибка компиляции
//val y = 20.l/(4.m * 5.m) + 14 ошибка компиляции

//Более заковыристые ошибки выявляются в runtime:
val exception = assertFailsWith<IllegalArgumentException>(
  block = { 1.m + 2.s }
)
assertTrue(exception.message!!.startsWith(COMPATIBILITY_ERR_PREFIX))

I want to especially emphasize this feature of the library: if your formula is incorrect, then regardless of the values ​​​​of physical quantities used, any unit test that “runs” through it will show its incorrectness. Why this is so, I will try to show in the next article in this series.

In the meantime, let’s pay attention to the fact that the described feature of the library is more than the classic type safety. We are talking here not only about the correctness of the units themselves, but also about the results calculated using arithmetic formulas of arbitrary complexity.

Comparison of complex objects

The library allows not only to add, subtract, multiply, divide and raise physical and other units to a power. She knows how to compare them correctly. And not only the original units, but also derivatives from them, obtained using the above operations.

As with addition and subtraction, only objects of the same type can be compared:

 assertTrue(5.m > 4.1.m)
 assertTrue(20.2*m3 > 4.2*m3)
 assertTrue(2.2*kg*m/s < 4.2*kg*m/s)

When trying to compare objects of different types, the library will throw an IllegalArgumentException

 val v1 = 2.4.m
 val v2 = 2.4.s
 val exception = assertFailsWith<IllegalArgumentException>(
   block = { v1 >= v2 }
 )
 assertTrue(exception.message!!.startsWith(COMPATIBILITY_ERR_PREFIX))

or:

 val v1 = 2.4.m*kg/s
 val v2 = 2.4.s*m3/μV
 val exception = assertFailsWith<IllegalArgumentException>(
   block = { v1 >= v2 }
 )
 assertTrue(exception.message!!.startsWith(COMPATIBILITY_ERR_PREFIX))

If you are wondering what does μV are microvolts. But we will talk about them and other prefix expressions within the SI system and KotUniL in the next article in the series.

Dimensional Analysis

Dimensional analysis is a very interesting area of ​​physics that allows you to quickly build and test hypotheses. Going into this topic is not included in my plans, but for those who are interested, I can recommend here this and this articles.

When working with physical and other dimensions, it is possible to “heap” such complex formulas that it would be nice to know what dimension we ended up with. This allows you to do two functions of the library.

In the SI system, each physical unit is given its name (for example, the meter, or m) and category (in the case of the meter, this is the length, or L).

Function unitSymbols() shows symbols of dimension and their degrees:

 val s = 4.m * 5.m
 assertEquals("m2", s.unitSymbols())

 val x = 20.l
 assertEquals("m3", x.unitSymbols())

 val h = x/s
 assertEquals("m", h.unitSymbols())

 val y = 1.2.s
 assertEquals("s", y.unitSymbols())

 val z = x/y
 assertEquals("m3/s", z.unitSymbols())

A function categorySymbols() – about the same for categories and in a slightly different form:

 val s = 4.m * 5.m
 assertEquals("L2", s.categorySymbols())

 val x = 20.l
 assertEquals("L3", x.categorySymbols())

 val h = x/s
 assertEquals("L", h.categorySymbols())

 val y = 1.2.s
 assertEquals("T", y.categorySymbols())

 val z = x/y
 assertEquals("L3T-1", z.categorySymbols())

How to use?

As mentioned above, KotUniL is a Kotlin library without any external dependencies. Therefore, it is very easy to connect it to your Kotlin project. In the case of gtadle/KTS this is done by adding to your build.gradle.kts lines:

repositories {
   mavenCentral()
 }

 dependencies {
   implementation("eu.sirotin.kotunil:kotunil:1.0.1")
 }

You need to add similar dependencies to the pom file in case of using Maven:

    <dependency>

        <groupId>eu.sirotin.kotunil</groupId>

        <artifactId>kotunil</artifactId>

        <version>1.0.1</version>

    </dependency>

Well, you can find the source codes of the library on GitHub: https://github.com/vsirotin/si-units

If you add an asterisk to the project at the same time, the author will not be offended 🙂

This concludes our introduction to the main features of the library. In the next article in this series, we will get acquainted with its “advanced” features.

Illustration: The Mars Climate Orbiter spacecraft crashed due to the fault of programmers who incorrectly worked with physical dimensions. Source: Wikipedia

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *