Getting and/or removing units from a variable

Description of problem

I might have completely overlooked something, but I couldn’t find any way to do any of the following:

  • get the unit of a variable (for scalar and numpy arrays)
  • get the numerical value of the variable (independent of unit), i.e., remove the unit of a variable when it is not known beforehand

Minimal code to reproduce problem

var = 10. * b2.mV

what I want:

  • func(var) → b2.mV, or
  • func(var) → 10

What you have already tried

np.array(var) (as well as calling pd.Dataframe on dictionary objects containing arrays with units) automatically transforms the variable to the base unit (e.g., V) before returning the value, in the above case, 0.01, and I don’t know the unit a priori to just divide through by it, but I would like to just receive the numerical value of the variable as I’m keeping track of the non-base units elsewhere.

Specifically, I’m saving out parameter values in a pandas dataframe, and their associated units in a dictionary elsewhere, and I would just like to save the numbers in the unit that’s most commonly used (e.g., pF instead of F).

If there’s a strong reason why doing this is not preferable, I suppose one solution is to do the whole thing in base unit? Please advise. Thanks!

Hi rdgao,

the brian docs recommendation is:

There are various options to remove the units from a value (e.g. to use it with analysis functions that do not correctly work with units)

  • Divide the value by its unit (most of the time the recommended option because it is clear about the scale)
  • Transform it to a pure numpy array in the base unit by calling asarray() (no copy) or array (copy)
  • Directly get the unitless value of a state variable by appending an underscore to the name
    e.g. print(G.v_[:])

I’m not sure whether Brian keeps track of the scale of unit (i.e. mV versus V) when you assign a value to a variable, or whether its storing everything in terms of the base unit, then choosing an appropriate scale when you print it. I think it’s the latter, in which case you’d need to manually specify what non-base scale you want (because it doesn’t remember you originally specified the value in pF for instance).

I’d suggest either doing your operations in the base unit (then you don’t have to keep track of scales for conversion) OR writing a simple conversion function yourself to specify your preferred scale. It’s probably most “within the spirit of Brian” to have any conversion be as transparent as possible:

Accordingly, the Brian simulator requires quantities provided by the user, such as parameters or initial values of dynamical variables, to be specified in consistent physical units such as mV or s. This is in contrast to the approach of most other simulators, which simply define expected units for all model components, for example units of mV for the membrane potential. This is a common source of error because conventions are not always obvious and can be inconsistent. For example, while membrane surface area is often stated in units of μm2, channel densities are often given in mS cm−2. To remove this potential source of error, the Brian simulator enforces explicit use of units.
elife paper

2 Likes

here’s some quick confirmation that scales of units aren’t preserved from assignment, they’re simply printed at a convenient human-readable precision, and a demo of a very simple conversion function.

looks like if you wanted the base unit for a variable you can use get_unit(bVar.dim).

source code:
import numpy as np
from brian2 import *

y = 5*ms
print(y)

x = (5/1000)*second
print(x)
#^ prints as ms even though I assigned the variable as second

TIME_BASE = ms

def brian_to_unitless_time(bVar):
    return bVar / TIME_BASE
def unitless_time_to_brian(utime):
    return utime*TIME_BASE

test_time_array = np.linspace(0, 0.03, 4)*second
print(test_time_array)

print(brian_to_unitless_time(test_time_array))

print(unitless_time_to_brian(brian_to_unitless_time(test_time_array)))


def get_brian_unit(bVar):
    """ returns the base unit for a brian variable """
    return get_unit(bVar.dim)

def brian_to_unitless_base(bVar):
    """ blindly removes the base unit from a brian2 variable """
    return bVar / get_brian_unit(bVar)

capacitance = 100*pF
print(capacitance)
print( f"base unit for {str(capacitance)} is {str(get_brian_unit(capacitance))}" )

thanks for the suggestion, Adam!

Yeah it probably makes the most sense that Brian stores the value in its base unit internally, in which case I will probably write a small dictionary that specifies the preferred scale for all the units in my simulator.

1 Like