dtype of coords array passed into BinMapper.assign()

4 views
Skip to first unread message

Schuyler Byrn

unread,
Feb 18, 2026, 3:41:05 PM (2 days ago) Feb 18
to westpa-users
Hi everyone,

I'm writing a script that involves unpacking the bin mapper for an iteration and then using mapper.assign() to assign bins. The documentation says

"A bin mapper must implement, at least, an assign(coords, mask=None, output=None) method, which is responsible for mapping each of the vector of coordinate tuples coords to an integer (np.uint16) indicating a what bin that coordinate tuple falls into."

From this I gathered that if I have a multidimensional pcoord (such as 2D rectilinear binning) then I want my coords array to be a 2D array of shape (nsegs, pcoord_length) (where pcoord_length is the number of md frames in a seg) or tuples of floats. However, it seems that the code enforces that the type be simply of float. 

How exactly do I pass in a multi-dimensional pcoord, then? Also, let me know if my question is not clear. Thanks!

Best,
Schuyler

Jeremy Leung

unread,
Feb 18, 2026, 4:00:56 PM (2 days ago) Feb 18
to westpa-users
Hi Schuyler,

I'm a little confused as to what isn't working on your end. Is `bin_mapper.assign()` erroring out based on your input? If so, I'll try to answer with an example.

Here's an example, with a 2x2 binning scheme, mapping four points into it:
```
In [1]: from westpa.core.binning import RectilinearBinMapper

In [2]: bm = RectilinearBinMapper([[0, 5, 10], [0, 5, 10]])

In [3]: bm.assign([[1,1], [1,7], [7,1], [7,7]])
Out[3]: array([0, 1, 2, 3], dtype=uint16)
```
This shows that the four points, which are 2D, are mapped to bins [0, 1, 2, 3]. If you're not using any recursive bins, these indices are expanded in row-major order. Otherwise, it's a little more complicated (https://github.com/westpa/westpa/pull/524).

Attaching code below on loading the bin mapper, just in case you need it. 

```
However, it seems that the code enforces that the type be simply of float. 
```
I'm also not sure I'm seeing this in the code. but the rest of your assumptions are correct.


Best,

Jeremy L.

---
Jeremy M. G. Leung, PhD
Research Assistant Professor, Chemistry (Chong Lab)
University of Pittsburgh | 219 Parkman Avenue, Pittsburgh, PA 15260
jml...@pitt.edu | [He, Him, His]

```
# load_binmapper.py
#
# Code to load in the BinMapper from a WESTPA HDF5 file.
#
# Modify the `if __name__ == '__main__'` block at the end to your liking.
# You can also pass in an argument to replace the iteration you want to look at.
# When n_iter=None, it implies that it'll look at the last possible iteration.
#
# Note that RecursiveBinMapper had a bug where the binlabels and bin_indices are mismatched
# for simulations created on versions >=2022.06.
#
# By Jeremy Leung
#
# Last Modified: Oct 31, 2024


from westpa.tools.binning import mapper_from_hdf5
import h5py
import sys


def load_mapper(h5_filename='west.h5', n_iter=None):
    '''
    Function to load in the bin mapper from the HDF5 File.

    '''
    with h5py.File(h5_filename) as h5file:
        if n_iter is None:
            n_iter = h5file.attrs['west_current_iteration']
            while True:
                # There are times where the last iteration might not exist.
                # This `while` loop will attempt to resolve that.
                try:
                    h5file[f'iterations/iter_{n_iter:>08}'].attrs['binhash']
                    break
                except KeyError:
                    n_iter -= 1

        print(f'looking at iteration {n_iter}')
        binhash = h5file[f'iterations/iter_{n_iter:>08}'].attrs['binhash']
        bingroup = h5file['bin_topologies']
        mapper, mapper_pickle, mapper_hash = mapper_from_hdf5(bingroup, binhash)

    return mapper, mapper_pickle, mapper_hash


if __name__ == '__main__':
    if len(sys.argv) > 1:  
        n_iter = sys.argv[1]
```

Schuyler Byrn

unread,
Feb 18, 2026, 4:22:44 PM (2 days ago) Feb 18
to westpa...@googlegroups.com
Hi Jeremy,

I think I see my error. I was trying to pass in an array where the first dimension was the number of segments and the second dimension was the pcoord for each frame of that segment. Then I had compressed the third dimension (the actual pcoord) into a tuple. Instead, it seems I just need to flatten the array so that the first dimension is all frames and second dimension is the pcoord. 

Also I got that statement about the function enforcing dtype=np.float32 from these lines in westpa/core/binning/assign.py in the source code on github:

    def assign(self, coords, mask=None, output=None):
        try:
            passed_coord_dtype = coords.dtype
        except AttributeError:
            coords = np.require(coords, dtype=coord_dtype)
        else:
            if passed_coord_dtype != coord_dtype:
                coords = np.require(coords, dtype=coord_dtype)

At the top of the script coord_dtype appears to be globally set to np.float32, but maybe I'm missing something somewhere else that overwrites it based on west.cfg.

Best,
Schuyler

--
You received this message because you are subscribed to the Google Groups "westpa-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to westpa-users...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/westpa-users/93abc16c-1d29-45f6-aecd-3b826603c373n%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages