# Use unitful unit to a variable

How can I attach a unit to a variable ?

e.g.

``````julia> using Unitful

julia> 25u"km" #works
25 km

julia> a = 25; # define a variable

julia> #would expect something like that or similar to work
julia> au = @u_str(a, "km")
ERROR: LoadError: MethodError: no method matching var"@u_str"(::LineNumberNode, ::Module, ::Symbol, ::String)
Closest candidates are:
var"@u_str"(::LineNumberNode, ::Module, ::Any) at ~/.julia/packages/Unitful/ApCuY/src/user.jl:629
in expression starting at REPL[6]:1
``````

For now I solve this with `eval`, but it doesn’t feel right:

``````julia> au = eval(:a)u"km"
25 km
``````

What’s the way to go here ?

``````julia> a = 25u"km"
25 km

julia> a
25 km
``````

this?

Or this:

``````julia> b = 25 * oneunit(a)
25 km
``````

In some sense, you cannot “attach” a unit to variable, because a variable with a unit is in some sense just a type of number. Is like if you could attach the property of being a `Complex{Float64}` to a `Float64`. You cannot, you have to create a new variable for that.

1 Like

I want to take the value inside of a variable and attach it to a type.
I don’t want to use a literal like `25` again.
E.g., let’s say I derived programmatically a long `Int[]` array and I want to attach `u"km"` to it.
Can I do it without `eval` ?

``````julia> very_complex_func() = [1,2,3,4,5]
very_complex_func (generic function with 1 method)

julia> ar = very_complex_func();

julia> aru = eval(ar)u"km"
5-element Vector{Quantity{Int64, 𝐋, Unitful.FreeUnits{(km,), 𝐋, nothing}}}:
1 km
2 km
3 km
4 km
5 km
``````

I don’t quite agree.
I can initiate a `Complex` and a `Float64` with a variable and not always a literal is needed.
Also I can “attach” a `Float64` value to a `Complex{Float64}` like the following:

``````julia> num = 3;

julia> a = Float64(num);

julia> ac = Complex(a,a)
3.0 + 3.0im

``````

Instead to initialize a `Unitful` type, it looks like I always need a literal.

You can multiply by the unit, for example. But I don’t know if this is what you don’t want to do.

``````julia> using Unitful
julia> a = [1, 2, 3]
julia> b = 1u"km".*a
3-element Vector{Quantity{Int64, 𝐋, Unitful.FreeUnits{(km,), 𝐋, nothing}}}:
1 km
2 km
3 km
``````

What you were trying to do with `@u_str` is also possible, but there is no need to introduce the `a` intro the macro call.

``````julia> a*@u_str("km")
3-element Vector{Quantity{Int64, 𝐋, Unitful.FreeUnits{(km,), 𝐋, nothing}}}:
1 km
2 km
3 km
``````

What happens is that the way of “attaching” a unit to a value, even in the example of a = 25u"km" is doing a product between `25` and the thing that comes back from `u"km"`

2 Likes

I don´t know if `Unitful` has a specific function for that. But you cannot “attach” the units to such array, because the array is of immutable values, and the unitful values are a different type of animal.

Thus, you can do, for example:

``````julia> x = [1,2,3,4,5];

julia> x .* 1u"km"
5-element Vector{Quantity{Int64, 𝐋, Unitful.FreeUnits{(km,), 𝐋, nothing}}}:
1 km
2 km
3 km
4 km
5 km
``````

I’m not sure if there is a `convert(...)` type of function in `Unitful` for doing that with another syntax. But anyway you need to define a new variable.

(I think that if the purpose is to propagate units that are attached to some input variable, the most useful pattern is something like:

``````julia> a = 1u"km"
1 km

julia> x = [1,2];

julia> y = x .* oneunit(a)
2-element Vector{Quantity{Int64, 𝐋, Unitful.FreeUnits{(km,), 𝐋, nothing}}}:
1 km
2 km
``````
1 Like

Yeah, the multiplication with `1u"km"` again works (probably better than the `eval`) but again it seems to me like a “hack”. Anyway, I better get used to it. Thanks!

It is not a hack, it is actually the usual way:

You can see it with:

``````julia> @which 25u"km"
*(x::Number, y::Unitful.Units, z::Unitful.Units...) in Unitful at /Users/arreyes/.julia/packages/Unitful/ApCuY/src/quantities.jl:26
``````

This actually calls the product of 25 and u"km". This product is defined at unitful. So it is not more esoteric than that.

3 Likes

If you think in terms of actual units, it is not a hack, it is what actually make sense. A dimensionless quantity does not assume dimensions, but it may be multiplied by a quantity with dimensions, and the result has the dimensions of that quantity.

It is not that you can “fix” the fact that you forgot to write the units of a quantity, because the types are different. For instance, you cannot do this:

``````julia> x = [ 1, 2 ];

julia> x[1] = 1u"km"
ERROR: DimensionError:  and km are not dimensionally compatible.
``````

Because `x` is a container of unitless (`Int64`) numbers only.

You can, however, do this:

``````julia> x = [1.0, 2.0]u"km"
2-element Vector{Quantity{Float64, 𝐋, Unitful.FreeUnits{(km,), 𝐋, nothing}}}:
1.0 km
2.0 km

julia> x[1] = 1.0u"m"
1.0 m

julia> x
2-element Vector{Quantity{Float64, 𝐋, Unitful.FreeUnits{(km,), 𝐋, nothing}}}:
0.001 km
2.0 km
``````

since how it makes sense to convert the meter to the km unit which the vector carries.

*you could do this:

``````julia> x = Number[1.0, 2.0];

julia> x[1] = 1.0u"km"
1.0 km

julia> x
2-element Vector{Number}:
1.0 km
2.0
``````

but that is only because that vector can contain anything that is a subtype of number, and there is no relation between the values before and after the mutation - and this is very bad for performance.

2 Likes