Skip to content

Cannot use function callbacks when loading octrees #5337

@cphyc

Description

@cphyc

load_octree happily accepts data that contains values that are functions; however, it never calls the function but tries (and fails) to operate on the function itself, rather than on the value it returns.

Expected behaviour: It should be possible to pass functions in the data dictionary.


Code for reproduction

import yt

import yt
import numpy as np
oct_mask = np.zeros(33) # 5 refined values gives 7 * 4 + 5 octs to mask
oct_mask[[0,  5,  7, 16]] = 8
octree_mask = np.array(oct_mask, dtype=np.uint8)
quantities = {}
quantities["gas", "density"] = lambda: np.random.random((29, 1))
bbox = np.array([[-10.0, 10.0], [-10.0, 10.0], [-10.0, 10.0]])

ds = yt.load_octree(
    octree_mask=octree_mask,
    data=quantities,
    bbox=bbox,
    num_zones=1,
    partial_coverage=0,
)

ds.r["gas", "density"]

Traceback:

File ~/Documents/prog/yt/yt/data_objects/region_expression.py:29, in RegionExpression.__getitem__(self, item)
     24 def __getitem__(self, item):
     25     # At first, we will only implement this as accepting a slice that is
     26     # (optionally) unitful corresponding to a specific set of coordinates
     27     # that result in a rectangular prism or a slice.
     28     try:
---> 29         return self.all_data[item]
     30     except (YTFieldNotParseable, YTFieldNotFound):
     31         # any error raised by self.ds._get_field_info
     32         # signals a type error (not a field), however we don't want to
     33         # catch plain TypeErrors as this may create subtle bugs very hard
     34         # to decipher, like broken internal function calls.
     35         pass

File ~/Documents/prog/yt/yt/data_objects/data_containers.py:235, in YTDataContainer.__getitem__(self, key)
    233         return self.field_data[f]
    234     else:
--> 235         self.get_data(f)
    236 # fi.units is the unit expression string. We depend on the registry
    237 # hanging off the dataset to define this unit object.
    238 # Note that this is less succinct so that we can account for the case
    239 # when there are, for example, no elements in the object.
    240 try:

File ~/Documents/prog/yt/yt/data_objects/selection_objects/data_selection_objects.py:204, in YTSelectionContainer.get_data(self, fields)
    200         fluids.append(field_key)
    201 # The _read method will figure out which fields it needs to get from
    202 # disk, and return a dict of those fields along with the fields that
    203 # need to be generated.
--> 204 read_fluids, gen_fluids = self.index._read_fluid_fields(
    205     fluids, self, self._current_chunk
    206 )
    207 for f, v in read_fluids.items():
    208     self.field_data[f] = self.ds.arr(v, units=finfos[f].units)

File ~/Documents/prog/yt/yt/geometry/geometry_handler.py:240, in Index._read_fluid_fields(self, fields, dobj, chunk)
    238 else:
    239     chunk_size = chunk.data_size
--> 240 fields_to_return = self.io._read_fluid_selection(
    241     self._chunk_io(dobj), selector, fields_to_read, chunk_size
    242 )
    243 return fields_to_return, fields_to_generate

File ~/Documents/prog/yt/yt/frontends/stream/io.py:261, in IOHandlerStreamOctree._read_fluid_selection(self, chunks, selector, fields, size)
    257         for field in fields:
    258             field_vals[field] = self.fields[
    259                 subset.domain_id - subset._domain_offset
    260             ][field]
--> 261         subset.fill(field_vals, rv, selector, ind)
    262 return rv

File ~/Documents/prog/yt/yt/frontends/stream/data_structures.py:849, in StreamOctreeSubset.fill(self, content, dest, selector, offset)
    847 def fill(self, content, dest, selector, offset):
    848     if self._num_ghost_zones == 0:
--> 849         return self._fill_no_ghostzones(content, dest, selector, offset)
    850     else:
    851         return self._fill_with_ghostzones(content, dest, selector, offset)

File ~/Documents/prog/yt/yt/frontends/stream/data_structures.py:817, in StreamOctreeSubset._fill_no_ghostzones(self, content, dest, selector, offset)
    815 dest.update((field, np.empty(cell_count, dtype="float64")) for field in content)
    816 # Make references ...
--> 817 count = oct_handler.fill_level(
    818     0, levels, cell_inds, file_inds, dest, content, offset
    819 )
    820 return count

File yt/geometry/oct_container.pyx:742, in yt.geometry.oct_container.OctreeContainer.fill_level()

File yt/geometry/oct_container.pyx:761, in yt.geometry.oct_container.OctreeContainer.fill_level()

File <stringsource>:664, in View.MemoryView.memoryview_cwrapper()

File <stringsource>:352, in View.MemoryView.memoryview.__cinit__()

TypeError: a bytes-like object is required, not 'function'

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