ChessAnalysisPipeline 0.0.12__py3-none-any.whl → 0.0.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ChessAnalysisPipeline might be problematic. Click here for more details.

CHAP/common/processor.py CHANGED
@@ -8,19 +8,84 @@ Description: Module for Processors used in multiple experiment-specific
8
8
  workflows.
9
9
  """
10
10
 
11
- # system modules
12
- from json import dumps
13
- from time import time
11
+ # Third party modules
12
+ import numpy as np
14
13
 
15
- # local modules
14
+ # Local modules
16
15
  from CHAP import Processor
17
16
 
18
17
 
18
+ class AnimationProcessor(Processor):
19
+ """A Processor to show and return an animation.
20
+ """
21
+ def process(
22
+ self, data, num_frames, axis=0, interval=1000, blit=True,
23
+ repeat=True, repeat_delay=1000):
24
+ """Show and return an animation of image slices from a dataset
25
+ contained in `data`.
26
+
27
+ :param data: Input data.
28
+ :type data: CHAP.pipeline.PipelineData
29
+ :param num_frames: Number of frames for the animation.
30
+ :type num_frames: int
31
+ :param axis: Axis direction of the image slices,
32
+ defaults to `0`
33
+ :type axis: int, optional
34
+ :param interval: Delay between frames in milliseconds,
35
+ defaults to `1000`
36
+ :type interval: int, optional
37
+ :param blit: Whether blitting is used to optimize drawing,
38
+ default to `True`
39
+ :type blit: bool, optional
40
+ :param repeat: Whether the animation repeats when the sequence
41
+ of frames is completed, defaults to `True`
42
+ :type repeat: bool, optional
43
+ :param repeat_delay: Delay in milliseconds between consecutive
44
+ animation runs if repeat is `True`, defaults to `1000`
45
+ :type repeat_delay: int, optional
46
+ :return: The matplotlib animation.
47
+ :rtype: matplotlib.animation.ArtistAnimation
48
+ """
49
+ # Third party modules
50
+ import matplotlib.animation as animation
51
+ import matplotlib.pyplot as plt
52
+
53
+ # Get the frames
54
+ data = self.unwrap_pipelinedata(data)[-1]
55
+ delta = int(data.shape[axis]/(num_frames+1))
56
+ indices = np.linspace(delta, data.shape[axis]-delta, num_frames)
57
+ if data.ndim == 3:
58
+ if not axis:
59
+ frames = [data[int(index)] for index in indices]
60
+ elif axis == 1:
61
+ frames = [data[:,int(index),:] for index in indices]
62
+ elif axis == 2:
63
+ frames = [data[:,:,int(index)] for index in indices]
64
+ else:
65
+ raise ValueError('Invalid data dimension (must be 2D or 3D)')
66
+
67
+ fig = plt.figure()
68
+ # vmin = np.min(frames)/8
69
+ # vmax = np.max(frames)/8
70
+ ims = [[plt.imshow(
71
+ #frames[n], vmin=vmin,vmax=vmax, cmap='gray',
72
+ frames[n], cmap='gray',
73
+ animated=True)]
74
+ for n in range(num_frames)]
75
+ ani = animation.ArtistAnimation(
76
+ fig, ims, interval=interval, blit=blit, repeat=repeat,
77
+ repeat_delay=repeat_delay)
78
+
79
+ plt.show()
80
+
81
+ return ani
82
+
83
+
19
84
  class AsyncProcessor(Processor):
20
85
  """A Processor to process multiple sets of input data via asyncio
21
- module
86
+ module.
22
87
 
23
- :ivar mgr: The `Processor` used to process every set of input data
88
+ :ivar mgr: The `Processor` used to process every set of input data.
24
89
  :type mgr: Processor
25
90
  """
26
91
  def __init__(self, mgr):
@@ -31,31 +96,31 @@ class AsyncProcessor(Processor):
31
96
  """Asynchronously process the input documents with the
32
97
  `self.mgr` `Processor`.
33
98
 
34
- :param data: input data documents to process
99
+ :param data: Input data documents to process.
35
100
  :type docs: iterable
36
101
  """
37
-
102
+ # System modules
38
103
  import asyncio
39
104
 
40
105
  async def task(mgr, doc):
41
- """Process given data using provided `Processor`
106
+ """Process given data using provided `Processor`.
42
107
 
43
- :param mgr: the object that will process given data
108
+ :param mgr: The object that will process given data.
44
109
  :type mgr: Processor
45
- :param doc: the data to process
110
+ :param doc: The data to process.
46
111
  :type doc: object
47
- :return: processed data
112
+ :return: The processed data.
48
113
  :rtype: object
49
114
  """
50
115
  return mgr.process(doc)
51
116
 
52
117
  async def execute_tasks(mgr, docs):
53
118
  """Process given set of documents using provided task
54
- manager
119
+ manager.
55
120
 
56
- :param mgr: the object that will process all documents
121
+ :param mgr: The object that will process all documents.
57
122
  :type mgr: Processor
58
- :param docs: the set of data documents to process
123
+ :param docs: The set of data documents to process.
59
124
  :type doc: iterable
60
125
  """
61
126
  coroutines = [task(mgr, d) for d in docs]
@@ -64,22 +129,56 @@ class AsyncProcessor(Processor):
64
129
  asyncio.run(execute_tasks(self.mgr, data))
65
130
 
66
131
 
67
- class IntegrationProcessor(Processor):
68
- """A processor for integrating 2D data with pyFAI"""
132
+ class ImageProcessor(Processor):
133
+ """A Processor to plot an image slice from a dataset.
134
+ """
135
+ def process(self, data, index=0, axis=0):
136
+ """Plot an image from a dataset contained in `data` and return
137
+ the full dataset.
138
+
139
+ :param data: Input data.
140
+ :type data: CHAP.pipeline.PipelineData
141
+ :param index: Array index of the slice of data to plot,
142
+ defaults to `0`
143
+ :type index: int, optional
144
+ :param axis: Axis direction of the image slice,
145
+ defaults to `0`
146
+ :type axis: int, optional
147
+ :return: The full input dataset.
148
+ :rtype: object
149
+ """
150
+ # Local modules
151
+ from CHAP.utils.general import quick_imshow
152
+
153
+ data = self.unwrap_pipelinedata(data)[0]
154
+ if data.ndim == 2:
155
+ quick_imshow(data, block=True)
156
+ elif data.ndim == 3:
157
+ if not axis:
158
+ quick_imshow(data[index], block=True)
159
+ elif axis == 1:
160
+ quick_imshow(data[:,index,:], block=True)
161
+ elif axis == 2:
162
+ quick_imshow(data[:,:,index], block=True)
163
+ else:
164
+ raise ValueError(f'Invalid parameter axis ({axis})')
165
+ else:
166
+ raise ValueError('Invalid data dimension (must be 2D or 3D)')
167
+
168
+ return data
69
169
 
170
+
171
+ class IntegrationProcessor(Processor):
172
+ """A processor for integrating 2D data with pyFAI.
173
+ """
70
174
  def process(self, data):
71
175
  """Integrate the input data with the integration method and
72
- keyword arguments supplied and return the results.
176
+ keyword arguments supplied in `data` and return the results.
73
177
 
74
- :param data: input data, including raw data, integration
178
+ :param data: Input data, containing the raw data, integration
75
179
  method, and keyword args for the integration method.
76
- :type data: tuple[typing.Union[numpy.ndarray,
77
- list[numpy.ndarray]], callable, dict]
78
- :param integration_method: the method of a
79
- `pyFAI.azimuthalIntegrator.AzimuthalIntegrator` or
80
- `pyFAI.multi_geometry.MultiGeometry` that returns the
81
- desired integration results.
82
- :return: integrated raw data
180
+ :type data: CHAP.pipeline.PipelineData
181
+ :return: Integrated raw data.
83
182
  :rtype: pyFAI.containers.IntegrateResult
84
183
  """
85
184
  detector_data, integration_method, integration_kwargs = data
@@ -88,26 +187,23 @@ class IntegrationProcessor(Processor):
88
187
 
89
188
 
90
189
  class IntegrateMapProcessor(Processor):
91
- """Class representing a process that takes a map and integration
92
- configuration and returns a `nexusformat.nexus.NXprocess`
93
- containing a map of the integrated detector data requested.
190
+ """A processor that takes a map and integration configuration and
191
+ returns a NeXus NXprocesss object containing a map of the
192
+ integrated detector data requested.
94
193
  """
95
-
96
194
  def process(self, data):
97
195
  """Process the output of a `Reader` that contains a map and
98
- integration configuration and return a
99
- `nexusformat.nexus.NXprocess` containing a map of the
100
- integrated detector data requested
196
+ integration configuration and return a NeXus NXprocess object
197
+ containing a map of the integrated detector data requested.
101
198
 
102
- :param data: Result of `Reader.read` where at least one item
103
- has the value `'MapConfig'` for the `'schema'` key, and at
104
- least one item has the value `'IntegrationConfig'` for the
199
+ :param data: Input data, containing at least one item
200
+ with the value `'MapConfig'` for the `'schema'` key, and at
201
+ least one item with the value `'IntegrationConfig'` for the
105
202
  `'schema'` key.
106
- :type data: list[dict[str,object]]
107
- :return: integrated data and process metadata
203
+ :type data: CHAP.pipeline.PipelineData
204
+ :return: Integrated data and process metadata.
108
205
  :rtype: nexusformat.nexus.NXprocess
109
206
  """
110
-
111
207
  map_config = self.get_config(
112
208
  data, 'common.models.map.MapConfig')
113
209
  integration_config = self.get_config(
@@ -118,27 +214,31 @@ class IntegrateMapProcessor(Processor):
118
214
 
119
215
  def get_nxprocess(self, map_config, integration_config):
120
216
  """Use a `MapConfig` and `IntegrationConfig` to construct a
121
- `nexusformat.nexus.NXprocess`
217
+ NeXus NXprocess object.
122
218
 
123
- :param map_config: a valid map configuration
219
+ :param map_config: A valid map configuration.
124
220
  :type map_config: MapConfig
125
- :param integration_config: a valid integration configuration
126
- :type integration_config: IntegrationConfig
127
- :return: the integrated detector data and metadata contained
128
- in a NeXus structure
221
+ :param integration_config: A valid integration configuration
222
+ :type integration_config: IntegrationConfig.
223
+ :return: The integrated detector data and metadata.
129
224
  :rtype: nexusformat.nexus.NXprocess
130
225
  """
226
+ # System modules
227
+ from json import dumps
228
+ from time import time
229
+
230
+ # Third party modules
231
+ from nexusformat.nexus import (
232
+ NXdata,
233
+ NXdetector,
234
+ NXfield,
235
+ NXprocess,
236
+ )
237
+ import pyFAI
131
238
 
132
239
  self.logger.debug('Constructing NXprocess')
133
240
  t0 = time()
134
241
 
135
- from nexusformat.nexus import (NXdata,
136
- NXdetector,
137
- NXfield,
138
- NXprocess)
139
- import numpy as np
140
- import pyFAI
141
-
142
242
  nxprocess = NXprocess(name=integration_config.title)
143
243
 
144
244
  nxprocess.map_config = dumps(map_config.dict())
@@ -255,24 +355,21 @@ class IntegrateMapProcessor(Processor):
255
355
 
256
356
 
257
357
  class MapProcessor(Processor):
258
- """A Processor to take a map configuration and return a
259
- `nexusformat.nexus.NXentry` representing that map's metadata and
260
- any scalar-valued raw data requseted by the supplied map
261
- configuration.
358
+ """A Processor that takes a map configuration and returns a NeXus
359
+ NXentry object representing that map's metadata and any
360
+ scalar-valued raw data requested by the supplied map configuration.
262
361
  """
263
-
264
362
  def process(self, data):
265
363
  """Process the output of a `Reader` that contains a map
266
- configuration and return a `nexusformat.nexus.NXentry`
267
- representing the map.
364
+ configuration and returns a NeXus NXentry object representing
365
+ the map.
268
366
 
269
367
  :param data: Result of `Reader.read` where at least one item
270
368
  has the value `'MapConfig'` for the `'schema'` key.
271
- :type data: list[dict[str,object]]
272
- :return: Map data & metadata
369
+ :type data: CHAP.pipeline.PipelineData
370
+ :return: Map data and metadata.
273
371
  :rtype: nexusformat.nexus.NXentry
274
372
  """
275
-
276
373
  map_config = self.get_config(data, 'common.models.map.MapConfig')
277
374
  nxentry = self.__class__.get_nxentry(map_config)
278
375
 
@@ -280,29 +377,29 @@ class MapProcessor(Processor):
280
377
 
281
378
  @staticmethod
282
379
  def get_nxentry(map_config):
283
- """Use a `MapConfig` to construct a
284
- `nexusformat.nexus.NXentry`
380
+ """Use a `MapConfig` to construct a NeXus NXentry object.
285
381
 
286
- :param map_config: a valid map configuration
382
+ :param map_config: A valid map configuration.
287
383
  :type map_config: MapConfig
288
- :return: the map's data and metadata contained in a NeXus
289
- structure
384
+ :return: The map's data and metadata contained in a NeXus
385
+ structure.
290
386
  :rtype: nexusformat.nexus.NXentry
291
387
  """
292
-
293
- from nexusformat.nexus import (NXcollection,
294
- NXdata,
295
- NXentry,
296
- NXfield,
297
- NXsample)
298
- import numpy as np
388
+ # System modules
389
+ from json import dumps
390
+
391
+ # Third party modules
392
+ from nexusformat.nexus import (
393
+ NXcollection,
394
+ NXdata,
395
+ NXentry,
396
+ NXfield,
397
+ NXsample,
398
+ )
299
399
 
300
400
  nxentry = NXentry(name=map_config.title)
301
-
302
401
  nxentry.map_config = dumps(map_config.dict())
303
-
304
402
  nxentry[map_config.sample.name] = NXsample(**map_config.sample.dict())
305
-
306
403
  nxentry.attrs['station'] = map_config.station
307
404
 
308
405
  nxentry.spec_scans = NXcollection()
@@ -352,22 +449,21 @@ class MapProcessor(Processor):
352
449
 
353
450
 
354
451
  class NexusToNumpyProcessor(Processor):
355
- """A Processor to convert the default plottable data in an
356
- `NXobject` into an `numpy.ndarray`.
452
+ """A Processor to convert the default plottable data in a NeXus
453
+ object into a `numpy.ndarray`.
357
454
  """
358
-
359
455
  def process(self, data):
360
- """Return the default plottable data signal in `data` as an
361
- `numpy.ndarray`.
362
-
363
- :param data: input NeXus structure
364
- :type data: nexusformat.nexus.tree.NXobject
365
- :raises ValueError: if `data` has no default plottable data
366
- signal
367
- :return: default plottable data signal in `data`
456
+ """Return the default plottable data signal in a NeXus object
457
+ contained in `data` as an `numpy.ndarray`.
458
+
459
+ :param data: Input data.
460
+ :type data: nexusformat.nexus.NXobject
461
+ :raises ValueError: If `data` has no default plottable data
462
+ signal.
463
+ :return: The default plottable data signal.
368
464
  :rtype: numpy.ndarray
369
465
  """
370
-
466
+ # Third party modules
371
467
  from nexusformat.nexus import NXdata
372
468
 
373
469
  data = self.unwrap_pipelinedata(data)[-1]
@@ -394,22 +490,21 @@ class NexusToNumpyProcessor(Processor):
394
490
 
395
491
 
396
492
  class NexusToXarrayProcessor(Processor):
397
- """A Processor to convert the default plottable data in an
398
- `NXobject` into an `xarray.DataArray`.
493
+ """A Processor to convert the default plottable data in a
494
+ NeXus object into an `xarray.DataArray`.
399
495
  """
400
-
401
496
  def process(self, data):
402
- """Return the default plottable data signal in `data` as an
403
- `xarray.DataArray`.
497
+ """Return the default plottable data signal in a NeXus object
498
+ contained in `data` as an `xarray.DataArray`.
404
499
 
405
- :param data: input NeXus structure
406
- :type data: nexusformat.nexus.tree.NXobject
407
- :raises ValueError: if metadata for `xarray` is absent from
500
+ :param data: Input data.
501
+ :type data: nexusformat.nexus.NXobject
502
+ :raises ValueError: If metadata for `xarray` is absent from
408
503
  `data`
409
- :return: default plottable data signal in `data`
504
+ :return: The default plottable data signal.
410
505
  :rtype: xarray.DataArray
411
506
  """
412
-
507
+ # Third party modules
413
508
  from nexusformat.nexus import NXdata
414
509
  from xarray import DataArray
415
510
 
@@ -458,18 +553,15 @@ class PrintProcessor(Processor):
458
553
  """A Processor to simply print the input data to stdout and return
459
554
  the original input data, unchanged in any way.
460
555
  """
461
-
462
556
  def process(self, data):
463
557
  """Print and return the input data.
464
558
 
465
- :param data: Input data
559
+ :param data: Input data.
466
560
  :type data: object
467
561
  :return: `data`
468
562
  :rtype: object
469
563
  """
470
-
471
564
  print(f'{self.__name__} data :')
472
-
473
565
  if callable(getattr(data, '_str_tree', None)):
474
566
  # If data is likely an NXobject, print its tree
475
567
  # representation (since NXobjects' str representations are
@@ -482,23 +574,23 @@ class PrintProcessor(Processor):
482
574
 
483
575
 
484
576
  class RawDetectorDataMapProcessor(Processor):
485
- """A Processor to return a map of raw derector data in an NXroot"""
486
-
577
+ """A Processor to return a map of raw derector data in a
578
+ NeXus NXroot object.
579
+ """
487
580
  def process(self, data, detector_name, detector_shape):
488
581
  """Process configurations for a map and return the raw
489
582
  detector data data collected over the map.
490
583
 
491
- :param data: input map configuration
492
- :type data: list[dict[str,object]]
493
- :param detector_name: detector prefix
584
+ :param data: Input map configuration.
585
+ :type data: CHAP.pipeline.PipelineData
586
+ :param detector_name: The detector prefix.
494
587
  :type detector_name: str
495
- :param detector_shape: shape of detector data for a single
496
- scan step
588
+ :param detector_shape: The shape of detector data for a single
589
+ scan step.
497
590
  :type detector_shape: list
498
- :return: map of raw detector data
591
+ :return: Map of raw detector data.
499
592
  :rtype: nexusformat.nexus.NXroot
500
593
  """
501
-
502
594
  map_config = self.get_config(data)
503
595
  nxroot = self.get_nxroot(map_config, detector_name, detector_shape)
504
596
 
@@ -506,17 +598,18 @@ class RawDetectorDataMapProcessor(Processor):
506
598
 
507
599
  def get_config(self, data):
508
600
  """Get instances of the map configuration object needed by this
509
- `Processor`
601
+ `Processor`.
510
602
 
511
603
  :param data: Result of `Reader.read` where at least one item
512
- has the value `'MapConfig'` for the `'schema'` key
513
- :type data: list[dict[str,object]]
604
+ has the value `'MapConfig'` for the `'schema'` key.
605
+ :type data: CHAP.pipeline.PipelineData
514
606
  :raises Exception: If a valid map config object cannot be
515
607
  constructed from `data`.
516
- :return: valid instances of the map configuration object with
608
+ :return: A valid instance of the map configuration object with
517
609
  field values taken from `data`.
518
610
  :rtype: MapConfig
519
611
  """
612
+ # Local modules
520
613
  from CHAP.common.models.map import MapConfig
521
614
 
522
615
  map_config = False
@@ -534,27 +627,28 @@ class RawDetectorDataMapProcessor(Processor):
534
627
 
535
628
  def get_nxroot(self, map_config, detector_name, detector_shape):
536
629
  """Get a map of the detector data collected by the scans in
537
- `map_config`.The data will be returned along with some
630
+ `map_config`. The data will be returned along with some
538
631
  relevant metadata in the form of a NeXus structure.
539
632
 
540
- :param map_config: the map configuration
633
+ :param map_config: The map configuration.
541
634
  :type map_config: MapConfig
542
- :param detector_name: detector prefix
635
+ :param detector_name: The detector prefix.
543
636
  :type detector_name: str
544
- :param detector_shape: shape of detector data for a single
545
- scan step
637
+ :param detector_shape: The shape of detector data for a single
638
+ scan step.
546
639
  :type detector_shape: list
547
- :return: a map of the raw detector data
640
+ :return: A map of the raw detector data.
548
641
  :rtype: nexusformat.nexus.NXroot
549
642
  """
550
- # third party modules
551
- from nexusformat.nexus import (NXdata,
552
- NXdetector,
553
- NXinstrument,
554
- NXroot)
555
- import numpy as np
556
-
557
- # local modules
643
+ # Third party modules
644
+ from nexusformat.nexus import (
645
+ NXdata,
646
+ NXdetector,
647
+ NXinstrument,
648
+ NXroot,
649
+ )
650
+
651
+ # Local modules
558
652
  from CHAP.common import MapProcessor
559
653
 
560
654
  nxroot = NXroot()
@@ -604,42 +698,39 @@ class RawDetectorDataMapProcessor(Processor):
604
698
 
605
699
 
606
700
  class StrainAnalysisProcessor(Processor):
607
- """A Processor to compute a map of sample strains by fitting bragg
701
+ """A Processor to compute a map of sample strains by fitting Bragg
608
702
  peaks in 1D detector data and analyzing the difference between
609
703
  measured peak locations and expected peak locations for the sample
610
704
  measured.
611
705
  """
612
-
613
706
  def process(self, data):
614
707
  """Process the input map detector data & configuration for the
615
708
  strain analysis procedure, and return a map of sample strains.
616
709
 
617
- :param data: results of `MutlipleReader.read` containing input
710
+ :param data: Results of `MutlipleReader.read` containing input
618
711
  map detector data and strain analysis configuration
619
- :type data: dict[list[str,object]]
620
- :return: map of sample strains
712
+ :type data: CHAP.pipeline.PipelineData
713
+ :return: A map of sample strains.
621
714
  :rtype: xarray.Dataset
622
715
  """
623
-
624
716
  strain_analysis_config = self.get_config(data)
625
717
 
626
718
  return data
627
719
 
628
720
  def get_config(self, data):
629
721
  """Get instances of the configuration objects needed by this
630
- `Processor` from a returned value of `Reader.read`
722
+ `Processor`.
631
723
 
632
724
  :param data: Result of `Reader.read` where at least one item
633
725
  has the value `'StrainAnalysisConfig'` for the `'schema'`
634
726
  key.
635
- :type data: list[dict[str,object]]
727
+ :type data: CHAP.pipeline.PipelineData
636
728
  :raises Exception: If valid config objects cannot be
637
729
  constructed from `data`.
638
- :return: valid instances of the configuration objects with
730
+ :return: A valid instance of the configuration object with
639
731
  field values taken from `data`.
640
732
  :rtype: StrainAnalysisConfig
641
733
  """
642
-
643
734
  strain_analysis_config = False
644
735
  if isinstance(data, list):
645
736
  for item in data:
@@ -655,25 +746,25 @@ class StrainAnalysisProcessor(Processor):
655
746
 
656
747
 
657
748
  class XarrayToNexusProcessor(Processor):
658
- """A Processor to convert the data in an `xarray` structure to an
659
- `nexusformat.nexus.NXdata`.
749
+ """A Processor to convert the data in an `xarray` structure to a
750
+ NeXus NXdata object.
660
751
  """
661
-
662
752
  def process(self, data):
663
- """Return `data` represented as an `nexusformat.nexus.NXdata`.
753
+ """Return `data` represented as a NeXus NXdata object.
664
754
 
665
- :param data: The input `xarray` structure
755
+ :param data: The input `xarray` structure.
666
756
  :type data: typing.Union[xarray.DataArray, xarray.Dataset]
667
- :return: The data and metadata in `data`
757
+ :return: The data and metadata in `data`.
668
758
  :rtype: nexusformat.nexus.NXdata
669
759
  """
670
-
671
- from nexusformat.nexus import NXdata, NXfield
760
+ # Third party modules
761
+ from nexusformat.nexus import (
762
+ NXdata,
763
+ NXfield,
764
+ )
672
765
 
673
766
  data = self.unwrap_pipelinedata(data)[-1]
674
-
675
767
  signal = NXfield(value=data.data, name=data.name, attrs=data.attrs)
676
-
677
768
  axes = []
678
769
  for name, coord in data.coords.items():
679
770
  axes.append(
@@ -687,13 +778,12 @@ class XarrayToNumpyProcessor(Processor):
687
778
  """A Processor to convert the data in an `xarray.DataArray`
688
779
  structure to an `numpy.ndarray`.
689
780
  """
690
-
691
781
  def process(self, data):
692
782
  """Return just the signal values contained in `data`.
693
783
 
694
- :param data: The input `xarray.DataArray`
784
+ :param data: The input `xarray.DataArray`.
695
785
  :type data: xarray.DataArray
696
- :return: The data in `data`
786
+ :return: The data in `data`.
697
787
  :rtype: numpy.ndarray
698
788
  """
699
789
 
@@ -701,5 +791,7 @@ class XarrayToNumpyProcessor(Processor):
701
791
 
702
792
 
703
793
  if __name__ == '__main__':
794
+ # Local modules
704
795
  from CHAP.processor import main
796
+
705
797
  main()