Skip to content

Commit 9116f19

Browse files
fixed formatting
1 parent a3ca48c commit 9116f19

2 files changed

Lines changed: 62 additions & 48 deletions

File tree

imas/ids_slice.py

Lines changed: 57 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -100,20 +100,20 @@ def shape(self) -> Tuple[int, ...]:
100100
array, based on the hierarchy of slicing operations performed.
101101
102102
Raises:
103-
ValueError: The underlying data is ragged (non-rectangular). Use
104-
.is_ragged to check first, or use .values() to extract values
105-
as a flat list.
103+
ValueError: The underlying data is ragged (non-rectangular).
104+
Use .is_ragged to check first, or use .values() to extract
105+
values as a flat list.
106106
107107
Returns:
108108
Tuple of dimensions.
109109
"""
110110
if self.is_ragged:
111111
raise ValueError(
112-
f"Cannot get shape of ragged array: dimensions have varying sizes. "
113-
f"Use .is_ragged to check if data is ragged, or .values() to "
114-
f"get a flat list of elements."
112+
"Cannot get shape of ragged array: dimensions have varying "
113+
"sizes. Use .is_ragged to check if data is ragged, or .values() "
114+
"to get a flat list of elements."
115115
)
116-
116+
117117
# Build shape from hierarchy
118118
shape = []
119119
for i, hierarchy_level in enumerate(self._element_hierarchy):
@@ -129,7 +129,7 @@ def shape(self) -> Tuple[int, ...]:
129129
else:
130130
# This is a single count
131131
shape.append(hierarchy_level)
132-
132+
133133
return tuple(shape)
134134

135135
def __len__(self) -> int:
@@ -211,13 +211,15 @@ def _handle_list_slice(self, item: slice) -> "IDSSlice":
211211
IDSSlice with updated shape and hierarchy
212212
"""
213213
from imas.ids_struct_array import IDSStructArray
214-
214+
215215
slice_str = self._format_slice(item)
216216
# Full path: current path + slice operation
217217
full_path = self._path + slice_str
218218

219219
# Check if matched elements are IDSStructArray (nested arrays)
220-
if self._matched_elements and isinstance(self._matched_elements[0], IDSStructArray):
220+
if self._matched_elements and isinstance(
221+
self._matched_elements[0], IDSStructArray
222+
):
221223
# When slicing nested arrays, apply slice to each array and then flatten
222224
flattened_elements = []
223225
new_hierarchy_values = []
@@ -227,25 +229,28 @@ def _handle_list_slice(self, item: slice) -> "IDSSlice":
227229
# Flatten: add each element from the sliced array to flattened list
228230
for element in sliced_array:
229231
flattened_elements.append(element)
230-
232+
231233
# Build new hierarchy
232-
# The key is: if we have a multi-level grouped hierarchy (like [3, [2, 2, 2], ...]),
233-
# we're dealing with a nested structure that's already been flattened.
234-
# We should only update the innermost level, NOT create a new top-level grouping.
235-
234+
# The key is: if we have a multi-level grouped hierarchy
235+
# (like [3, [2, 2, 2], ...]), we're dealing with a nested
236+
# structure that's already been flattened. We should only update
237+
# the innermost level, NOT create a new top-level grouping.
238+
236239
num_groups = len(self._matched_elements)
237-
238-
if (len(self._element_hierarchy) >= 2 and
239-
isinstance(self._element_hierarchy[0], int) and
240-
isinstance(self._element_hierarchy[1], list)):
240+
241+
if (
242+
len(self._element_hierarchy) >= 2
243+
and isinstance(self._element_hierarchy[0], int)
244+
and isinstance(self._element_hierarchy[1], list)
245+
):
241246
# Multi-level hierarchy like [3, [2, 2, 2], ...]
242247
# The top level is the original grouping, so DON'T recreate it
243248
# Just replace the last (innermost) level
244249
new_hierarchy = self._element_hierarchy[:-1] + [new_hierarchy_values]
245250
else:
246251
# Single level or not grouped yet - create new grouping
247252
new_hierarchy = [num_groups, new_hierarchy_values]
248-
253+
249254
return IDSSlice(
250255
self.metadata,
251256
flattened_elements,
@@ -257,7 +262,7 @@ def _handle_list_slice(self, item: slice) -> "IDSSlice":
257262
else:
258263
# Normal slice on outer list
259264
sliced_elements = self._matched_elements[item]
260-
265+
261266
# Update shape to reflect the slice on first dimension
262267
new_virtual_shape = (len(sliced_elements),) + self._virtual_shape[1:]
263268
new_element_hierarchy = [len(sliced_elements)] + self._element_hierarchy[1:]
@@ -316,7 +321,9 @@ def __getattr__(self, name: str) -> "IDSSlice":
316321

317322
# Get attributes from all non-empty matched elements
318323
# Special case: if matched_elements are IDSStructArray, keep them grouped
319-
if self._matched_elements and isinstance(self._matched_elements[0], IDSStructArray):
324+
if self._matched_elements and isinstance(
325+
self._matched_elements[0], IDSStructArray
326+
):
320327
# For nested arrays, return the arrays themselves, not attributes from them
321328
# This allows chaining like .ion[:].element[:] to work
322329
child_elements = self._matched_elements
@@ -335,19 +342,21 @@ def __getattr__(self, name: str) -> "IDSSlice":
335342
element_hierarchy=self._element_hierarchy,
336343
)
337344

338-
# If matched_elements are IDSStructArray and we're accessing an attribute on them,
339-
# we need to get that attribute from each array's elements
345+
# If matched_elements are IDSStructArray and we're accessing an
346+
# attribute on them, we need to get that attribute from each
347+
# array's elements
340348
if isinstance(self._matched_elements[0], IDSStructArray):
341-
# Accessing attribute on nested arrays: need to get attr from each array's elements
349+
# Accessing attribute on nested arrays: get attr from each
350+
# array's elements
342351
flattened_elements = []
343352
for array in child_elements:
344-
# array is IDSStructArray, get the attribute from its elements
353+
# array is IDSStructArray, get attribute from its elements
345354
for element in array:
346355
flattened_elements.append(getattr(element, name))
347-
356+
348357
# Keep track of grouping for shape preservation
349358
child_sizes = [len(array) for array in child_elements]
350-
359+
351360
return IDSSlice(
352361
child_metadata,
353362
flattened_elements,
@@ -417,21 +426,24 @@ def __repr__(self) -> str:
417426
"""
418427
ids_name = self.metadata.ids_name
419428
item_word = "item" if len(self) == 1 else "items"
420-
return f"<{type(self).__name__} (IDS:{ids_name}, {self._path} with {len(self)} {item_word})>"
429+
return (
430+
f"<{type(self).__name__} (IDS:{ids_name}, {self._path} with "
431+
f"{len(self)} {item_word})>"
432+
)
421433

422434
def values(self) -> List[Any]:
423435
"""Extract raw values from elements in this slice.
424436
425437
For IDSPrimitive elements, this extracts the wrapped value.
426438
For other element types, returns them as-is.
427439
428-
Returns a flat list of extracted values. This is useful for getting
429-
the actual data without the IDS wrapper when accessing scalar fields
430-
through a slice, without requiring explicit looping through the
440+
Returns a flat list of extracted values. This is useful for getting
441+
the actual data without the IDS wrapper when accessing scalar fields
442+
through a slice, without requiring explicit looping through the
431443
original collection.
432444
433445
For multi-dimensional access to values:
434-
- Use direct indexing: ``ids_obj[i1].collection[i2].value`` for best
446+
- Use direct indexing: ``ids_obj[i1].collection[i2].value`` for best
435447
performance and clarity
436448
- Use ``.to_array()`` if you need numpy array integration
437449
@@ -491,12 +503,14 @@ def to_array(self) -> np.ndarray:
491503
Tensorize a 1D slice of numeric data::
492504
493505
# Works: leaf nodes are numeric arrays
494-
array = core_profiles.profiles_1d[:].te.to_array() # Shape: (n_profiles,)
506+
array = core_profiles.profiles_1d[:].te.to_array()
507+
# Shape: (n_profiles,)
495508
496509
Multi-dimensional tensorization::
497510
498511
# Works: accessing leaf nodes from nested structure
499-
array = core_profiles.profiles_1d[:].te.to_array() # Shape: (n_profiles,)
512+
array = core_profiles.profiles_1d[:].te.to_array()
513+
# Shape: (n_profiles,)
500514
501515
Direct indexing for non-leaf nodes::
502516
@@ -516,17 +530,17 @@ def to_array(self) -> np.ndarray:
516530
first = self._matched_elements[0]
517531
if isinstance(first, (IDSStructure, IDSStructArray)):
518532
raise ValueError(
519-
f"Cannot tensorize {type(first).__name__} slice - only works for "
520-
f"leaf nodes (scalars, numeric arrays). Use direct indexing instead: "
521-
f"ids[i][j] to access structures."
533+
f"Cannot tensorize {type(first).__name__} slice - only "
534+
f"works for leaf nodes (scalars, numeric arrays). Use "
535+
f"direct indexing instead: ids[i][j] to access structures."
522536
)
523537

524538
# Validate: data must be rectangular (not ragged)
525539
if self.is_ragged:
526540
raise ValueError(
527-
f"Cannot tensorize ragged array - dimensions have varying sizes. "
528-
f"Use .values() to get a flat list, or use direct indexing for "
529-
f"multi-dimensional access."
541+
"Cannot tensorize ragged array - dimensions have varying "
542+
"sizes. Use .values() to get a flat list, or use direct "
543+
"indexing for multi-dimensional access."
530544
)
531545

532546
# Get the target shape (we validated it's not ragged)
@@ -548,11 +562,11 @@ def to_array(self) -> np.ndarray:
548562

549563
# Tensorize to target shape
550564
arr = np.array(flat_values)
551-
565+
552566
# For 1D, no reshape needed
553567
if len(actual_shape) == 1:
554568
return arr
555-
569+
556570
# For multi-dimensional, reshape to target shape
557571
try:
558572
return arr.reshape(actual_shape)

imas/test/test_multidim_slicing.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,11 @@ def test_to_array_variable_size(self):
8585
cp.profiles_1d[2].grid.rho_tor_norm = np.array([0.0, 0.5, 1.0])
8686

8787
result = cp.profiles_1d[:].grid.rho_tor_norm
88-
88+
8989
# to_array() should raise ValueError for ragged data
9090
with pytest.raises(ValueError, match="Cannot tensorize ragged array"):
9191
result.to_array()
92-
92+
9393
# But .values() should still work
9494
values = result.values()
9595
assert len(values) == 3
@@ -162,12 +162,12 @@ def test_integer_index_not_supported(self):
162162
# Option 1: Direct indexing (recommended)
163163
ion_0_from_first_profile = cp.profiles_1d[0].ion[:1] # Use slice, not int index
164164
assert len(ion_0_from_first_profile) == 1
165-
165+
166166
# Option 2: Convert to list
167167
ions_list = list(cp.profiles_1d[:].ion)
168168
ions_from_first_profile = ions_list[0]
169169
assert len(ions_from_first_profile) == 2
170-
170+
171171
# Option 3: Extract values
172172
ions_values = cp.profiles_1d[:].ion.values()
173173
first_profile_ions = ions_values[0]
@@ -216,7 +216,7 @@ def test_negative_indexing_not_supported(self):
216216
# Get last ion from each profile using slice
217217
result = cp.profiles_1d[:].ion[2:3] # Get last element with slice
218218
assert result.shape == (5, 1)
219-
219+
220220
# Or better: direct indexing
221221
last_ions = [p.ion[-1] for p in cp.profiles_1d]
222222
assert len(last_ions) == 5

0 commit comments

Comments
 (0)