From 68cffbef4faab5bb89d7f9aeb89f72774739852f Mon Sep 17 00:00:00 2001
From: arigdon-usgs <arigdon@usgs.gov>
Date: Fri, 29 Jun 2018 11:52:29 -0600
Subject: [PATCH] Changes to fix missing channel specification issues in
 XYZAlgorithm and StreamConverter, added a get_required_channels option

---
 geomagio/StreamConverter.py        | 35 +++++++++++++++---------------
 geomagio/algorithm/Algorithm.py    | 10 +++++++++
 geomagio/algorithm/XYZAlgorithm.py | 24 +++++++++++++++++---
 3 files changed, 49 insertions(+), 20 deletions(-)

diff --git a/geomagio/StreamConverter.py b/geomagio/StreamConverter.py
index d2e65e47b..bedc9dacb 100644
--- a/geomagio/StreamConverter.py
+++ b/geomagio/StreamConverter.py
@@ -28,15 +28,15 @@ def get_geo_from_mag(mag):
     """
     h = mag.select(channel='H')[0]
     d = mag.select(channel='D')[0]
-    z = mag.select(channel='Z')[0]
-    f = mag.select(channel='F')[0]
+    z = mag.select(channel='Z')
+    f = mag.select(channel='F')
     mag_h = h.data
     mag_d = d.data
     (geo_x, geo_y) = ChannelConverter.get_geo_from_mag(mag_h, mag_d)
     return obspy.core.Stream((
         __get_trace('X', h.stats, geo_x),
         __get_trace('Y', d.stats, geo_y),
-        z, f))
+        )) + z + f
 
 
 def get_geo_from_obs(obs):
@@ -116,15 +116,15 @@ def get_mag_from_geo(geo):
     """
     x = geo.select(channel='X')[0]
     y = geo.select(channel='Y')[0]
-    z = geo.select(channel='Z')[0]
-    f = geo.select(channel='F')[0]
+    z = geo.select(channel='Z')
+    f = geo.select(channel='F')
     geo_x = x.data
     geo_y = y.data
     (mag_h, mag_d) = ChannelConverter.get_mag_from_geo(geo_x, geo_y)
     return obspy.core.Stream((
             __get_trace('H', x.stats, mag_h),
             __get_trace('D', y.stats, mag_d),
-            z, f))
+            )) + z + f
 
 
 def get_mag_from_obs(obs):
@@ -142,8 +142,8 @@ def get_mag_from_obs(obs):
     """
     h = obs.select(channel='H')[0]
     e = __get_obs_e_from_obs(obs)
-    z = obs.select(channel='Z')[0]
-    f = obs.select(channel='F')[0]
+    z = obs.select(channel='Z')
+    f = obs.select(channel='F')
     obs_h = h.data
     obs_e = e.data
     d0 = ChannelConverter.get_radians_from_minutes(
@@ -152,7 +152,7 @@ def get_mag_from_obs(obs):
     return obspy.core.Stream((
             __get_trace('H', h.stats, mag_h),
             __get_trace('D', e.stats, mag_d),
-            z, f))
+            )) + z + f
 
 
 def get_obs_from_geo(geo, include_d=False):
@@ -189,8 +189,9 @@ def get_obs_from_mag(mag, include_d=False):
     """
     h = mag.select(channel='H')[0]
     d = mag.select(channel='D')[0]
-    z = mag.select(channel='Z')[0]
-    f = mag.select(channel='F')[0]
+    z = mag.select(channel='Z')
+    f = mag.select(channel='F')
+
     mag_h = h.data
     mag_d = d.data
     d0 = ChannelConverter.get_radians_from_minutes(
@@ -200,11 +201,11 @@ def get_obs_from_mag(mag, include_d=False):
     traces = (
         __get_trace('H', h.stats, obs_h),
         __get_trace('E', d.stats, obs_e),
-        z, f)
+        )
     if include_d:
         obs_d = ChannelConverter.get_obs_d_from_obs(obs_h, obs_e)
         traces = traces + (__get_trace('D', d.stats, obs_d),)
-    return obspy.core.Stream(traces)
+    return obspy.core.Stream(traces) + z + f
 
 
 def get_obs_from_obs(obs, include_e=False, include_d=False):
@@ -225,16 +226,16 @@ def get_obs_from_obs(obs, include_e=False, include_d=False):
         new stream object containing observatory components H, D, E, Z, and F
     """
     h = obs.select(channel='H')[0]
-    z = obs.select(channel='Z')[0]
-    f = obs.select(channel='F')[0]
-    traces = (h, z, f)
+    z = obs.select(channel='Z')
+    f = obs.select(channel='F')
+    traces = (h, )
     if include_d:
         d = __get_obs_d_from_obs(obs)
         traces = traces + (d, )
     if include_e:
         e = __get_obs_e_from_obs(obs)
         traces = traces + (e, )
-    return obspy.core.Stream(traces)
+    return obspy.core.Stream(traces) + z + f
 
 
 def __get_trace(channel, stats, data):
diff --git a/geomagio/algorithm/Algorithm.py b/geomagio/algorithm/Algorithm.py
index 1aa1b7b6c..a3f264cb5 100644
--- a/geomagio/algorithm/Algorithm.py
+++ b/geomagio/algorithm/Algorithm.py
@@ -46,6 +46,16 @@ class Algorithm(object):
         """
         return self._inchannels
 
+    def get_required_channels(self):
+        """Get only required channels
+
+        Returns
+        -------
+        array_like
+            list of channels essential to the algorithm
+        """
+        return self._inchannels
+
     def get_output_channels(self):
         """Get output channels
 
diff --git a/geomagio/algorithm/XYZAlgorithm.py b/geomagio/algorithm/XYZAlgorithm.py
index 92aa4585c..b93f26a88 100644
--- a/geomagio/algorithm/XYZAlgorithm.py
+++ b/geomagio/algorithm/XYZAlgorithm.py
@@ -7,6 +7,7 @@ from __future__ import absolute_import
 from .Algorithm import Algorithm
 from .AlgorithmException import AlgorithmException
 from .. import StreamConverter
+import numpy as np
 
 # List of channels by geomagnetic observatory orientation.
 # geo represents a geographic north/south orientation
@@ -51,11 +52,17 @@ class XYZAlgorithm(Algorithm):
         channels: array_like
             channels that are expected in stream.
         """
-        for channel in self._inchannels:
+        for channel in self.get_required_channels():
             if len(timeseries.select(channel=channel)) == 0:
                 raise AlgorithmException(
                     'Channel %s not found in input' % channel)
 
+    def get_required_channels(self):
+        """Only the first two channels are required
+            for the XYZAlgorithm
+        """
+        return self._inchannels[:2]
+
     def process(self, timeseries):
         """converts a timeseries stream into a different coordinate system
 
@@ -104,6 +111,7 @@ class XYZAlgorithm(Algorithm):
             elif informat == 'obs' or informat == 'obsd':
                 out_stream = StreamConverter.get_obs_from_obs(timeseries,
                         include_d=True)
+
         return out_stream
 
     @classmethod
@@ -134,5 +142,15 @@ class XYZAlgorithm(Algorithm):
         """
         self._informat = arguments.xyz_from
         self._outformat = arguments.xyz_to
-        self._inchannels = CHANNELS[self._informat]
-        self._outchannels = CHANNELS[self._outformat]
+        self._inchannels = arguments.inchannels or \
+                            CHANNELS[self._informat]
+        # outchannels set according to specified outchannels or inchannel designation
+        # if the inchannels do not have 'Z' or 'F' neither will the outchannel
+        if arguments.outchannels:
+            self._outchannels = arguments.outchannels
+        else:
+            self._outchannels = CHANNELS[self._outformat]
+            if not 'Z' in self._inchannels:
+                del self._outchannels[self._outchannels.index('Z')]
+            if not 'F' in self._inchannels:
+                del self._outchannels[self._outchannels.index('F')]
-- 
GitLab