From 446933cd2e031de487a327a3e9e8ed13d71b98e7 Mon Sep 17 00:00:00 2001
From: Daniel van der Schuur <schuur@astron.nl>
Date: Tue, 1 Nov 2016 11:25:55 +0000
Subject: [PATCH] -Added generator output limiter (nof_blocks).

---
 applications/arts/doc/python/arts_sc1.py | 17 ++-----
 applications/arts/doc/python/stream.py   | 65 +++++++++++++-----------
 2 files changed, 40 insertions(+), 42 deletions(-)

diff --git a/applications/arts/doc/python/arts_sc1.py b/applications/arts/doc/python/arts_sc1.py
index 0ddc34a4b9..86df064dbb 100644
--- a/applications/arts/doc/python/arts_sc1.py
+++ b/applications/arts/doc/python/arts_sc1.py
@@ -42,7 +42,7 @@ N_POL = 2   # Number of polarizations
 N_BAND = 16 # Number of bands
 # Serial (time) dimensions
 N_INT_X = 800000 # Number of time samples per corrrelator intergration period
-N_SLOT = 1024 #FIXME we need global indices here, don't we?
+N_SLOT = 1024 # Number of beamlet slots on single BF FN
 W_BEAMLET = 6 # Complex beamlet data width
 
 nof_intervals = 0 # Unlimited runtime
@@ -57,25 +57,18 @@ data_width = N_POL*W_BEAMLET
 parallel_definition = (('dish', N_DISH), ('polarization', N_POL), ('band', N_BAND))
 serial_definition = (('interval', nof_intervals, T_INT_X),('timesample', N_INT_X), ('slot', N_SLOT))
 
-CB444 = StreamArray(parallel_definition, serial_definition, data_width, block_size=1024)
+CB444 = StreamArray(parallel_definition, serial_definition, data_width, block_size=1024, nof_blocks=1)
 
 # Print dish 0, pol 0, band (front node) 0:
 for i in CB444[0][0][0]:
-    print i['dish']
-    break
-for i in CB444[0][0][1]:
-    print i['dish']
-    break
+    print i['slot']
+
 
 ###############################################################################
 # Equation 2: transpose the band and dish (physical) dimensions of CB444: flip dimensions 0 and 2
 ###############################################################################
-CB444_T = CB444.transpose((2,1,0)) #NOTE transpose works. How to print this nicely?
+CB444_T = CB444.transpose((2,1,0))
 
 for i in CB444_T[0][0][0]:
     print i['dish']
-    break
-for i in CB444_T[0][0][1]:
-    print i['dish']
-    break
 
diff --git a/applications/arts/doc/python/stream.py b/applications/arts/doc/python/stream.py
index 6929ed5a81..e60a85d400 100644
--- a/applications/arts/doc/python/stream.py
+++ b/applications/arts/doc/python/stream.py
@@ -7,7 +7,7 @@ class Stream:
     """
     Single serial stream generator 
     """
-    def __init__(self, parallel_definition, serial_definition, data_width, block_size):
+    def __init__(self, parallel_definition, serial_definition, data_width, block_size, nof_blocks):
 
         # Parallel definition: physical stream tags and indices for this serial stream
         self.parallel_definition = parallel_definition
@@ -34,32 +34,39 @@ class Stream:
             self.serial_data_out.append(dimension-1)
         # Create an interval out counter. Initialize to -1 as there is no maximum
         self.interval_out = -1
+
         self.block_size = block_size
-        self.last_block_lo = 0
-        self.last_block_hi = block_size
+        self.nof_blocks = nof_blocks
+        self.out_count = 0
 
     def next(self):
-        block = []
-        for i in range(self.block_size):
-            # Start with the fastest changing dimension (index -1). When we have e.g. 2 dimensions, don't go beyond index -2.
-            for dimension_index in range(-1, -(self.nof_serial_dimensions+1), -1):
-                if self.serial_data_out[dimension_index]==self.serial_dimensions[dimension_index]-1:
-                    # Max of this dimension reached; reset to 0
-                    self.serial_data_out[dimension_index]=0
-                    # If this is the highest dimension, this is the last value of this interval.
-                    if dimension_index==-(self.nof_serial_dimensions):
-                        self.interval_out+=1
-                else:
-                    # Max not reached; increment index
-                    self.serial_data_out[dimension_index]+=1
-                    break
-            block.append(tuple(self.parallel_indices)+tuple([self.interval_out]+(self.serial_data_out)))
-
-        # Zip the tags with datatype 'int' (fixed for now) to pass to np.array. This makes array dimensions viewable
-        # by name, which is essential.
-        np_dtype = zip(self.parallel_tags+self.serial_tags, (len(self.parallel_tags)+self.nof_serial_dimensions+1)*['int']) # Do add the interval dimension here
-        np_block = np.array(block, np_dtype)
-        return np_block
+        # Break out of the generator loop when we've output nof_blocks
+        if self.nof_blocks>0 and self.out_count==self.nof_blocks:
+            self.out_count = 0
+            raise StopIteration
+        else:
+            self.out_count+=1
+            block = []
+            for i in range(self.block_size):
+                # Start with the fastest changing dimension (index -1). When we have e.g. 2 dimensions, don't go beyond index -2.
+                for dimension_index in range(-1, -(self.nof_serial_dimensions+1), -1):
+                    if self.serial_data_out[dimension_index]==self.serial_dimensions[dimension_index]-1:
+                        # Max of this dimension reached; reset to 0
+                        self.serial_data_out[dimension_index]=0
+                        # If this is the highest dimension, this is the last value of this interval.
+                        if dimension_index==-(self.nof_serial_dimensions):
+                            self.interval_out+=1
+                    else:
+                        # Max not reached; increment index
+                        self.serial_data_out[dimension_index]+=1
+                        break
+                block.append(tuple(self.parallel_indices)+tuple([self.interval_out]+(self.serial_data_out)))
+    
+            # Zip the tags with datatype 'int' (fixed for now) to pass to np.array. This makes array dimensions viewable
+            # by name, which is essential.
+            np_dtype = zip(self.parallel_tags+self.serial_tags, (len(self.parallel_tags)+self.nof_serial_dimensions+1)*['int']) # Do add the interval dimension here
+            np_block = np.array(block, np_dtype)
+            return np_block
 
     def __iter__(self):
         return self
@@ -67,12 +74,10 @@ class Stream:
 
 class StreamArray(np.ndarray):
     """
-    NOTE: Use get() functions instead of fixed attributes as indexing a subset of StreamArray should yield e.g.
-          a different data rate than the full array.
-    NOTE: An ESSENTIAL property of subclassing numpy array is that method return values *DEPEND ON THE INDEXED SUBSTREAMS*!
-          . e.g. pass substream to Component without the difficulties of component_class.py!
+    User can limit the generated output using nof_intervals (highest dimension = still large amount of output) 
+    or nof_blocks (lowest dimension = small amount of output). 0=unlimited.
     """
-    def __new__(cls, parallel_definition, serial_definition, data_width, block_size):
+    def __new__(cls, parallel_definition, serial_definition, data_width, block_size, nof_blocks):
         # We need the parallel dimensions here, but we'll pass the parallel tags to the serial Stream instances.
         parallel_tags       = [pair[0] for pair in parallel_definition] 
         parallel_dimensions = [pair[1] for pair in parallel_definition] 
@@ -84,7 +89,7 @@ class StreamArray(np.ndarray):
         for index in np.ndindex(tuple(parallel_dimensions)):
             parallel_definition = zip(parallel_tags, index)
             # Replace the dimension size in the parallel_definition with the actual stream index
-            streams.append(Stream(parallel_definition, serial_definition, data_width, block_size))
+            streams.append(Stream(parallel_definition, serial_definition, data_width, block_size, nof_blocks))
         input_array = np.array(streams)
 
         input_array.resize(parallel_dimensions)
-- 
GitLab