From 2a271b1edab6540e0c79d9a3a73655f0ee133e3d Mon Sep 17 00:00:00 2001 From: drodarie Date: Thu, 3 Oct 2024 17:05:24 +0200 Subject: [PATCH 1/7] fix: origin parameter for cylinder targetting is now a numpy array. --- bsb/simulation/targetting.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bsb/simulation/targetting.py b/bsb/simulation/targetting.py index 84148ae30..daf554e84 100755 --- a/bsb/simulation/targetting.py +++ b/bsb/simulation/targetting.py @@ -177,7 +177,7 @@ class CylindricalTargetting( Targets all cells in a cylinder along specified axis. """ - origin: list[float] = config.attr(type=types.list(type=float, size=2)) + origin: np.ndarray[float] = config.attr(type=types.ndarray(dtype=float)) axis: typing.Union[typing.Literal["x"], typing.Literal["y"], typing.Literal["z"]] = ( config.attr(type=types.in_(["x", "y", "z"]), default="y") ) @@ -198,7 +198,7 @@ def get_targets(self, adapter, simulation, simdata): model: simdata.populations[model][ np.sum( simdata.placement[model].load_positions()[:, axes] - self.origin**2, - axis=0, + axis=1, ) < self.radius**2 ] From cabeb50d3b173ff747854ae07f59f8fe1440c043 Mon Sep 17 00:00:00 2001 From: drodarie Date: Thu, 3 Oct 2024 17:31:36 +0200 Subject: [PATCH 2/7] feat: add shape attribute to types.ndarray Add documentation for cylinder targetting. fix minor typos in documentations --- bsb/config/types.py | 10 ++++-- .../geometric/geometric_shapes.py | 32 +++++++++---------- bsb/core.py | 2 +- bsb/services/pool.py | 2 +- bsb/simulation/targetting.py | 5 ++- 5 files changed, 29 insertions(+), 22 deletions(-) diff --git a/bsb/config/types.py b/bsb/config/types.py index ceea9315c..29d0137bf 100644 --- a/bsb/config/types.py +++ b/bsb/config/types.py @@ -789,13 +789,17 @@ class ndarray(TypeHandler): :rtype: Callable """ - def __init__(self, dtype=None): + def __init__(self, shape=None, dtype=None): + self.shape = shape self.dtype = dtype def __call__(self, value): + result = np.array(value, copy=False) if self.dtype is not None: - return np.array(value, copy=False, dtype=self.dtype) - return np.array(value, copy=False) + result = np.asarray(result, dtype=self.dtype) + if self.shape is not None: + result = result.reshape(self.shape) + return result @property def __name__(self): diff --git a/bsb/connectivity/geometric/geometric_shapes.py b/bsb/connectivity/geometric/geometric_shapes.py index 387596d2f..3c8bbf18f 100644 --- a/bsb/connectivity/geometric/geometric_shapes.py +++ b/bsb/connectivity/geometric/geometric_shapes.py @@ -577,15 +577,15 @@ class Ellipsoid(GeometricShape, classmap_entry="ellipsoid"): """ origin = config.attr( - type=types.ndarray(dtype=float), required=True, hint=[0.0, 0.0, 0.0] + type=types.ndarray(shape=(3,), dtype=float), required=True, hint=[0.0, 0.0, 0.0] ) """The coordinates of the center of the ellipsoid.""" lambdas = config.attr( - type=types.ndarray(dtype=float), required=True, hint=[1.0, 0.5, 2.0] + type=types.ndarray(shape=(3,), dtype=float), required=True, hint=[1.0, 0.5, 2.0] ) """The length of the three semi-axes.""" - @config.property(type=types.ndarray(), required=True) + @config.property(type=types.ndarray(shape=(3,)), required=True) def v0(self): """The versor on which the first semi-axis lies.""" return self._v0 @@ -594,7 +594,7 @@ def v0(self): def v0(self, value): self._v0 = np.copy(value) / np.linalg.norm(value) - @config.property(type=types.ndarray(), required=True) + @config.property(type=types.ndarray(shape=(3,)), required=True) def v1(self): """The versor on which the second semi-axis lies.""" return self._v1 @@ -603,7 +603,7 @@ def v1(self): def v1(self, value): self._v1 = np.copy(value) / np.linalg.norm(value) - @config.property(type=types.ndarray(), required=True) + @config.property(type=types.ndarray(shape=(3,)), required=True) def v2(self): """The versor on which the third semi-axis lies.""" return self._v2 @@ -700,11 +700,11 @@ class Cone(GeometricShape, classmap_entry="cone"): """ apex = config.attr( - type=types.ndarray(dtype=float), required=True, hint=[0.0, 1.0, 0.0] + type=types.ndarray(shape=(3,), dtype=float), required=True, hint=[0.0, 1.0, 0.0] ) """The coordinates of the apex of the cone.""" origin = config.attr( - type=types.ndarray(dtype=float), required=True, hint=[0.0, 0.0, 0.0] + type=types.ndarray(shape=(3,), dtype=float), required=True, hint=[0.0, 0.0, 0.0] ) """The coordinates of the center of the cone's base.""" radius = config.attr(type=float, required=False, default=1.0) @@ -824,11 +824,11 @@ class Cylinder(GeometricShape, classmap_entry="cylinder"): """ origin = config.attr( - type=types.ndarray(dtype=float), required=True, hint=[0.0, 0.0, 0.0] + type=types.ndarray(shape=(3,), dtype=float), required=True, hint=[0.0, 0.0, 0.0] ) """The coordinates of the center of the bottom circle of the cylinder.""" top_center = config.attr( - type=types.ndarray(dtype=float), required=True, hint=[0.0, 2.0, 0.0] + type=types.ndarray(shape=(3,), dtype=float), required=True, hint=[0.0, 2.0, 0.0] ) """The coordinates of the center of the top circle of the cylinder.""" radius = config.attr(type=float, required=False, default=1.0) @@ -936,7 +936,7 @@ class Sphere(GeometricShape, classmap_entry="sphere"): """ origin = config.attr( - type=types.ndarray(dtype=float), required=True, hint=[0.0, 0.0, 0.0] + type=types.ndarray(shape=(3,), dtype=float), required=True, hint=[0.0, 0.0, 0.0] ) """The coordinates of the center of the sphere.""" radius = config.attr(type=float, required=False, default=1.0) @@ -1008,11 +1008,11 @@ class Cuboid(GeometricShape, classmap_entry="cuboid"): """ origin = config.attr( - type=types.ndarray(dtype=float), required=True, hint=[0.0, 0.0, 0.0] + type=types.ndarray(shape=(3,), dtype=float), required=True, hint=[0.0, 0.0, 0.0] ) """The coordinates of the center of the barycenter of the bottom rectangle.""" top_center = config.attr( - type=types.ndarray(dtype=float), required=True, hint=[0.0, 1.0, 0.0] + type=types.ndarray(shape=(3,), dtype=float), required=True, hint=[0.0, 1.0, 0.0] ) """The coordinates of the center of the barycenter of the top rectangle.""" side_length_1 = config.attr(type=float, required=False, default=1.0) @@ -1159,21 +1159,21 @@ class Parallelepiped(GeometricShape, classmap_entry="parallelepiped"): """ origin = config.attr( - type=types.ndarray(dtype=float), required=True, hint=[0.0, 0.0, 0.0] + type=types.ndarray(shape=(3,), dtype=float), required=True, hint=[0.0, 0.0, 0.0] ) """The coordinates of the left-bottom edge.""" side_vector_1 = config.attr( - type=types.ndarray(dtype=float), required=True, hint=[1.0, 0.0, 0.0] + type=types.ndarray(shape=(3,), dtype=float), required=True, hint=[1.0, 0.0, 0.0] ) """The first vector identifying the parallelepiped (using the right-hand orientation: the thumb).""" side_vector_2 = config.attr( - type=types.ndarray(dtype=float), required=True, hint=[0.0, 1.0, 0.0] + type=types.ndarray(shape=(3,), dtype=float), required=True, hint=[0.0, 1.0, 0.0] ) """The second vector identifying the parallelepiped (using the right-hand orientation: the index).""" side_vector_3 = config.attr( - type=types.ndarray(dtype=float), required=True, hint=[0.0, 0.0, 1.0] + type=types.ndarray(shape=(3,), dtype=float), required=True, hint=[0.0, 0.0, 1.0] ) """The third vector identifying the parallelepiped (using the right-hand orientation: the middle finger).""" diff --git a/bsb/core.py b/bsb/core.py index 902308068..891f62ae4 100644 --- a/bsb/core.py +++ b/bsb/core.py @@ -464,7 +464,7 @@ def place_cells( chunk=None, ): """ - Place cells inside of the scaffold + Place cells inside the scaffold .. code-block:: python diff --git a/bsb/services/pool.py b/bsb/services/pool.py index 0df898541..dafb5b363 100644 --- a/bsb/services/pool.py +++ b/bsb/services/pool.py @@ -709,7 +709,7 @@ def execute(self, return_results=False): """ Execute the jobs in the queue - In serial execution this runs all of the jobs in the queue in First In First Out + In serial execution this runs all the jobs in the queue in First In First Out order. In parallel execution this enqueues all jobs into the MPIPool unless they have dependencies that need to complete first. """ diff --git a/bsb/simulation/targetting.py b/bsb/simulation/targetting.py index daf554e84..53ef0f13a 100755 --- a/bsb/simulation/targetting.py +++ b/bsb/simulation/targetting.py @@ -177,11 +177,14 @@ class CylindricalTargetting( Targets all cells in a cylinder along specified axis. """ - origin: np.ndarray[float] = config.attr(type=types.ndarray(dtype=float)) + origin: np.ndarray[float] = config.attr(type=types.ndarray(shape=(2,), dtype=float)) + """Coordinates of the base of the cylinder for each non main axis""" axis: typing.Union[typing.Literal["x"], typing.Literal["y"], typing.Literal["z"]] = ( config.attr(type=types.in_(["x", "y", "z"]), default="y") ) + """Main axis of the cylinder""" radius: float = config.attr(type=float, required=True) + """Radius of the cylinder""" @FractionFilter.filter def get_targets(self, adapter, simulation, simdata): From 21663182b058c42048f91ab9d93712967c264b0c Mon Sep 17 00:00:00 2001 From: drodarie Date: Fri, 4 Oct 2024 15:51:50 +0200 Subject: [PATCH 3/7] fix: add test for ndarray shape parameter --- bsb/config/types.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/bsb/config/types.py b/bsb/config/types.py index 29d0137bf..e8bed81ad 100644 --- a/bsb/config/types.py +++ b/bsb/config/types.py @@ -789,7 +789,10 @@ class ndarray(TypeHandler): :rtype: Callable """ - def __init__(self, shape=None, dtype=None): + def __init__(self, shape: tuple[int] = None, dtype=None): + for dim in shape: + if dim < 0: + raise TypeError(f"Ndarray shape must all be positive. Provided {shape}.") self.shape = shape self.dtype = dtype @@ -798,7 +801,12 @@ def __call__(self, value): if self.dtype is not None: result = np.asarray(result, dtype=self.dtype) if self.shape is not None: - result = result.reshape(self.shape) + try: + result = result.reshape(self.shape) + except Exception: + raise TypeError( + "Couldn't cast {} into an array of shape {}".format(value, self.shape) + ) return result @property From da1637f25ce8b7ca3f021de2e51aaef5f0831b10 Mon Sep 17 00:00:00 2001 From: drodarie Date: Fri, 4 Oct 2024 16:00:04 +0200 Subject: [PATCH 4/7] fix: case when shape is None. --- bsb/config/types.py | 9 ++++++--- bsb/core.py | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/bsb/config/types.py b/bsb/config/types.py index e8bed81ad..218e2a8ec 100644 --- a/bsb/config/types.py +++ b/bsb/config/types.py @@ -790,9 +790,12 @@ class ndarray(TypeHandler): """ def __init__(self, shape: tuple[int] = None, dtype=None): - for dim in shape: - if dim < 0: - raise TypeError(f"Ndarray shape must all be positive. Provided {shape}.") + if shape is not None: + for dim in shape: + if dim < 0: + raise TypeError( + f"Ndarray shape must all be positive. Provided {shape}." + ) self.shape = shape self.dtype = dtype diff --git a/bsb/core.py b/bsb/core.py index 891f62ae4..c1ddb5f94 100644 --- a/bsb/core.py +++ b/bsb/core.py @@ -767,7 +767,8 @@ def create_job_pool(self, fail_fast=None, quiet=False): try: # Check whether stdout is a TTY, and that it is larger than 0x0 # (e.g. MPI sets it to 0x0 unless an xterm is emulated. - tty = os.isatty(sys.stdout.fileno()) and sum(os.get_terminal_size()) + # tty = os.isatty(sys.stdout.fileno()) and sum(os.get_terminal_size()) + tty = False except Exception: tty = False if tty: From 24c25dd455dab7e0d59009bec1e8ba2d448d9979 Mon Sep 17 00:00:00 2001 From: drodarie Date: Fri, 4 Oct 2024 16:02:49 +0200 Subject: [PATCH 5/7] revert: core.py commited by mistake --- bsb/core.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bsb/core.py b/bsb/core.py index c1ddb5f94..891f62ae4 100644 --- a/bsb/core.py +++ b/bsb/core.py @@ -767,8 +767,7 @@ def create_job_pool(self, fail_fast=None, quiet=False): try: # Check whether stdout is a TTY, and that it is larger than 0x0 # (e.g. MPI sets it to 0x0 unless an xterm is emulated. - # tty = os.isatty(sys.stdout.fileno()) and sum(os.get_terminal_size()) - tty = False + tty = os.isatty(sys.stdout.fileno()) and sum(os.get_terminal_size()) except Exception: tty = False if tty: From 64089db54425f0829ace7468509103efefeffe4f Mon Sep 17 00:00:00 2001 From: drodarie Date: Fri, 4 Oct 2024 16:30:54 +0200 Subject: [PATCH 6/7] docs: fix typing --- bsb/config/types.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bsb/config/types.py b/bsb/config/types.py index 218e2a8ec..1f1b57438 100644 --- a/bsb/config/types.py +++ b/bsb/config/types.py @@ -789,7 +789,11 @@ class ndarray(TypeHandler): :rtype: Callable """ - def __init__(self, shape: tuple[int] = None, dtype=None): + def __init__(self, shape: builtins.tuple[builtins.int, ...] = None, dtype=None): + """ + :param shape: shape of the array, optional. + :param dtype: data-type, optional + """ if shape is not None: for dim in shape: if dim < 0: From 3c69c10ae262cf7cb6db1b4e1658809ba8846cef Mon Sep 17 00:00:00 2001 From: drodarie Date: Sat, 5 Oct 2024 14:53:38 +0200 Subject: [PATCH 7/7] fix: implement feedback from review. --- bsb/config/types.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/bsb/config/types.py b/bsb/config/types.py index 1f1b57438..ae183d482 100644 --- a/bsb/config/types.py +++ b/bsb/config/types.py @@ -794,12 +794,8 @@ def __init__(self, shape: builtins.tuple[builtins.int, ...] = None, dtype=None): :param shape: shape of the array, optional. :param dtype: data-type, optional """ - if shape is not None: - for dim in shape: - if dim < 0: - raise TypeError( - f"Ndarray shape must all be positive. Provided {shape}." - ) + if any(dim < 0 for dim in (shape or ())): + raise TypeError(f"Ndarray shape must all be positive. Provided {shape}.") self.shape = shape self.dtype = dtype @@ -812,7 +808,7 @@ def __call__(self, value): result = result.reshape(self.shape) except Exception: raise TypeError( - "Couldn't cast {} into an array of shape {}".format(value, self.shape) + f"Couldn't cast array of {getattr(value, 'shape', 'unknown')} shape into an array of {self.shape} shape." ) return result