Skip to content

Commit

Permalink
Arrays of primitives (#21)
Browse files Browse the repository at this point in the history
* arrays of primitives are now dtype array

* arrays of primitives are now dtype array

* arrays of primitives are now dtype array

* method for arrayNP to get numpy string of root datatype

* added tests

* add tests

---------

Co-authored-by: Peter Braun <[email protected]>
  • Loading branch information
Bilchreis and Peter Braun authored Nov 27, 2024
1 parent e76cc2a commit 1040618
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 17 deletions.
2 changes: 1 addition & 1 deletion frappy
6 changes: 3 additions & 3 deletions src/secop_ophyd/SECoPSignal.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,9 @@ def __init__(self, path: Path, secclient: AsyncFrappyClient) -> None:

if self.SECoP_type_info.max_depth > MAX_DEPTH:
warnings.warn(
f"The datatype of parameter '{path._accessible_name}' has a maximum"
f"depth of {self.SECoP_type_info.max_depth}. Tiled & Databroker only"
f"support a Depth upto {MAX_DEPTH}"
f"The datatype of parameter '{path._accessible_name}' has a maximum "
f"depth of {self.SECoP_type_info.max_depth}. Tiled & Databroker only "
f"support a Depth upto {MAX_DEPTH} "
f"dtype_descr: {self.SECoP_type_info.dtype_descr}"
)

Expand Down
34 changes: 21 additions & 13 deletions src/secop_ophyd/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ def __init__(
self.array_element = array_element

def make_numpy_dtype(self) -> tuple:
return (self.name, bool)
return (self.name, "<b1")

def make_concrete_numpy_dtype(self, value) -> tuple:
return self.make_numpy_dtype()
Expand All @@ -251,7 +251,7 @@ def __init__(
self.array_element = array_element

def make_numpy_dtype(self) -> tuple:
return (self.name, int)
return (self.name, "<i8")

def make_concrete_numpy_dtype(self, value) -> tuple:
return self.make_numpy_dtype()
Expand All @@ -272,7 +272,7 @@ def __init__(
self.array_element = array_element

def make_numpy_dtype(self) -> tuple:
return (self.name, float)
return (self.name, "<f8")

def make_concrete_numpy_dtype(self, value) -> tuple:
return self.make_numpy_dtype()
Expand All @@ -293,7 +293,7 @@ def __init__(
self.array_element = array_element

def make_numpy_dtype(self) -> tuple:
return (self.name, int)
return (self.name, "<i8")

def make_concrete_numpy_dtype(self, value) -> tuple:
return self.make_numpy_dtype()
Expand All @@ -314,7 +314,7 @@ def __init__(
self.array_element = array_element

def make_numpy_dtype(self) -> tuple:
return (self.name, int)
return (self.name, "<i8")

def make_concrete_numpy_dtype(self, value) -> tuple:
return self.make_numpy_dtype()
Expand Down Expand Up @@ -345,10 +345,10 @@ def __init__(
self.strlen = string_dt.maxchars

def make_numpy_dtype(self) -> tuple:
return (self.name, "U" + str(self.strlen))
return (self.name, "<U" + str(self.strlen))

def make_concrete_numpy_dtype(self, value) -> tuple:
return (self.name, "U" + str(self.strlen))
return (self.name, "<U" + str(self.strlen))

def make_numpy_compatible_list(self, value: str):
return value
Expand Down Expand Up @@ -479,7 +479,7 @@ def __init__(
pass
# raise NestedRaggedArray(
# "ragged arrays with more than a single dimension are not supported"
# )
# )int

else:
self.root_type = self.members
Expand All @@ -490,6 +490,10 @@ def __init__(
"are not supported"
)

def get_root_np_str(self) -> str:
dtype_list = self.root_type.make_numpy_dtype()
return dtype_list[1]

def make_numpy_dtype(self) -> tuple:
if self.shape == []:
return self.members.make_numpy_dtype()
Expand Down Expand Up @@ -557,13 +561,15 @@ def __init__(self, datatype: DataType) -> None:
self.dtype_tree: DtypeNP

self._is_composite: bool = False
self._is_array: bool = False

self.dtype_tree = dt_factory(datatype)

self.max_depth: int = self.dtype_tree.max_depth

if isinstance(self.dtype_tree, ArrayNP):
self.shape = self.dtype_tree.shape
self._is_array = True
self._is_composite = (
True
if isinstance(self.dtype_tree.root_type, (StructNP, TupleNP))
Expand All @@ -590,12 +596,10 @@ def __init__(self, datatype: DataType) -> None:

# Scalar atomic Datatypes and arrays of atomic dataypes
else:
self._is_composite = False

if isinstance(self.dtype_tree, ArrayNP):
if self._is_array:
# root secop datatype that is contained in the array
root_secop_dt = self.dtype_tree.root_type.secop_dtype
self.dtype = SECOP2DTYPE[root_secop_dt.__class__]
self.dtype = "array"

else:
self.dtype = SECOP2DTYPE[datatype.__class__]

Expand All @@ -604,8 +608,12 @@ def get_datakey(self):
# Composite Datatypes & Arrays of COmposite Datatypes
if self._is_composite:
describe_dict["dtype_str"] = self.dtype_str
# describe_dict["dtype_numpy"] = self.dtype_descr
describe_dict["dtype_descr"] = self.dtype_descr

if isinstance(self.dtype_tree, ArrayNP):
describe_dict["dtype_numpy"] = self.dtype_tree.get_root_np_str()

describe_dict["dtype"] = self.dtype
describe_dict["shape"] = self.shape
describe_dict["SECOP_datainfo"] = self.secop_dtype_str
Expand Down
75 changes: 75 additions & 0 deletions tests/test_primitive_arrays.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import pytest
from bluesky import RunEngine
from bluesky.callbacks.best_effort import BestEffortCallback
from bluesky.plans import count
from bluesky.utils import ProgressBarManager
from ophyd_async.core import SignalR

from secop_ophyd.SECoPDevices import SECoPNodeDevice, SECoPReadableDevice


async def test_primitive_arrays(
nested_struct_sim, run_engine: RunEngine, nested_node_re: SECoPNodeDevice
):
bec = BestEffortCallback()
run_engine.subscribe(bec)
run_engine.waiting_hook = ProgressBarManager()
run_engine.ignore_callback_exceptions = False

prim_arr: SECoPReadableDevice = nested_node_re.primitive_arrays

prim_arr.add_readables(
[
prim_arr.arr_int,
prim_arr.arr_float,
prim_arr.arr_arr_float,
prim_arr.arr_bool,
prim_arr.arr_String,
prim_arr.arr_String_nomax,
]
)

run_engine(count([prim_arr], 1, 3))

nested_node_re.disconnect()


@pytest.mark.parametrize(
"sig,shape_expected,dtype_numpy_expected",
[
pytest.param("arr_int", [6], "<i8", id="integer array"),
pytest.param("arr_float", [2], "<f8", id="float array"),
pytest.param("arr_arr_float", [2, 2], "<f8", id="2d float array"),
pytest.param("arr_bool", [3], "<b1", id="boolean array"),
pytest.param("arr_String", [3], "<U20", id="string array"),
pytest.param("arr_String_nomax", [3], "<U100", id="string array no maxlen"),
],
)
async def test_primitive_float_array(
nested_struct_sim,
run_engine: RunEngine,
nested_node_re: SECoPNodeDevice,
shape_expected,
sig,
dtype_numpy_expected,
):
bec = BestEffortCallback()
run_engine.subscribe(bec)
run_engine.waiting_hook = ProgressBarManager()
run_engine.ignore_callback_exceptions = False

prim_arr: SECoPReadableDevice = nested_node_re.primitive_arrays

arr_signal: SignalR = getattr(prim_arr, sig)

descr = await arr_signal.describe()

decr_dict = descr[arr_signal.name]

dtype_numpy = decr_dict["dtype_numpy"]
shape = decr_dict["shape"]

assert shape == shape_expected
assert dtype_numpy == dtype_numpy_expected

nested_node_re.disconnect()

0 comments on commit 1040618

Please sign in to comment.