Skip to content

Advice on unit-aware arithmetic #2176

Closed
@mcgibbon

Description

@mcgibbon

This isn't really a bug report. In sympl we're using DataArrays that allow unit-aware operations using the 'units' attribute as the only persistent unit storage. We use pint as a backend to operate on unit strings, but this is never exposed to the user and could be swapped for another backend without much consequence.

Basically, we currently have this implemented as a subclass sympl.DataArray. @dopplershift recently introduced me to the accessor interface, and I've been thinking about whether to switch over to that way of extending DataArray.

The problem I have is that the new code that results from using an accessor is quite cumbersome. The issue lies in that we mainly use new implementations for arithmetic operations. So, for example, the following code:

dt = DataArray(timestep.total_seconds(), attrs={'units': 's'})
for key in tendencies_list[0].keys():
    return_state[key] = state[key] + dt * (
        1.5 * tendencies_list[-1][key] - 0.5 * tendencies_list[-2][key]
    )

instead becomes

dt = DataArray(timestep.total_seconds(), attrs={'units': 's'})
for key in tendencies_list[0].keys():
    return_state[key] = state[key].sympl.add(
        dt.sympl.multiply(
            tendencies_list[-1][key].sympl.multiply(1.5).sympl.subtract(
                tendencies_list[-2][key].sympl.multiply(0.5)
            )
        )
    )

This could be a little less cumbersome if we avoid a sympl namespace and instead add separate accessors for each method. At the least it reads naturally. However, there's a reason you don't generally recommend doing this.

dt = DataArray(timestep.total_seconds(), attrs={'units': 's'})
for key in tendencies_list[0].keys():
    return_state[key] = state[key].add(
        dt.multiply(
            tendencies_list[-1][key].multiply(1.5).subtract(
                tendencies_list[-2][key].multiply(0.5)
            )
        )
    )

I'm looking for advice on what is best for sympl to do here. Right now I'm leaning towards that we should use a subclass rather than an accessor - does this seem like an appropriate case to do so?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions