Units#

Within POLARIS there are many quantities that are represented within the various models, each of which has a variety of representations that it could take. The distance between two points could be 2 miles or equivalently 3.2 kilometers.

There are occasions where our code won’t really care about what units a quantity is expressed in. For example if we are calculating the free-flow speed of a road segment as a fraction of its posted speed - we might say somehting like:

auto freeflow_speed = 0.95 * link->posted_speed();

We care that the units are preserved, but we don’t care what they actually are.

There are many other occasions where we absolutely need to care about the units being used. For example if we define a cost function for a choice model that uses distance as a component, it will be very important if that cost function evaluates using the distance in miles or the distance in kilometers.

To keep track of the various unit types and the conversion between them, we utilise the units library.

The general rule of thumb for using units is to keep our quantity in a compatible unit type for as long as possible and only convert back to a float/double when we are performing model calculations that are no longer representable as any unit type. For example:

// All of these operations are performed using unit types (_t)
meter_per_second_t link_speed = link->congested_speed();
kilometer_t        link_length = link->length();
second_t           travel_time = link_length / link_speed;

// when we convert back to a dummy variable (bool) we compare against another unit type (20 minutes)
bool dummy_is_long_travel_time = travel_time > 20_mins;

// If we were going to put this into a utility function, we would **only** then convert to a primitive (float) type
// as utility is no longer a phyisical quantity
float utility = _beta_tt * travel_time.to<float> + _beta_x * person->owns_an_ev();

Transition from Basic_Units to units#

At the start of 2024 the core team started the process of transitioning from an internally developed units library to a more feature rich open source library. This is currently (2024/02) an ongoing process and you may see both Basic_Units and units throughout the codebase.

Key examples of converting between the two are shown in the tests/core_tests/Units_Tests.h test file.

An example of the two different ways that these two libraries could be used to represent a distance quantity on a component are shown below.

implementation struct UnitTester : public Polaris_Component<MT_, INHERIT(UnitTester)>
{
    // Old way
    member_component_and_feature_accessor(distance_old, Value, Basic_Units::Implementations::Length_Implementation<NT>);

    // New way
    t_data(meter_t, distance_new);
};

void foo() {
    auto obj = Allocate<UnitTester<MT_>>();

    // Setters
    obj->template distance_old<Meters>(13.0);
    obj->template distance_old<Centimeters>(1300.0);

    obj->distance_new(13_m);
    obj->distance_new(1300_cm);
    obj->distance_new(meter_t(13));

    // Getters
    Meters d = obj->template distance_old<Meters>();
    assert d.Value == 13.00;

    inch_t ud = obj->distance_new(); // it doesn't matter what unit type we store in now
    assert ud == 13_m;               // we can still compare it to other length type variables directly
    assert ud == 1300_cm;
}